#!perl
use Cassandane::Tiny;

sub test_calendarevent_query
    :min_version_3_1
{
    my ($self) = @_;

    my $jmap = $self->{jmap};
    my $caldav = $self->{caldav};
    my ($maj, $min) = Cassandane::Instance->get_version();

    xlog $self, "create calendars";
    my $res = $jmap->CallMethods([
        ['Calendar/set', {
            create => {
                calendarA => {
                    name => "A",
                },
                calendarB => {
                    name => "B",
                }
            }
        }, "R1"]
    ]);
    my $calendarIdA = $res->[0][1]{created}{calendarA}{id};
    $self->assert_not_null($calendarIdA);
    my $calendarIdB = $res->[0][1]{created}{calendarB}{id};
    $self->assert_not_null($calendarIdB);

    my %eventA1 = (
        uid => 'a1-03df209b28-4005-a458-751e2f6058b5'
    );
    my %eventA2 = (
        uid => 'a2-73e05c2f12fa-43c4-a17f-9c6e35ddd8'
    );
    my %eventB1 = (
        uid => 'b1-8528b44b7cdd-4867-85f0-09746080d9'
    );

    xlog $self, "create events";
    $res = $jmap->CallMethods([
        ['CalendarEvent/set', {
            create => {
                eventA1 => {
                    calendarIds => {
                        $calendarIdA => JSON::true,
                    },
                    uid => $eventA1{uid},
                    title => 'eventA1',
                    description => 'test',
                    start => '2023-01-01T01:00:00',
                    timeZone => 'Etc/UTC',
                    duration => 'PT1H',
                },
                eventB1 => {
                    calendarIds => {
                        $calendarIdB => JSON::true,
                    },
                    uid => $eventB1{uid},
                    title => 'eventB1',
                    description => 'test',
                    start => '2023-02-01T01:00:00',
                    timeZone => 'Etc/UTC',
                    duration => 'PT1H',
                },
                eventA2 => {
                    calendarIds => {
                        $calendarIdA => JSON::true,
                    },
                    uid => $eventA2{uid},
                    title => 'eventA2',
                    description => 'test',
                    start => '2023-03-01T01:00:00',
                    timeZone => 'Etc/UTC',
                    duration => 'PT1H',
                },
            }
        }, 'R1']
    ]);

    $eventA1{id} = $res->[0][1]{created}{eventA1}{id};
    $self->assert_not_null($eventA1{id});
    $eventA2{id} = $res->[0][1]{created}{eventA2}{id};
    $self->assert_not_null($eventA2{id});
    $eventB1{id} = $res->[0][1]{created}{eventB1}{id};
    $self->assert_not_null($eventB1{id});

    xlog $self, "Run squatter";
    $self->{instance}->run_command({cyrus => 1}, 'squatter');

    my @testCases = ({
        filter => undef,
        wantIds => [$eventA1{id}, $eventA2{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            before => '2023-03-01T01:00:00',
        },
        wantIds => [$eventA1{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            after => '2023-01-01T02:00:00',
        },
        wantIds => [$eventA2{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            after =>  '2023-01-01T02:00:00',
            before => '2023-03-01T01:00:00',
        },
        wantIds => [$eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'AND',
            conditions => [{
                after =>  '2023-01-01T02:00:00',
            }, {
                before => '2023-03-01T01:00:00',
            }],
        },
        wantIds => [$eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'NOT',
            conditions => [{
                after => '2023-01-01T02:00:00',
            }],
        },
        wantIds => [$eventA1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            uid => $eventA2{uid},
        },
        wantIds => [$eventA2{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'NOT',
            conditions => [{
                uid => $eventA2{uid},
            }],
        },
        wantIds => [$eventA1{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'OR',
            conditions => [{
                uid => $eventA1{uid},
            }, {
                uid => $eventB1{uid},
            }],
        },
        wantIds => [$eventA1{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            inCalendars => [$calendarIdA, $calendarIdB],
        },
        wantIds => [$eventA1{id}, $eventA2{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'NOT',
            conditions => [{
                inCalendars => [$calendarIdA],
            }],
        },
        wantIds => [$eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'OR',
            conditions => [{
                inCalendars => [$calendarIdA, $calendarIdB],
            }],
        },
        wantIds => [$eventA1{id}, $eventA2{id}, $eventB1{id}],
        wantFastPath => JSON::true,
    }, {
        filter => {
            operator => 'AND',
            conditions => [{
                inCalendars => [$calendarIdA],
            }, {
                text => 'test',
            }],
        },
        wantIds => [$eventA1{id}, $eventA2{id}],
        wantFastPath => JSON::false,
    }, {
        filter => undef,
        position => 1,
        limit => 1,
        wantTotal => 3,
        wantIds => [$eventA2{id}],
        wantFastPath => JSON::true,
    }, {
        filter => undef,
        position => -1,
        wantTotal => 3,
        wantIds => [$eventB1{id}],
        wantFastPath => JSON::false,
    }, {
        filter => {
            operator => 'NOT',
            conditions => [{
                blarg => 'foo', # invalid - blarg is not a calevent property
            }],
        },
        wantErr => {
            type => 'invalidArguments',
            arguments => [ 'filter/conditions[0]/blarg' ],
        },
    }, {
        filter => {
            operator => 'NOT',
            conditions => {  # invalid - object rather than array
                blarg => 'foo',
            },
        },
        wantErr => {
            type => 'invalidArguments',
            arguments => [ 'filter/conditions' ],
        },
    }, {
        filter => {
            operator => 'NOT',
            # invalid - no conditions
        },
        wantErr => {
            type => 'invalidArguments',
            arguments => [ 'filter/conditions' ],
        },
    }, {
        filter => {
            operator => 'BLARG', # invalid operator
            conditions => [{
                after => '2023-01-01T02:00:00',
            }],
        },
        wantErr => {
            type => 'invalidArguments',
            arguments => [ 'filter/operator' ],
        },
    });

    for my $tc (@testCases) {
        my $q = {
            filter => $tc->{filter},
            sort => [{
                property => 'uid',
            }],
        };

        if (defined $tc->{position}) {
            $q->{position} = $tc->{position};
        }

        if (defined $tc->{limit}) {
            $q->{limit} = $tc->{limit};
        }

        $res = $jmap->CallMethods([
            ['CalendarEvent/query', $q, 'R1'],
        ]);
        if ($tc->{wantErr}) {
            $self->assert_str_equals('error', $res->[0][0]);
            $self->assert_deep_equals($tc->{wantErr}, $res->[0][1]);
        } else {
            my $wantTotal = defined $tc->{wantTotal} ?
                $tc->{wantTotal} : scalar @{$tc->{wantIds}};
            $self->assert_num_equals($wantTotal, $res->[0][1]{total});
            $self->assert_deep_equals($tc->{wantIds}, $res->[0][1]{ids});

            if ($maj > 3 || ($maj == 3 && $min > 8)) {
                $self->assert_equals($tc->{wantFastPath},
                    $res->[0][1]{debug}{isFastPath});
            }
        }
    }
}
