#!perl
use Cassandane::Tiny;

sub test_restore_contacts_all
    :min_version_3_3
{
    my ($self) = @_;

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

    my $start = time();
    sleep 2;

    xlog "create contacts";
    my $res = $jmap->CallMethods([['Contact/set', {create => {
                        "a" => {firstName => "a", lastName => "a"},
                        "b" => {firstName => "b", lastName => "b"},
                        "c" => {firstName => "c", lastName => "c"},
                        "d" => {firstName => "d", lastName => "d"}
                    }}, "R1"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/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};

    xlog "destroy contact A, update contact B";
    $res = $jmap->CallMethods([['Contact/set', {
                    destroy => [$contactA],
                    update => {$contactB => {firstName => "B"}}
                }, "R2"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/set', $res->[0][0]);
    $self->assert_str_equals('R2', $res->[0][2]);

    xlog "get contacts";
    $res = $jmap->CallMethods([
        ['Contact/get', {
            properties => ['firstName', 'lastName'],
         }, "R3"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/get', $res->[0][0]);
    $self->assert_str_equals('R3', $res->[0][2]);

    my @expect = sort { $a->{firstName} cmp $b->{firstName} } @{$res->[0][1]{list}};

    my $mark = time();
    sleep 2;

    xlog "destroy contact C, update contacts B and D, create contact E";
    $res = $jmap->CallMethods([['Contact/set', {
                    destroy => [$contactC],
                    update => {
                        $contactB => {lastName => "B"},
                        $contactD => {lastName => "D"},
                    },
                    create => {
                        "e" => {firstName => "e", lastName => "e"}
                    }
                }, "R4"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/set', $res->[0][0]);
    $self->assert_str_equals('R4', $res->[0][2]);
    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([
        ['Contact/get', {
            properties => ['firstName', 'lastName'],
         }, "R6"],
        ['ContactGroup/get', {}, "R6.1"]
    ]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/get', $res->[0][0]);
    $self->assert_str_equals('R6', $res->[0][2]);

    my @got = sort { $a->{firstName} cmp $b->{firstName} } @{$res->[0][1]{list}};
    $self->assert_num_equals(scalar @expect, scalar @got);
    $self->assert_deep_equals(\@expect, \@got);

    $self->assert_str_equals('ContactGroup/get', $res->[1][0]);
    $self->assert_str_equals('R6.1', $res->[1][2]);
    $self->assert_str_equals($contactC, $res->[1][1]{list}[0]{contactIds}[0]);

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

    $self->assert_str_equals('ContactGroup/changes', $res->[1][0]);
    $self->assert_str_equals('R6.6', $res->[1][2]);
    $self->assert_str_equals($state, $res->[1][1]{oldState});
    $self->assert_str_not_equals($state, $res->[1][1]{newState});
    $self->assert_equals(JSON::false, $res->[1][1]{hasMoreChanges});
    $self->assert_num_equals(1, scalar @{$res->[1][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[1][1]{updated}});
    $self->assert_num_equals(0, scalar @{$res->[1][1]{destroyed}});
    $state = $res->[1][1]{newState};

    $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([
        ['Contact/get', {
            properties => ['firstName', 'lastName'],
         }, "R8"],
        ['ContactGroup/get', {}, "R8.1"]
    ]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/get', $res->[0][0]);
    $self->assert_str_equals('R8', $res->[0][2]);
    $self->assert_deep_equals([], $res->[0][1]{list});

    $self->assert_str_equals('ContactGroup/get', $res->[1][0]);
    $self->assert_str_equals('R8.1', $res->[1][2]);
    $self->assert_deep_equals([], $res->[1][1]{list});

    xlog "get contact updates";
    $res = $jmap->CallMethods([
        ['Contact/changes', {
            sinceState => $state
         }, "R8.5"],
        ['ContactGroup/changes', {
            sinceState => $state
         }, "R8.6"]
    ]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/changes', $res->[0][0]);
    $self->assert_str_equals('R8.5', $res->[0][2]);
    $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(3, scalar @{$res->[0][1]{destroyed}});

    $self->assert_str_equals('ContactGroup/changes', $res->[1][0]);
    $self->assert_str_equals('R8.6', $res->[1][2]);
    $self->assert_str_equals($state, $res->[1][1]{oldState});
    $self->assert_str_not_equals($state, $res->[1][1]{newState});
    $self->assert_equals(JSON::false, $res->[1][1]{hasMoreChanges});
    $self->assert_num_equals(0, scalar @{$res->[1][1]{created}});
    $self->assert_num_equals(0, scalar @{$res->[1][1]{updated}});
    $self->assert_num_equals(1, scalar @{$res->[1][1]{destroyed}});
    $state = $res->[1][1]{newState};
}
