use warnings;
use strict;

use Net::Domain qw(hostfqdn);
use JSON qw();
use Test::More;
use Storable qw();
use URI::Escape qw();

use JSON::PP;
use LWP::Debug;

my $uri = $ENV{CATALYST_SERVER} || ('https://'.hostfqdn.':4443');

my ($ua, $req, $res);

use Test::Collection;
$ua = Test::Collection->new()->ua();

# OPTIONS tests
{
    $req = HTTP::Request->new('OPTIONS', $uri.'/api/profilepackages/');
    $res = $ua->request($req);
    is($res->code, 200, "check options request");
    is($res->header('Accept-Post'), "application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-profilepackages", "check Accept-Post header in options response");
    my $opts = JSON::from_json($res->decoded_content);
    my @hopts = split /\s*,\s*/, $res->header('Allow');
    ok(exists $opts->{methods} && ref $opts->{methods} eq "ARRAY", "check for valid 'methods' in body");
    foreach my $opt(qw( GET HEAD OPTIONS POST )) {
        ok(grep { /^$opt$/ } @hopts, "check for existence of '$opt' in Allow header");
        ok(grep { /^$opt$/ } @{ $opts->{methods} }, "check for existence of '$opt' in body");
    }
}


my $t = time;
my $default_reseller_id = 1;

$req = HTTP::Request->new('POST', $uri.'/api/billingprofiles/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
    name => "test profile $t",
    handle  => "testprofile$t",
    reseller_id => $default_reseller_id,
}));
$res = $ua->request($req);
is($res->code, 201, "POST test billing profile");
my $billingprofile_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $billingprofile_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed billing profile");
my $billingprofile = JSON::from_json($res->decoded_content);

$req = HTTP::Request->new('POST', $uri.'/api/billingprofiles/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
    name => "test prepaid $t",
    handle  => "testprepaid$t",
    reseller_id => $default_reseller_id,
    prepaid => 1,
}));
$res = $ua->request($req);
is($res->code, 201, "POST test prepaid billing profile");
$billingprofile_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $billingprofile_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed prepaid billing profile");
my $prepaid_billingprofile = JSON::from_json($res->decoded_content);

$req = HTTP::Request->new('POST', $uri.'/api/billingprofiles/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
    name => "test free cash $t",
    handle  => "testfreecash$t",
    reseller_id => $default_reseller_id,
    interval_free_cash => 100
}));
$res = $ua->request($req);
is($res->code, 201, "POST test free cash billing profile");
$billingprofile_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $billingprofile_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed free cash billing profile");
my $free_cash_billingprofile = JSON::from_json($res->decoded_content);

$req = HTTP::Request->new('POST', $uri.'/api/billingprofiles/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
    name => "test free time $t",
    handle  => "testfreetime$t",
    reseller_id => $default_reseller_id,
    interval_free_time => 100
}));
$res = $ua->request($req);
is($res->code, 201, "POST test free time billing profile");
$billingprofile_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $billingprofile_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed free time billing profile");
my $free_time_billingprofile = JSON::from_json($res->decoded_content);

$req = HTTP::Request->new('POST', $uri.'/api/billingnetworks/');
$req->header('Content-Type' => 'application/json');
$req->header('Prefer' => 'return=representation');
$req->content(JSON::to_json({
    name => "test billing network ".$t,
    description  => "test billing network description ".$t,
    reseller_id => $default_reseller_id,
    blocks => [{ip=>'fdfe::5a55:caff:fefa:9089',mask=>128},
               {ip=>'fdfe::5a55:caff:fefa:908a'},
               {ip=>'fdfe::5a55:caff:fefa:908b',mask=>128},],
}));
$res = $ua->request($req);
is($res->code, 201, "POST test billingnetwork");
my $billingnetwork_uri = $uri.'/'.$res->header('Location');
$req = HTTP::Request->new('GET', $billingnetwork_uri);
$res = $ua->request($req);
is($res->code, 200, "fetch POSTed billingnetwork");
my $billingnetwork = JSON::from_json($res->decoded_content);

