#!perl
use Cassandane::Tiny;

sub test_contact_set
    :min_version_3_1
{
    my ($self) = @_;

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

    my $contact = {
        firstName => "first",
        lastName => "last",
        avatar => JSON::null
    };

    my $res = $jmap->CallMethods([['Contact/set', {create => {"1" => $contact }}, "R1"]]);
    $self->assert_not_null($res);
    $self->assert_str_equals('Contact/set', $res->[0][0]);
    $self->assert_str_equals('R1', $res->[0][2]);
    my $id = $res->[0][1]{created}{"1"}{id};

    # get expands default values, so do the same manually
    $contact->{id} = $id;
    $contact->{uid} = $id;
    $contact->{isFlagged} = JSON::false;
    $contact->{prefix} = '';
    $contact->{suffix} = '';
    $contact->{nickname} = '';
    $contact->{birthday} = '0000-00-00';
    $contact->{anniversary} = '0000-00-00';
    $contact->{company} = '';
    $contact->{department} = '';
    $contact->{jobTitle} = '';
    $contact->{online} = [];
    $contact->{phones} = [];
    $contact->{addresses} = [];
    $contact->{emails} = [];
    $contact->{notes} = '';
    $contact->{avatar} = undef;

    # Non-JMAP properties.
    $contact->{"importance"} = 0;
    $contact->{"x-hasPhoto"} = JSON::false;
    $contact->{"addressbookId"} = 'Default';

    if ($res->[0][1]{created}{"1"}{blobId}) {
        $contact->{blobId} = $res->[0][1]{created}{"1"}{blobId};
        $contact->{size} = $res->[0][1]{created}{"1"}{size};
    }

    xlog $self, "get contact $id";
    my $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);

    $self->assert_not_null($fetch);
    $self->assert_str_equals('Contact/get', $fetch->[0][0]);
    $self->assert_str_equals('R2', $fetch->[0][2]);
    $contact->{"x-href"} = $fetch->[0][1]{list}[0]{"x-href"};
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update isFlagged";
    $contact->{isFlagged} = JSON::true;
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {isFlagged => JSON::true} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update prefix";
    $contact->{prefix} = 'foo';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {prefix => 'foo'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update suffix";
    $contact->{suffix} = 'bar';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {suffix => 'bar'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update nickname";
    $contact->{nickname} = 'nick';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {nickname => 'nick'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update birthday (with JMAP datetime error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {birthday => '1979-04-01T00:00:00Z'} }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("birthday", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update birthday";
    $contact->{birthday} = '1979-04-01'; # Happy birthday, El Barto!
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {birthday => '1979-04-01'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update anniversary (with JMAP datetime error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {anniversary => '1989-12-17T00:00:00Z'} }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("anniversary", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update anniversary";
    $contact->{anniversary} = '1989-12-17'; # Happy anniversary, Simpsons!
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {anniversary => '1989-12-17'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update company";
    $contact->{company} = 'acme';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {company => 'acme'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update department";
    $contact->{department} = 'looney tunes';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {department => 'looney tunes'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update jobTitle";
    $contact->{jobTitle} = 'director of everything';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {jobTitle => 'director of everything'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    # emails
    xlog $self, "update emails (with missing type error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            emails => [{ value => "acme\@example.com" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("emails[0].type", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update emails (with missing value error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            emails => [{ type => "other" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("emails[0].value", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update emails";
    $contact->{emails} = [{ type => "work", value => "acme\@example.com", isDefault => JSON::true }];
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            emails => [{ type => "work", value => "acme\@example.com" }]
                        } }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    # phones
    xlog $self, "update phones (with missing type error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            phones => [{ value => "12345678" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("phones[0].type", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update phones (with missing value error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            phones => [{ type => "home" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("phones[0].value", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update phones";
    $contact->{phones} = [{ type => "home", value => "12345678" }];
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            phones => [{ type => "home", value => "12345678" }]
                        } }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    # online
    xlog $self, "update online (with missing type error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            online => [{ value => "http://example.com/me" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("online[0].type", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update online (with missing value error)";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            online => [{ type => "uri" }]
                        } }}, "R1"]]);
    $self->assert_str_equals("invalidProperties", $res->[0][1]{notUpdated}{$id}{type});
    $self->assert_str_equals("online[0].value", $res->[0][1]{notUpdated}{$id}{properties}[0]);

    xlog $self, "update online";
    $contact->{online} = [{ type => "uri", value => "http://example.com/me" }];
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            online => [{ type => "uri", value => "http://example.com/me" }]
                        } }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    # addresses
    xlog $self, "update addresses";
    $contact->{addresses} = [{
            type => "home",
            street => "acme lane 1",
            locality => "acme city",
            region => "",
            postcode => "1234",
            country => "acme land",
            label => undef,
        }];
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {
                            addresses => [{
                                    type => "home",
                                    street => "acme lane 1",
                                    locality => "acme city",
                                    region => "",
                                    postcode => "1234",
                                    country => "acme land",
                                    label => undef,
                                }]
                        } }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    xlog $self, "update notes";
    $contact->{notes} = 'baz';
    $res = $jmap->CallMethods([['Contact/set', {update => {$id => {notes => 'baz'} }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    xlog $self, "get contact $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);

    # avatar
    xlog $self, "upload avatar";
    $res = $jmap->Upload("some photo", "image/jpeg");
    my $blobId = $res->{blobId};
    $contact->{"x-hasPhoto"} = JSON::true;
    $contact->{avatar} = {
        blobId => $blobId,
        size => 10,
        type => "image/jpeg",
        name => JSON::null
    };

    xlog $self, "attempt to update avatar with invalid type";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id =>
                            {avatar => {
                                blobId => $blobId,
                                size => 10,
                                type => "JPEG",
                                name => JSON::null
                             }
                     } }}, "R1"]]);
    $self->assert_null($res->[0][1]{updated});
    $self->assert_not_null($res->[0][1]{notUpdated}{$id});

    xlog $self, "update avatar";
    $res = $jmap->CallMethods([['Contact/set', {update => {$id =>
                            {avatar => {
                                blobId => $blobId,
                                size => 10,
                                type => "image/jpeg",
                                name => JSON::null
                             }
                     } }}, "R1"]]);
    $self->assert(exists $res->[0][1]{updated}{$id});

    if ($res->[0][1]{updated}{$id}{blobId}) {
        $contact->{blobId} = $res->[0][1]{updated}{$id}{blobId};
        $contact->{size} = $res->[0][1]{updated}{$id}{size};
    }

    if ($res->[0][1]{updated}{$id}{avatar}{blobId}) {
        $contact->{avatar}{blobId} = $res->[0][1]{updated}{$id}{avatar}{blobId};
    }

    xlog $self, "get avatar $id";
    $fetch = $jmap->CallMethods([['Contact/get', {}, "R2"]]);
    $self->assert_deep_equals($contact, $fetch->[0][1]{list}[0]);
}
