#!perl
use Cassandane::Tiny;

sub test_cardgroup_changes
    :min_version_3_9
    ($self)
{
    my $jmap = $self->{jmap};

    xlog $self, "create contacts";
    my $res = $jmap->CallMethods([
        ['ContactCard/set', {
            create => {
                "a" => {
                    '@type' => 'Card',
                    version => '1.0',
                    name => { full => 'John' }
                },
                "b" => {
                    '@type' => 'Card',
                    version => '1.0',
                    name => { full => 'Jane' }
                },
                "c" => {
                    '@type' => 'Card',
                    version => '1.0',
                    name => { full => 'Jack' }
                },
                "d" => {
                    '@type' => 'Card',
                    version => '1.0',
                    name => { full => 'Jill' }
                }
            }
         }, "R1"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('ContactCard/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);
    my $contactA = $res->[0][1]{created}{"a"}{id};
    my $contactB = $res->[0][1]{created}{"b"}{id};
    my $contactC = $res->[0][1]{created}{"c"}{id};
    my $contactD = $res->[0][1]{created}{"d"}{id};
    $self->assert_not_null($contactA);
    $self->assert_not_null($contactB);
    $self->assert_not_null($contactC);
    $self->assert_not_null($contactD);

    xlog $self, "get contact groups state";
    $res = $jmap->CallMethods([['ContactCard/get', {}, "R2"]]);
    my $state = $res->[0][1]{state};

    xlog $self, "create contact group 1";
    $res = $jmap->CallMethods([['ContactCard/set', {create => {
        "1" => {
            '@type' => 'Card',
            version => '1.0',
            kind => 'group',
            name => { full => "group1" },
            members => { $contactA => JSON::true, $contactB => 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 $id1 = $res->[0][1]{created}{"1"}{id};
    $self->assert_not_null($id1);

    xlog $self, "get contact group updates";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $state
                }, "R2"]]);
    $self->assert_str_equals($state, $res->[0][1]{oldState});
    $self->assert_str_not_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::false, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{destroyed}});
    $self->assert_str_equals($id1, $res->[0][1]{created}[0]);

    my $oldState = $state;
    $state = $res->[0][1]{newState};

    xlog $self, "create contact group 2";
    $res = $jmap->CallMethods([['ContactCard/set', {create => {
        "2" => {
            '@type' => 'Card',
            version => '1.0',
            kind => 'group',
            name => { full => "group2" },
            members =>  { $contactC => JSON::true, $contactD => 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 $id2 = $res->[0][1]{created}{"2"}{id};
    $self->assert_not_null($id2);

    xlog $self, "get contact group updates (since last change)";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $state
                }, "R2"]]);
    $self->assert_str_equals($state, $res->[0][1]{oldState});
    $self->assert_str_not_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::false, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{destroyed}});
    $self->assert_str_equals($id2, $res->[0][1]{created}[0]);
    $state = $res->[0][1]{newState};

    xlog $self, "get contact group updates (in bulk)";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $oldState
                }, "R2"]]);
    $self->assert_str_equals($oldState, $res->[0][1]{oldState});
    $self->assert_str_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::false, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(2, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{destroyed}});

    xlog $self, "get contact group updates from initial state (maxChanges=1)";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $oldState,
                    maxChanges => 1
                }, "R2"]]);
    $self->assert_str_equals($oldState, $res->[0][1]{oldState});
    $self->assert_str_not_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::true, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{destroyed}});
    $self->assert_str_equals($id1, $res->[0][1]{created}[0]);
    my $interimState = $res->[0][1]{newState};

    xlog $self, "get contact group updates from interim state (maxChanges=10)";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $interimState,
                    maxChanges => 10
                }, "R2"]]);
    $self->assert_str_equals($interimState, $res->[0][1]{oldState});
    $self->assert_str_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::false, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{destroyed}});
    $self->assert_str_equals($id2, $res->[0][1]{created}[0]);
    $state = $res->[0][1]{newState};

    xlog $self, "destroy contact group 1, update contact group 2";
    $res = $jmap->CallMethods([['ContactCard/set', {
                    destroy => [$id1],
                    update => {$id2 => {name => { full => "group2 (updated)" }}}
                }, "R1"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('ContactCard/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);

    xlog $self, "get contact group updates";
    $res = $jmap->CallMethods([['ContactCard/changes', {
                    sinceState => $state
                }, "R2"]]);
    $self->assert_str_equals($state, $res->[0][1]{oldState});
    $self->assert_str_not_equals($state, $res->[0][1]{newState});
    $self->assert_equals(JSON::false, $res->[0][1]{hasMoreChanges});
    $self->assert_num_equals(0, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{updated}});
    $self->assert_str_equals($id2, $res->[0][1]{updated}[0]);
    $self->assert_num_equals(1, scalar @{$res->[0][1]{destroyed}});
    $self->assert_str_equals($id1, $res->[0][1]{destroyed}[0]);

    xlog $self, "destroy contact group 2";
    $res = $jmap->CallMethods([['ContactCard/set', {destroy => [$id2]}, "R1"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('ContactCard/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);
}
