use strict;
use warnings;

use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
use JSON;
use Clone qw/clone/;
use MIME::Base64 qw(encode_base64);
use feature "state";

my $ngcp_type = $ENV{NGCP_TYPE} || "sppro";

#init test_machine
my $test_machine = Test::Collection->new(
    name => 'preferences',
);
my $fake_data =  Test::FakeData->new;

my $test_machine_soundsets = Test::Collection->new(
    name => 'soundsets',
    QUIET_DELETION => 1,
);
my $fake_data_soundsets = Test::FakeData->new(
    keep_db_data => 1,
    test_machine => $test_machine_soundsets,
);
$fake_data_soundsets->load_collection_data('soundsets');
$fake_data_soundsets->apply_data({
    'soundsets' => {
        'reseller_id' => 1,
    },
    'billingprofiles' => {
        'reseller_id' => 1,
    },
    'customercontacts' => {
        'reseller_id' => 1,
        'email' => 'api_preferences_tests@tests.com',
    },
    'sysemcontacts' => {
        'email' => 'api_preferences_tests@tests.com',
    },
    'customers' => {
        'external_id' => 'api_preferences_tests',
    },
    'resellers' => {
        'name' => 'api_preferences_tests',
    },
    'contracts' => {
        'external_id' => 'api_preferences_tests',
    },
});
$fake_data_soundsets->process('soundsets');
$fake_data->{data}->{'orphansoundsets'} = $fake_data_soundsets->{data}->{'soundsets'};

$fake_data->set_data_from_script({
    preferences => {
        data => {
            peeringserver_id  =>  sub { return shift->get_id('peeringservers',@_); },
            customer_id  =>  sub { return shift->get_id('customers',@_); },
            subscriber_id  =>  sub { return shift->get_id('subscribers',@_); },
            domain_id  =>  sub { return shift->get_id('domains',@_); },
            profile_id  =>  sub { return shift->get_id('subscriberprofiles',@_); },
            pbxdevice_id  =>  sub { return shift->get_id('pbxdevicemodels',@_); },
            pbxfielddevice_id  =>  sub { return shift->get_id('pbxdevices',@_); },
            pbxdeviceprofile_id  =>  sub { return shift->get_id('pbxdeviceprofiles',@_); },
            emergencymappingcontainer_id  =>  sub { return shift->get_id('emergencymappingcontainers',@_); },

            rewriteruleset_id  =>  sub { return shift->get_id('rewriterulesets',@_); },
            headerruleset_id  =>  sub { return ($ngcp_type eq 'sppro' || $ngcp_type eq 'carrier')
                                                ? shift->get_id('headerrulesets',@_)
                                                : 1 },
            soundset_id  =>  sub { return shift->get_id('soundsets',@_); },
            orphan_soundset_id  =>  sub { return $fake_data_soundsets->get_id('soundsets',@_); },
            ncoslevel_id  =>  sub { return shift->get_id('ncoslevels',@_); },
        },
    },
});
#for item creation test purposes /post request data/
$test_machine->DATA_ITEM_STORE($fake_data->process('preferences'));
$test_machine->form_data_item( );

my @apis = qw/subscriber domain peeringserver customer profile pbxdevice pbxfielddevice pbxdeviceprofile/;
#my @apis = qw/peeringserver/;