my %package_map = ();

{ #if ($enable_profile_packages) {
    $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/');
    $req->header('Content-Type' => 'application/json');
    $req->header('Prefer' => 'return=representation');
    $req->content(JSON::to_json({
        name => "test profile package " . $t,
        description  => "test profile package description " . $t,
        reseller_id => $default_reseller_id,
        initial_profiles => [{ profile_id => $billingprofile->{id}, }, ]
    }));
    $res = $ua->request($req);
    is($res->code, 201, "POST test profilepackage");
    my $profilepackage_uri = $uri.'/'.$res->header('Location');
    $req = HTTP::Request->new('GET', $profilepackage_uri);
    $res = $ua->request($req);
    is($res->code, 200, "fetch POSTed profilepackage");
    my $profilepackage = JSON::from_json($res->decoded_content);
    
    $req = HTTP::Request->new('PUT', $profilepackage_uri);
    $req->header('Content-Type' => 'application/json');
    $req->header('Prefer' => 'return=representation');
    $req->content(JSON::to_json({
        name => "test profile package ".$t." PUT",
        description  => "test profile package description ".$t." PUT",
        #reseller_id => $reseller_id,
        initial_profiles => [{ profile_id => $billingprofile->{id}, }, ],
    }));
    $res = $ua->request($req);
    is($res->code, 200, "PUT test profilepackage");
    $req = HTTP::Request->new('GET', $profilepackage_uri);
    $res = $ua->request($req);
    is($res->code, 200, "fetch PUT test profilepackage");
    $profilepackage = JSON::from_json($res->decoded_content);
    
    $req = HTTP::Request->new('PATCH', $profilepackage_uri);
    $req->header('Content-Type' => 'application/json-patch+json');
    $req->header('Prefer' => 'return=representation');
    $req->content(JSON::to_json(
        [ { op => 'replace', path => '/name', value => "test profile package ".$t." PATCH" } ]
    ));
    $res = $ua->request($req);
    is($res->code, 200, "PATCH test profilepackage");
    $req = HTTP::Request->new('GET', $profilepackage_uri);
    $res = $ua->request($req);
    is($res->code, 200, "fetch PATCHed test profilepackage");
    $profilepackage = JSON::from_json($res->decoded_content);

    $req = HTTP::Request->new('DELETE', $profilepackage_uri);
    $res = $ua->request($req);
    is($res->code, 204, "delete profilepackage");
    
    $req = HTTP::Request->new('GET', $profilepackage_uri);
    $res = $ua->request($req);
    is($res->code, 404, "try to fetch deleted test profilepackage");
    
}

