#!perl
use Cassandane::Tiny;

use Digest::MD5 qw(md5_hex);

my sub install_test($name, $arg) {
    my ($action) = split /_/, $name;
    Carp::croak("name must start with 'create_' or 'update_'")
      unless $action eq 'create' || $action eq 'update';

    $arg->{desc} //= do {
        my ( undef, $file, $line ) = caller;
        "test declared in $file at line $line";
    };

    require Sub::Install;
    Sub::Install::install_sub(
        {
            as   => "test_card_set_onlineservices_$name",
            code => sub ($self) {
                if ($action eq 'create') {
                    $self->assert_card_set_create_onlineservices($arg);
                } else {
                    $self->assert_card_set_update_onlineservices($arg);
                }
            },
        }
    );
}

## ContactCard/set{create} tests for 'onlineServices'

install_test(create_impp => {
    onlineServices => { 1 => { uri => 'https://uri.local' } },
    wantVcard => ['IMPP;PROP-ID=1:https://uri.local'],
});

install_test(create_xonlineservice => {
    onlineServices => {
        1 => { uri => 'https://uri.local', user => 'user' },
    },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local'],
});

install_test(create_xonlineservice_user => {
    onlineServices => {
        1 => { user => 'user' },
    },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;'],
});

install_test(create_impp_service => {
    onlineServices => {
        1 => { uri => 'https://uri.local', service => 'Service' },
    },
    wantVcard => ['IMPP;X-SERVICE-TYPE=Service;PROP-ID=1:https://uri.local'],
});

install_test(create_xonlineservice_service => {
    onlineServices => {
        1 => {
            uri     => 'https://uri.local',
            user    => 'user',
            service => 'Service',
        },
    },
    wantVcard => [ 'X-CYRUS-ONLINESERVICE;X-SERVICE-TYPE=Service;PROP-ID=1:user;https://uri.local' ],
});

install_test(create_xonlineservice_user_service => {
    onlineServices => {
        1 => { user => 'user', service => 'Service' },
    },
    wantVcard => [ 'X-CYRUS-ONLINESERVICE;X-SERVICE-TYPE=Service;PROP-ID=1:user;' ],
});

install_test(create_impp_uri_context_label => {
    onlineServices => {
        1 => {
            uri      => 'https://uri.local',
            contexts => { work => JSON::true },
            label    => 'label',
        }
    },
    wantVcard => [
        'impp0.IMPP;PROP-ID=1;TYPE=WORK:https://uri.local',
        'impp0.X-ABLabel:label'
    ],
});

install_test(create_xonlineservice_all => {
    onlineServices => {
        1 => {
            '@type'  => 'OnlineService',
            uri      => 'https://uri.local',
            user     => 'user',
            service  => 'Service',
            label    => 'label',
            contexts => { private => JSON::true },
            pref     => 2,
        },
    },
    wantVcard => [
        'x-cyr0.X-CYRUS-ONLINESERVICE;X-SERVICE-TYPE=Service;PROP-ID=1;PREF=2;TYPE=HOME:user;https://uri.local',
        'x-cyr0.X-ABLabel:label'
    ],
});

## ContactCard/set{update} tests for 'onlineServices'

install_test(update_xonlineservice => {
    fromVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local'],
    update    => { nicknames => { 1 => { name => 'nickname' } } },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local'],
});

install_test(update_impp => {
    fromVcard => ['IMPP;PROP-ID=1:https://uri.local'],
    update    => { nicknames => { 1 => { name => 'nickname' } } },
    wantVcard => ['IMPP;PROP-ID=1:https://uri.local'],
});

install_test(update_impp_change_uri => {
    fromVcard => ['IMPP;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/uri' => 'https://uri2.local' },
    wantVcard => ['IMPP;PROP-ID=1:https://uri2.local'],
});

install_test(update_impp_set_user => {
    fromVcard => ['IMPP;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/user' => 'user' },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local']
});

install_test(update_impp_change_user_xuserparam => {
    fromVcard => ['IMPP;X-USER=user;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/user' => 'user2' },
    wantVcard => ['IMPP;X-USER=user2;PROP-ID=1:https://uri.local'],
});

install_test(update_impp_change_user_usernameparam => {
    fromVcard => ['IMPP;USERNAME=user;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/user' => 'user2' },
    wantVcard => ['IMPP;USERNAME=user2;PROP-ID=1:https://uri.local'],
});

install_test(update_impp_unset_uri => {
    fromVcard => ['IMPP;PROP-ID=1:https://uri.local'],
    update    => {
        'onlineServices/1/uri'  => undef,
        'onlineServices/1/user' => 'user',
    },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;'],
});

install_test(update_xonlineservice_change_uri => {
    fromVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local'],
    update    => { 'onlineServices/1/uri' => 'https://uri2.local' },
    wantVcard =>
      ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri2.local'],
});

install_test(update_xonlineservice_unset_user => {
    fromVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:user;https://uri.local'],
    update    => { 'onlineServices/1/user' => undef },
    wantVcard => ['X-CYRUS-ONLINESERVICE;PROP-ID=1:;https://uri.local'],
});

install_test(update_socialprofile_change_uri => {
    fromVcard => ['SOCIALPROFILE;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/uri' => 'https://uri2.local' },
    wantVcard => ['SOCIALPROFILE;PROP-ID=1:https://uri2.local'],
});