foreach my $api (@apis){
    my $preferences_old;
    my $preferences_put;
    my $preferences_patch;
    my $res;
    my $index = $api.'_id';
    my ($preferences) = {'uri' => '/api/'.$api.'preferences/'.$test_machine->DATA_ITEM->{$index}};
    (undef, $preferences_old) = $test_machine->check_item_get($preferences->{uri});
    #$preferences->{content} = $preferences_old;

    my $api_test_machine = Test::Collection->new(
        name => $api.'preferences',
    );
    $api_test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS POST)};
    $api_test_machine->methods->{item}->{allowed}       = {map {$_ => 1} qw(GET HEAD OPTIONS PUT PATCH DELETE)};
    my $defs = $api_test_machine->get_item_hal($api.'preferencedefs');
    delete $defs->{content}->{_links};
    foreach my $preference_name(keys %{$defs->{content}}){
        my $preference = $defs->{content}->{$preference_name};
        $preference->{name} = $preference_name;
        #if($preference->{read_only}){
        #    next;
        #}
        my $value;
        if('boolean' eq $preference->{data_type}){
            $value = JSON::true;
        }elsif('enum' eq $preference->{data_type}){
            my @values = @{$preference->{enum_values}};
            if(@values){
                if($#values > 0){
                #take second value from enum if exists
                    $value = $values[1]->{value};
                }
            }
            #foreach my $preference_enum_value(@{$preference->{enum_values}}){
            #
            #}
        }elsif('string' eq $preference->{data_type}){
            $value = get_preference_existen_value($preference, $api) // "test_api preference string";
        }elsif('int' eq $preference->{data_type}){
            $value = get_preference_existen_value($preference, $api) // 33;
        }elsif('blob' eq $preference->{data_type}){
            $value = {content_type => "text/plain", data => encode_base64("Test blob text")};
        }else{
            die("unknown data type: ".$preference->{data_type}." for $preference_name;\n");
        }
        if($value && 'no_process' ne $value){
            if($preference->{max_occur} > 0 ){
                if ($preference->{max_occur} eq '1') {
                    $preferences->{content}->{$preference_name} = $value;
                } else {
                    $preferences->{content}->{$preference_name} = [$value];
                }
            }
        }else{
            #print "Undefined value for preference: $api:$preference_name;\n";
            #print Dumper $preference;
        }
    }
    #(undef, $preferences_put->{content}) = $test_machine->request_put($preferences->{content},$preferences->{uri});
    #we don't check read_only flag when update preferences?
    (undef, $preferences_put->{content}) = $test_machine->check_put2get({data_in=>$preferences->{content},uri=>$preferences->{uri}},undef, { skip_compare => 1 });
    (undef, $preferences_patch->{content}) = $test_machine->check_patch2get({location => $preferences->{uri}} );

    if ($api eq 'subscriber') {
        diag("test extended patch");
        my $preferences_new;
        my $addmulti_value = ['111','222','222','333'];
        my $preferences_patch_op = [
            {'op' => 'add', 'path' => '/allowed_clis', value => $addmulti_value, mode => 'append' },
        ];
        ($res, $preferences_put->{content}) = $test_machine->request_patch($preferences_patch_op,$preferences->{uri});
        $test_machine->http_code_msg(422, "check extended patch result: add multi to absent", $res, $preferences_put->{content});

        $addmulti_value = ['444','555','666','777','888'];
        $preferences_patch_op = [
            {'op' => 'add', 'path' => '/allowed_clis', value => $addmulti_value, mode => 'append' },
        ];
        ($res, $preferences_put->{content}) = $test_machine->request_patch($preferences_patch_op,$preferences->{uri});
        $test_machine->http_code_msg(200, "check extended patch result: add multi to existing", $res, $preferences_put->{content});
        (undef, $preferences_new->{content}) = $test_machine->check_item_get($preferences->{uri});
        is_deeply([@{$preferences_new->{content}->{allowed_clis}}[-5..-1]], $addmulti_value, "check patched allowed_clis: add multi to existing");

        $preferences_patch_op = [
            {'op' => 'remove', 'path' => '/allowed_clis', value => '888', 'index' => 1 },
        ];
        ($res, $preferences_put->{content}) = $test_machine->request_patch($preferences_patch_op,$preferences->{uri});
        $test_machine->http_code_msg(200, "check extended patch result: remove by value", $res, $preferences_put->{content});
        (undef, $preferences_new->{content}) = $test_machine->check_item_get($preferences->{uri});
        is_deeply([@{$preferences_new->{content}->{allowed_clis}}[-5..-2]], ['444','555','666','777'], "check patched allowed_clis: remove by value.");
    }


    ($res, $preferences_put->{content}) = $test_machine->request_put($preferences_old,$preferences->{uri});
    $test_machine->http_code_msg(200, "check return old values", $res, $preferences_put->{content});
}

$test_machine->clear_test_data_all();
undef $fake_data;
undef $test_machine;
done_testing;

#----------------- aux
sub get_preference_existen_value{
    my ($preference, $api) = @_;
    my $res;
    if($preference->{name}=~/^rewrite_rule_set$/){
        $res = get_fake_data_created_or_data('rewriterulesets','name');
    }elsif($preference->{name}=~/^header_rule_set$/){
        if ($ngcp_type eq 'sppro' || $ngcp_type eq 'carrier') {
            $res = get_fake_data_created_or_data('headerrulesets','name');
        } else {
            $res = 'no_process';
        }
    }elsif($preference->{name}=~/^(adm_)?(cf_)?ncos$/){
        $res = get_fake_data_created_or_data('ncoslevels','level');
    }elsif($preference->{name}=~/^(adm_)?(cf_)?ncos_set$/){
        $res = 'no_process';
    }elsif($preference->{name}=~/^(contract_)?sound_set$/){
        if ($api eq 'peeringserver') {
            $res = get_fake_data_created_or_data('orphansoundsets','name');
        } else {
            $res = get_fake_data_created_or_data('soundsets','name');
        }
    }elsif($preference->{name}=~/^emergency_mapping_container$/){
        $res = get_fake_data_created_or_data('emergencymappingcontainers','name');
    }elsif($preference->{name}=~/^(man_)?allowed_ips_grp$/){
        $res = 'no_process';
    }
    return $res;
}

sub get_fake_data_created_or_data{
    my($collection_name, $field, $number) = @_;
    $number //= 0;
    my $res = $fake_data->created->{$collection_name}
        ?
        $fake_data->created->{$collection_name}->{values}->[$number]->{content}->{$field}
        :
        $fake_data->{data}->{$collection_name}->{data}->{$field};
    return $res;
}

__DATA__

'lbrtp_set' => {
    'enum_values' => [
        {
            'value' => undef,
            'default_val' => $VAR1->{'mobile_push_expiry'}{'read_only'},
            'label' => 'None'
        },
        {
            'value' => '50',
            'default_val' => $VAR1->{'mobile_push_expiry'}{'read_only'},
            'label' => 'default'
        }
    ],
    'data_type' => 'enum',
    'read_only' => $VAR1->{'mobile_push_expiry'}{'read_only'},
    'max_occur' => 1,
    'label' => 'The cluster set used for SIP lb and RTP',
    'description' => 'Use a particular cluster set of load-balancers for SIP towards this endpoint (only for peers, as for subscribers it is defined by Path during registration) and of RTP relays (both peers and subscribers).'
},

# vim: set tabstop=4 expandtab:
