#!perl
use Cassandane::Tiny;

sub test_card_query
    :min_version_3_9 :needs_dependency_icalvcard
{
    my ($self) = @_;

    my $jmap = $self->{jmap};

    xlog $self, "create cards";
    my $res = $jmap->CallMethods([['ContactCard/set', {
        create => {
            "1" => {
                name => {
                    isOrdered => JSON::false,
                    components => [
                        {
                            kind => "given",
                            value => "foo"
                        },
                        {
                            kind => "surname",
                            value => "last"
                        },
                    ],
                    sortAs => {
                        surname => 'aaa'
                    }
                },
                nicknames => {
                    'n1' => {
                        name => "foo"
                    }
                },
                emails => {
                    'e1' => {
                        contexts => {
                            private => JSON::true
                        },
                        address => "foo\@example.com"
                    }
                },
                personalInfo => {
                    'p1' => {
                        kind => 'hobby',
                        value => 'reading'
                    }
                }
            },
            "2" => {
                name => {
                    isOrdered => JSON::false,
                    components => [
                        {
                            kind => "given",
                            value => "bar"
                        },
                        {
                            kind => "surname",
                            value => "last"
                        },
                    ]
                },
                emails => {
                    'e1' => {
                        contexts => {
                            work => JSON::true
                        },
                        address => "bar\@bar.org"
                    },
                    'e2' => {
                        contexts => {
                            other => JSON::true
                        },
                        address => "me\@example.com"
                    }
                },
                addresses => {
                    'a1' => {
                        contexts => {
                            private => JSON::true
                        },
                        isOrdered => JSON::false,
                        components => [
                            {
                                kind => "name",
                                value => "Some Lane"
                            },
                            {
                                kind => "number",
                                value => "24"
                            },
                            {
                                kind => "locality",
                                value => "SomeWhere City"
                            },
                            {
                                kind => "region",
                                value => ""
                            },
                            {
                                kind => "postcode",
                                value => "1234"
                            }
                        ]
                    }
                }
            },
            "3" => {
                name => {
                    isOrdered => JSON::false,
                    components => [
                        {
                            kind => "given",
                            value => "baz"
                        },
                        {
                            kind => "surname",
                            value => "last"
                        },
                    ]
                },
                addresses => {
                    'a1' => {
                        contexts => {
                            private => JSON::true
                        },
                        isOrdered => JSON::false,
                        components => [
                            {
                                kind => "name",
                                value => "Some Lane"
                            },
                            {
                                kind => "number",
                                value => "24"
                            },
                            {
                                kind => "locality",
                                value => "SomeWhere City"
                            },
                            {
                                kind => "region",
                                value => ""
                            },
                            {
                                kind => "postcode",
                                value => "1234"
                            },
                            {
                                kind => "country",
                                value => "Someinistan"
                            }
                        ]
                    }
                },
                personalInfo => {
                    'p1' => {
                        kind => 'interest',
                        value => 'r&b music'
                    }
                }
            },
            "4" => {
                name => {
                    isOrdered => JSON::false,
                    components => [
                        {
                            kind => "given",
                            value => "bam"
                        },
                        {
                            kind => "surname",
                            value => "last"
                        },
                    ]
                },
                nicknames => {
                    'n1' => {
                        name => "bam"
                    }
                },
                notes => {
                    'n1' => {
                        note => "hello"
                    }
                }
            },
            "5" => {
                kind => 'org',
                name => {
                    full => 'My Org'
                }
            }
        }
    }, "R1"]]);

    $self->assert_not_null($res);
    $self->assert_str_equals('ContactCard/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);
    my $id1 = $res->[0][1]{created}{"1"}{id};
    my $id2 = $res->[0][1]{created}{"2"}{id};
    my $id3 = $res->[0][1]{created}{"3"}{id};
    my $id4 = $res->[0][1]{created}{"4"}{id};

    xlog $self, "create card groups";
    $res = $jmap->CallMethods([['ContactCard/set', {create => {
        "1" => { kind => 'group',
                 name => { full => "group1" },
                 members => { $id1 => JSON::true, $id2 => JSON::true }
               },
        "2" => { kind => 'group',
                 name => { full => "group2" },
                 members => { $id3 => JSON::true }
               },
        "3" => { kind => 'group',
                 name => { full => "group3" },
                 members => { $id4 => JSON::true }
               }
    }}, "R1"]]);

    $self->assert_not_null($res);
    $self->assert_str_equals('ContactCard/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);
    my $group1 = $res->[0][1]{created}{"1"}{id};
    my $group2 = $res->[0][1]{created}{"2"}{id};
    my $group3 = $res->[0][1]{created}{"3"}{id};

    xlog $self, "get unfiltered card list";
    $res = $jmap->CallMethods([ ['ContactCard/query', { }, "R1"] ]);

    $self->assert_num_equals(8, $res->[0][1]{total});
    $self->assert_num_equals(8, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by kind";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { kind => 'individual'}
                }, "R1"] ]);

    $self->assert_num_equals(4, $res->[0][1]{total});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by kind";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { kind => 'org'}
                }, "R1"] ]);

    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by name (fullName)";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { name => "foo" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id1, $res->[0][1]{ids}[0]);

    xlog $self, "filter by name (fullName)";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { name => "last" }
                }, "R1"] ]);
    $self->assert_num_equals(4, $res->[0][1]{total});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by name/given";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { 'name/given' => "foo" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id1, $res->[0][1]{ids}[0]);

    xlog $self, "filter by name/surname";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { 'name/surname' => "last" }
                }, "R1"] ]);
    $self->assert_num_equals(4, $res->[0][1]{total});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by name/given and name/surname (one filter)";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { 'name/given' => "bam", 'name/surname' => "last" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id4, $res->[0][1]{ids}[0]);

    xlog $self, "filter by name/given and name/surname (AND filter)";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { operator => "AND", conditions => [{
                                'name/surname' => "last"
                            }, {
                                'name/given' => "baz"
                    }]}
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id3, $res->[0][1]{ids}[0]);

    xlog $self, "filter by name/given (OR filter)";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { operator => "OR", conditions => [{
                                'name/given' => "bar"
                            }, {
                                'name/given' => "baz"
                    }]}
                }, "R1"] ]);
    $self->assert_num_equals(2, $res->[0][1]{total});
    $self->assert_num_equals(2, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by text";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { text => "some" }
                }, "R1"] ]);
    $self->assert_num_equals(2, $res->[0][1]{total});
    $self->assert_num_equals(2, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by nickName";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { nickName => "foo" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id1, $res->[0][1]{ids}[0]);

    xlog $self, "filter by email";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { email => "example.com" }
                }, "R1"] ]);
    $self->assert_num_equals(2, $res->[0][1]{total});
    $self->assert_num_equals(2, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by hobby";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { hobby => "reading" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by note";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { note => "hello" }
                }, "R1"] ]);
    $self->assert_num_equals(1, $res->[0][1]{total});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by inCardGroup";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { inCardGroup => [$group1, $group3] }
                }, "R1"] ]);
    $self->assert_num_equals(3, scalar @{$res->[0][1]{ids}});

    xlog $self, "filter by inCardGroup and name/given";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { inCardGroup => [$group1, $group3],
                                'name/given' => "foo" }
                }, "R1"] ]);
    $self->assert_num_equals(1, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id1, $res->[0][1]{ids}[0]);

    xlog $self, "sort by name/given";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { kind => 'individual'},
                    sort => [ { property => "name/given" } ]
                }, "R1"] ]);
    $self->assert_num_equals(4, $res->[0][1]{total});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id4, $res->[0][1]{ids}[0]);
    $self->assert_str_equals($id2, $res->[0][1]{ids}[1]);
    $self->assert_str_equals($id3, $res->[0][1]{ids}[2]);
    $self->assert_str_equals($id1, $res->[0][1]{ids}[3]);

    xlog $self, "sort by name/surname";
    $res = $jmap->CallMethods([ ['ContactCard/query', {
                    filter => { kind => 'individual'},
                    sort => [ { property => "name/surname" } ]
                }, "R1"] ]);
    $self->assert_num_equals(4, $res->[0][1]{total});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{ids}});
    $self->assert_str_equals($id1, $res->[0][1]{ids}[0]);
}
