#!perl
use Cassandane::Tiny;

sub test_restore_contacts_all
    :min_version_3_3
    ($self)
{
    my $jmap = $self->{jmap};

    my $start = time();
    sleep 2;

    xlog "create contacts";
    my $res = $jmap->CallMethods([['ContactCard/set', {
        create => {
            "a" => {
                '@type' => 'Card',
                version => '1.0',
                name => { full => "a" }
            },
            "b" => {
                '@type' => 'Card',
                version => '1.0',
                name => { full => "b" }
            },
            "c" => {
                '@type' => 'Card',
                version => '1.0',
                name => { full => "c" }
            },
            "d" => {
                '@type' => 'Card',
                version => '1.0',
                name => { full => "d" }
            }
        }
    }, "R1"]]);
    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 $contactCuid = $res->[0][1]{created}{"c"}{uid};
    my $contactD = $res->[0][1]{created}{"d"}{id};

    xlog "destroy contact A, update contact B";
    $res = $jmap->CallMethods([['ContactCard/set', {
        destroy => [$contactA],
        update => {
            $contactB => {
                'name/full' => "B"
            }
        }
    }, "R2"]]);

    my $mark = time();
    sleep 2;

    xlog "destroy contact C, update contacts B and D, create contact E";
    $res = $jmap->CallMethods([['ContactCard/set', {
        destroy => [$contactC],
        update => {
            $contactB => {
                'name/full' => "b"
            },
            $contactD => {
                'name/full' => "D"
            }
        },
        create => {
            "e" => {
                '@type' => 'Card',
                version => '1.0',
                name => { full => "e" }
            }
        }
    }, "R4"]]);
    my $contactE = $res->[0][1]{created}{"e"}{id};
    my $state = $res->[0][1]{newState};

    my $diff = time() - $mark;
    my $period = "PT" . $diff . "S";

    xlog "restore contacts prior to most recent changes";
    $res = $jmap->CallMethods([['Backup/restoreContacts', {
                    undoPeriod => $period,
                    undoAll => JSON::true
                }, "R5"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Backup/restoreContacts', $res->[0][0]);
    $self->assert_str_equals('R5', $res->[0][2]);
    $self->assert_num_equals(1, $res->[0][1]{numCreatesUndone});
    $self->assert_num_equals(2, $res->[0][1]{numUpdatesUndone});
    $self->assert_num_equals(1, $res->[0][1]{numDestroysUndone});

    xlog "get restored contacts";
    $res = $jmap->CallMethods([
        ['ContactCard/query', {
            filter => { kind => 'individual'}
         }, "R1"],
        ['ContactCard/get', {
            '#ids' => { resultOf => 'R1', name => 'ContactCard/query', path => '/ids' },
            properties => [ 'id', 'name' ],
         }, "R6"],
        ['ContactCard/query', {
            filter => { kind => 'group'}
         }, "R2"],
        ['ContactCard/get', {
            '#ids' => { resultOf => 'R2', name => 'ContactCard/query', path => '/ids' },
            properties => [ 'id', 'name', 'members' ],
         }, "R7"]
    ]);

    my @got = sort { $a->{name}{full} cmp $b->{name}{full} } @{$res->[1][1]{list}};

    $self->assert_num_equals(3, scalar @got);
    $self->assert_str_equals('B', $got[0]{name}{full});
    $self->assert_str_equals('c', $got[1]{name}{full});
    $self->assert_str_equals('d', $got[2]{name}{full});

    # Ids of contacts will changes when recreated
    $contactB = $got[0]{id};
    $contactC = $got[1]{id};
    $contactD = $got[2]{id};

    $self->assert_matches(qr/^Restored /, $res->[3][1]{list}[0]{name}{full});
    $self->assert_cmp_deeply(
        superhashof({
            $contactCuid => JSON::true,
        }),
        $res->[3][1]{list}[0]{members}
    );
    my $groupId = $res->[3][1]{list}[0]{id};

    xlog "get contact updates";
    $res = $jmap->CallMethods([
        ['ContactCard/changes', {
            sinceState => $state
         }, "R6.5"],
    ]);
    $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(2, scalar @{$res->[0][1]{created}});
    $self->assert_num_equals(2, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(1, scalar @{$res->[0][1]{destroyed}});
    $state = $res->[0][1]{newState};

    $self->assert_cmp_deeply(
        bag($contactC, $groupId),
        $res->[0][1]{created}
    );
    $self->assert_cmp_deeply(
        bag($contactB, $contactD),
        $res->[0][1]{updated}
    );
    $self->assert_cmp_deeply(
        bag($contactE),
        $res->[0][1]{destroyed}
    );

    $diff = time() - $start;
    $period = "PT" . $diff . "S";

    xlog "restore contacts to before initial creation";
    $res = $jmap->CallMethods([['Backup/restoreContacts', {
                    undoPeriod => $period,
                    undoAll => JSON::true
                }, "R7"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Backup/restoreContacts', $res->[0][0]);
    $self->assert_str_equals('R7', $res->[0][2]);
    $self->assert_num_equals(4, $res->[0][1]{numCreatesUndone});
    $self->assert_num_equals(0, $res->[0][1]{numUpdatesUndone});
    $self->assert_num_equals(0, $res->[0][1]{numDestroysUndone});

    xlog "get restored contacts";
    $res = $jmap->CallMethods([
        ['ContactCard/get', {
            properties => [ 'name' ],
         }, "R8"],
    ]);
    $self->assert_deep_equals([], $res->[0][1]{list});

    xlog "get contact updates";
    $res = $jmap->CallMethods([
        ['ContactCard/changes', {
            sinceState => $state
         }, "R8.5"],
    ]);
    $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(0, scalar @{$res->[0][1]{updated}});
    $self->assert_num_equals(4, scalar @{$res->[0][1]{destroyed}});
    $self->assert_cmp_deeply(
        bag($contactB, $contactC, $contactD, $groupId),
        $res->[0][1]{destroyed}
    );
}