install_test(update_socialprofile_change_user => {
    fromVcard => ['SOCIALPROFILE;VALUE=TEXT;PROP-ID=1:user'],
    update    => { 'onlineServices/1/user' => 'user2' },
    wantVcard => ['SOCIALPROFILE;VALUE=TEXT;PROP-ID=1:user2'],
});

install_test(update_socialprofile_set_uri => {
    fromVcard => ['SOCIALPROFILE;VALUE=TEXT;PROP-ID=1:user'],
    update    => { 'onlineServices/1/uri' => 'https://uri.local' },
    wantVcard => ['SOCIALPROFILE;USERNAME=user;PROP-ID=1:https://uri.local'],
});

install_test(update_socialprofile_set_user => {
    fromVcard => ['SOCIALPROFILE;PROP-ID=1:https://uri.local'],
    update    => { 'onlineServices/1/user' => 'user' },
    wantVcard => ['SOCIALPROFILE;USERNAME=user;PROP-ID=1:https://uri.local'],
});

install_test(update_xsocialprofile_fm => {
    fromVcard => ['X-SOCIAL-PROFILE;X-USER=user;TYPE=Github:https://uri.local'],
    update    => { name => { full => 'updated' } }, # update any property
    wantVcard => ['X-CYRUS-ONLINESERVICE;X-SERVICE-TYPE=Github;PROP-ID=[\w]+:user;https://uri.local'],
});

install_test(update_xfmonlineother => {
    fromVcard => ['X-FM-ONLINE-OTHER;LABEL=Foo:https://uri.local'],
    update    => { nicknames => { 1 => { name => 'nickname' } } },
    wantVcard => ['X-CYRUS-ONLINESERVICE;X-SERVICE-TYPE=Foo;PROP-ID=[\w]+:;https://uri.local'],
});

sub assert_card_set_create_onlineservices($self, $arg)
{
    my $jmap    = $self->{jmap};
    my $carddav = $self->{carddav};

    xlog $self, "Running test '$arg->{desc}'";
    my $res = $jmap->CallMethods(
        [
            [
                'ContactCard/set',
                {
                    create => {
                        card1 => {
                            '@type' => 'Card',
                            version => "1.0",
                            name    => { full => "$arg->{desc}" },
                            onlineServices => $arg->{onlineServices},
                        },
                    }
                },
                'R1',
            ],
            [
                'ContactCard/get',
                {
                    ids        => ['#card1'],
                    properties => ['onlineServices'],
                },
                'R2',
            ]
        ]
    );
    my $xhref = $res->[0][1]{created}{card1}{'cyrusimap.org:href'};
    $self->assert_not_null($xhref);
    my $id = $res->[0][1]{created}{card1}{id};
    $self->assert_not_null($id);

    xlog $self, "Assert Card object";
    delete($arg->{onlineServices}{1}{'@type'});
    $self->assert_deep_equals( $arg->{onlineServices},
        $res->[1][1]{list}[0]{onlineServices} );

    xlog $self, "Assert vCard";

    my $vcard = $carddav->Request( 'GET', $xhref, '',
        'Accept' => 'text/vcard; version=4.0' )->{content};
    $vcard =~ s/\r\n[\t ]//gs;    # unfold

    $self->assert_not_null($vcard);
    foreach my $ctline ( @{ $arg->{wantVcard} } ) {
        $self->assert_matches( qr/^$ctline\r$/m, $vcard );
    }
    $self->assert_does_not_match( qr|JSPROP|, $vcard );
}

sub assert_card_set_update_onlineservices($self, $arg)
{
    my $jmap    = $self->{jmap};
    my $carddav = $self->{carddav};

    xlog $self, "Running test '$arg->{desc}'";

    my $vprops = join( "\r\n", @{ $arg->{fromVcard} } ) . "\r\n";
    my $uid    = md5_hex($vprops);
    my $vcard =
        "BEGIN:VCARD\r\n"
      . "VERSION:4.0\r\n"
      . "UID:$uid\r\n"
      . "FN:$arg->{desc}\r\n"
      . $vprops
      . "END:VCARD\r\n";

    my $href = "Default/$uid.vcs";
    $carddav->Request( 'PUT', $href, $vcard, 'Content-Type' => 'text/vcard' );

    my $res = $jmap->CallMethods(
        [
            [
                'ContactCard/query',
                {
                    filter => { uid => $uid },
                },
                'R1',
            ],
        ]
    );
    $self->assert_num_equals( 1, scalar @{ $res->[0][1]{ids} } );
    my $cardId = $res->[0][1]{ids}[0];

    $res = $jmap->CallMethods(
        [
            [
                'ContactCard/set',
                {
                    update => {
                        $cardId => $arg->{update},
                    },
                },
                'R1',
            ],
        ]
    );
    $self->assert_not_null( $res->[0][1]{updated}{$cardId}{updated} );

    $vcard = $carddav->Request( 'GET', $href, '',
        'Accept' => 'text/vcard; version=4.0' )->{content};
    $vcard =~ s/\r\n[\t ]//gs;    # unfold

    $self->assert_not_null($vcard);
    foreach my $ctline ( @{ $arg->{wantVcard} } ) {
        $self->assert_matches( qr/^$ctline\r$/m, $vcard );
    }

    $self->assert_does_not_match( qr|JSPROP|, $vcard );
}
