#!perl
use Cassandane::Tiny;

sub test_email_query_guidsearch_sort
    :min_version_3_1 :needs_component_sieve :needs_component_jmap
    :JMAPExtensions
{
    my ($self) = @_;
    my $jmap = $self->{jmap};
    my $imap = $self->{store}->get_client();

    my $using = [
        'urn:ietf:params:jmap:core',
        'urn:ietf:params:jmap:mail',
        'urn:ietf:params:jmap:submission',
        'https://cyrusimap.org/ns/jmap/mail',
        'https://cyrusimap.org/ns/jmap/quota',
        'https://cyrusimap.org/ns/jmap/debug',
        'https://cyrusimap.org/ns/jmap/performance',
    ];

    my $emailCount = 10;

    xlog "Creating $emailCount emails (every 5th has same internaldate)";
    my %createEmails;
    for (my $i = 0; $i < $emailCount; $i++) {
        my $receivedAt = '2019-01-0' . (($i % 5) + 1) . 'T00:00:00Z';
        $createEmails{$i} = {
            mailboxIds => {
                '$inbox' => JSON::true
            },
            from => [{ email => "foo$i\@bar" }],
            to => [{ email => "bar$i\@example.com" }],
            receivedAt => $receivedAt,
            subject => "email$i",
            bodyStructure => {
                partId => '1',
            },
            bodyValues => {
                "1" => {
                    value => "email$i body",
                },
            },
        }
    }
    my $res = $jmap->CallMethods([
        ['Email/set', {
            create => \%createEmails,
        }, 'R1'],
    ]);
    $self->assert_num_equals($emailCount, scalar keys %{$res->[0][1]{created}});

    my @emails;
    for (my $i = 0; $i < $emailCount; $i++) {
        $emails[$i] = {
            id => $res->[0][1]{created}{$i}{id},
            receivedAt => $createEmails{$i}{receivedAt}
        };
    }

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

    xlog "Sort by id (ascending and descending)";
    $res = $jmap->CallMethods([
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'id',
                isAscending => JSON::true,
            }]
        }, 'R1'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'id',
                isAscending => JSON::false,
            }]
        }, 'R2'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'id',
                isAscending => JSON::true,
            }],
            disableGuidSearch => JSON::true,
        }, 'R1'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'id',
                isAscending => JSON::false,
            }],
            disableGuidSearch => JSON::true,
        }, 'R2'],
    ], $using);

    my $guidSearchIds;
    my @wantIds;

    # Check GUID search results
    $self->assert_equals(JSON::true, $res->[0][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort { $a->{id} cmp $b->{id} } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[0][1]{ids});

    $self->assert_equals(JSON::true, $res->[1][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort { $b->{id} cmp $a->{id} } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[1][1]{ids});

    # Check UID search result
    $self->assert_equals(JSON::false, $res->[2][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort { $a->{id} cmp $b->{id} } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[2][1]{ids});

    $self->assert_equals(JSON::false, $res->[3][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort { $b->{id} cmp $a->{id} } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[3][1]{ids});

    xlog "Sort by internaldate (break ties by id) (ascending and descending)";
    $res = $jmap->CallMethods([
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'receivedAt',
                isAscending => JSON::true,
            }]
        }, 'R1'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'receivedAt',
                isAscending => JSON::false,
            }]
        }, 'R2'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'receivedAt',
                isAscending => JSON::true,
            }],
            disableGuidSearch => JSON::true,
        }, 'R3'],
        ['Email/query', {
            filter => {
                to => '@example.com',
            },
            sort => [{
                property => 'receivedAt',
                isAscending => JSON::false,
            }],
            disableGuidSearch => JSON::true,
        }, 'R4'],
    ], $using);

    # Check GUID search results
    $self->assert_equals(JSON::true, $res->[0][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort {
        $a->{receivedAt} cmp $b->{receivedAt} or $b->{id} cmp $a->{id}
    } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[0][1]{ids});

    $self->assert_equals(JSON::true, $res->[1][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort {
        $b->{receivedAt} cmp $a->{receivedAt} or $b->{id} cmp $a->{id}
    } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[1][1]{ids});

    # Check UID search result
    $self->assert_equals(JSON::false, $res->[2][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort {
        $a->{receivedAt} cmp $b->{receivedAt} or $b->{id} cmp $a->{id}
    } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[2][1]{ids});

    $self->assert_equals(JSON::false, $res->[3][1]{performance}{details}{isGuidSearch});
    @wantIds = map { $_->{id} } sort {
        $b->{receivedAt} cmp $a->{receivedAt} or $b->{id} cmp $a->{id}
    } @emails;
    $self->assert_deep_equals(\@wantIds, $res->[3][1]{ids});
}