{ #if ($enable_profile_packages) {
    
    my @profile_packages = ();
    
    for (my $i = 1; $i <= 3; $i++) {
        push(@profile_packages,_post_profile_package());
    }
    
    $req = HTTP::Request->new('GET', $uri.'/api/profilepackages/?page=1&rows=5&network_name='.URI::Escape::uri_escape('*')."network $t");
    $res = $ua->request($req);
    is($res->code, 200, "filter packages by network name");
    my $collection = JSON::from_json($res->decoded_content);
    is_deeply($collection->{_embedded}->{'ngcp:profilepackages'},[ map { $_->{get}; } @profile_packages ],"compare filtered collection deeply");

    ok(_post_profile_package(initial_profiles => [{ profile_id => $prepaid_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /mixing prepaid/i, "check if mixing prepaid initial profiles is prohibited");
    ok(_post_profile_package(underrun_profiles => [{ profile_id => $prepaid_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /mixing prepaid/i, "check if mixing prepaid underrun profiles is prohibited");
    ok(_post_profile_package(topup_profiles => [{ profile_id => $prepaid_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /mixing prepaid/i, "check if mixing prepaid topup profiles is prohibited");    

    ok('HASH' eq ref _post_profile_package(topup_profiles => [{ profile_id => $billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],
                             initial_profiles => [{ profile_id => $prepaid_billingprofile->{id}, network_id => undef },
                             { profile_id => $prepaid_billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       , "check if creating a package with mixed prepaid profile sets was ok");

    ok(_post_profile_package(initial_profiles => [{ profile_id => $free_cash_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_cash/i, "check if mixing free cash initial profiles is prohibited");
    ok(_post_profile_package(underrun_profiles => [{ profile_id => $free_cash_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_cash/i, "check if mixing free cash underrun profiles is prohibited");
    ok(_post_profile_package(topup_profiles => [{ profile_id => $free_cash_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_cash/i, "check if mixing free cash topup profiles is prohibited");    

    ok('HASH' eq ref _post_profile_package(topup_profiles => [{ profile_id => $billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],
                             initial_profiles => [{ profile_id => $free_cash_billingprofile->{id}, network_id => undef },
                             { profile_id => $free_cash_billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       , "check if creating a package with mixed free cash profile sets was ok");

    ok(_post_profile_package(initial_profiles => [{ profile_id => $free_time_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_time/i, "check if mixing free time initial profiles is prohibited");
    ok(_post_profile_package(underrun_profiles => [{ profile_id => $free_time_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_time/i, "check if mixing free time underrun profiles is prohibited");
    ok(_post_profile_package(topup_profiles => [{ profile_id => $free_time_billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       =~ /the same interval_free_time/i, "check if mixing free time topup profiles is prohibited");    

    ok('HASH' eq ref _post_profile_package(topup_profiles => [{ profile_id => $billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],
                             initial_profiles => [{ profile_id => $free_time_billingprofile->{id}, network_id => undef },
                             { profile_id => $free_time_billingprofile->{id}, network_id => $billingnetwork->{id}}],)
       , "check if creating a package with mixed free time profile sets was ok");
    
}

done_testing;

sub _post_profile_package {

    my (@further_opts) = @_;
    
    my $i = 1 + scalar keys %package_map;

    my %test_data = (post => {
        name => "test profile package ".$i . ' ' . $t,
        description  => "test profile package description ".$i . $t,
        reseller_id => $default_reseller_id,
        #status => 'active',
        initial_profiles => [{ profile_id => $billingprofile->{id}, network_id => undef },
                             { profile_id => $billingprofile->{id}, network_id => $billingnetwork->{id}}],
        initial_balance => 0.0,
        balance_interval_value => 30,
        balance_interval_unit => 'day',
        balance_interval_start_mode => 'create',
        service_charge => 0.0,
        notopup_discard_intervals => undef,
        carry_over_mode => 'carry_over',
        timely_duration_value => 7,
        timely_duration_unit => 'day',        
        underrun_profile_threshold => 0.0,
        underrun_profiles => [ { profile_id => $billingprofile->{id}, network_id => undef } ],
        underrun_lock_threshold => 0.0,
        underrun_lock_level => 4,
        topup_profiles => [ { profile_id => $billingprofile->{id}, network_id => undef } ],
        topup_lock_level => undef,
        @further_opts,
    });
    $req = HTTP::Request->new('POST', $uri.'/api/profilepackages/');
    $req->header('Content-Type' => 'application/json');
    $req->header('Prefer' => 'return=representation');
    $req->content(JSON::to_json($test_data{post}));
    $res = $ua->request($req);
    if ($res->code == 201) {
        is($res->code, 201, "create test profile package " . $i);
        $test_data{uri} = $uri.'/'.$res->header('Location');
        $req = HTTP::Request->new('GET', $test_data{uri});
        $res = $ua->request($req);
        is($res->code, 200, "fetch test profile package " . $i);
        my $get = JSON::from_json($res->decoded_content);
        $package_map{$get->{id}} = $get;
        $test_data{get} = Storable::dclone($get);
        delete $get->{id};
        delete $get->{_links};
        is_deeply($get,$test_data{post}, "check created profile package $i deeply");
        return \%test_data;
    } else {
        my $get = JSON::from_json($res->decoded_content);
        return $get->{message};
    }

}
