#!/usr/bin/perl

# Some tests for SIP::ILS::Patron
# This needs to be extended! Your help is appreciated..

use Modern::Perl;
use Test::More tests => 11;

use t::lib::Mocks;
use t::lib::TestBuilder;

use C4::SIP::ILS::Patron;
use Koha::Account::Lines;
use Koha::Database;
use Koha::DateUtils qw( dt_from_string output_pref );
use Koha::Patron::Attributes;
use Koha::Patrons;

my $schema = Koha::Database->new->schema;
$schema->storage->txn_begin;

my $builder = t::lib::TestBuilder->new();
my $patron1 = $builder->build({ source => 'Borrower' });
my $card = $patron1->{cardnumber};

# Check existing card number
my $sip_patron = C4::SIP::ILS::Patron->new( $card );
is( defined $sip_patron, 1, "Patron is valid" );

# Check invalid cardnumber by deleting patron
$schema->resultset('Borrower')->search({ cardnumber => $card })->delete;
my $sip_patron2 = C4::SIP::ILS::Patron->new( $card );
is( $sip_patron2, undef, "Patron is not valid (anymore)" );

subtest "new tests" => sub {

    plan tests => 5;

    my $patron = $builder->build(
        {
            source => 'Borrower'
        }
    );

    my $cardnumber      = $patron->{cardnumber};
    my $userid         = $patron->{userid};
    my $borrowernumber = $patron->{borrowernumber};

    my $ils_patron = C4::SIP::ILS::Patron->new($cardnumber);
    is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via cardnumber scalar' );
    $ils_patron = C4::SIP::ILS::Patron->new($userid);
    is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via userid scalar' );
    $ils_patron = C4::SIP::ILS::Patron->new( { borrowernumber => $borrowernumber } );
    is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via borrowernumber hashref' );
    $ils_patron = C4::SIP::ILS::Patron->new( { cardnumber => $cardnumber } );
    is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via cardnumber hashref' );
    $ils_patron = C4::SIP::ILS::Patron->new( { userid => $userid } );
    is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via userid hashref' );
};

subtest "OverduesBlockCirc tests" => sub {

    plan tests => 6;

    my $odue_patron = $builder->build(
        {
            source => 'Borrower',
            value  => {
                dateexpiry    => "3000-01-01",
            }
        }
    );
    my $good_patron = $builder->build(
        {
            source => 'Borrower',
            value  => {
                dateexpiry    => "3000-01-01",
            }
        }
    );
    my $odue = $builder->build({ source => 'Issue', value => {
            borrowernumber => $odue_patron->{borrowernumber},
            date_due => '2017-01-01',
            }
    });
    t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'noblock' );
    my $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, 1, "Not blocked with overdues when set to 'Don't block'");
    $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'Don't block'");

    t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'confirmation' );
    $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, '', "Blocked with overdues when set to 'Ask for confirmation'");
    $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'confirmation'");

    t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'block' );
    $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, '', "Blocked with overdues when set to 'Block'");
    $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
    is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'Block'");

};

subtest "Test build_patron_attribute_string" => sub {

    plan tests => 2;

    my $patron = $builder->build( { source => 'Borrower' } );

    my $attribute_type = $builder->build( { source => 'BorrowerAttributeType' } );
    my $attribute = Koha::Patron::Attribute->new(
        {
            borrowernumber => $patron->{borrowernumber},
            code           => $attribute_type->{code},
            attribute      => 'Test Attribute'
        }
    )->store();

    my $attribute_type2 = $builder->build( { source => 'BorrowerAttributeType' } );
    my $attribute2 = Koha::Patron::Attribute->new(
        {
            borrowernumber => $patron->{borrowernumber},
            code           => $attribute_type2->{code},
            attribute      => 'Another Test Attribute'
        }
    )->store();

    my $ils_patron = C4::SIP::ILS::Patron->new( $patron->{cardnumber} );

    my $server = {};
    $server->{account}->{patron_attribute}->{code} = $attribute->code;
    $server->{account}->{patron_attribute}->{field} = 'XY';
    my $attribute_string = $ils_patron->build_patron_attributes_string( $server );
    is( $attribute_string, "XYTest Attribute|", 'Attribute field generated correctly with single param' );

    $server = {};
    $server->{account}->{patron_attribute}->[0]->{code} = $attribute->code;
    $server->{account}->{patron_attribute}->[0]->{field} = 'XY';
    $server->{account}->{patron_attribute}->[1]->{code} = $attribute2->code;
    $server->{account}->{patron_attribute}->[1]->{field} = 'YZ';
    $attribute_string = $ils_patron->build_patron_attributes_string( $server );
    is( $attribute_string, "XYTest Attribute|YZAnother Test Attribute|", 'Attribute field generated correctly with multiple params' );
};

subtest "Test build_custom_field_string" => sub {

    plan tests => 5;

    my $patron = $builder->build_object( { class => 'Koha::Patrons',value=>{surname => "Duck", firstname => "Darkwing"} } );


    my $ils_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );

    my $server = {};
    $server->{account}->{custom_patron_field}->{field} = "DW";
    my $attribute_string = $ils_patron->build_custom_field_string( $server );
    is( $attribute_string, "", 'Custom field not generated if no value passed' );

    $server = {};
    $server->{account}->{custom_patron_field}->{template} = "[% patron.surname %]";
    $attribute_string = $ils_patron->build_custom_field_string( $server );
    is( $attribute_string, "", 'Custom field not generated if no field passed' );


    $server = {};
    $server->{account}->{custom_patron_field}->{field} = "DW";
    $server->{account}->{custom_patron_field}->{template} = "[% patron.firstname %] [% patron.surname %], let's get dangerous!";
    $attribute_string = $ils_patron->build_custom_field_string( $server );
    is( $attribute_string, "DWDarkwing Duck, let's get dangerous!|", 'Custom field processed correctly' );

    $server = {};
    $server->{account}->{custom_patron_field}->[0]->{field} = "DW";
    $server->{account}->{custom_patron_field}->[0]->{template} = "[% patron.firstname %] [% patron.surname %], let's get dangerous!";
    $server->{account}->{custom_patron_field}->[1]->{field} = "LM";
    $server->{account}->{custom_patron_field}->[1]->{template} = "Launchpad McQuack crashed on [% patron.dateexpiry %]";
    $attribute_string = $ils_patron->build_custom_field_string( $server );
    is( $attribute_string, "DWDarkwing Duck, let's get dangerous!|LMLaunchpad McQuack crashed on ".$patron->dateexpiry."|", 'Custom fields processed correctly when multiple exist' );

    $server = {};
    $server->{account}->{custom_patron_field}->[0]->{field} = "DW";
    $server->{account}->{custom_patron_field}->[0]->{template} = "[% IF (patron.firstname) %] patron.surname, let's get dangerous!";
    $server->{account}->{custom_patron_field}->[1]->{field} = "LM";
    $server->{account}->{custom_patron_field}->[1]->{template} = "Launchpad McQuack crashed on [% patron.dateexpiry %]";
    $attribute_string = $ils_patron->build_custom_field_string( $server );
    is( $attribute_string, "LMLaunchpad McQuack crashed on ".$patron->dateexpiry."|", 'Custom fields processed correctly, bad template generate no text' );

};

subtest "fine_items tests" => sub {

    plan tests => 12;

    my $patron = $builder->build(
        {
            source => 'Borrower',
        }
    );

    my $fee1 = $builder->build(
        {
            source => 'Accountline',
            value  => {
                borrowernumber => $patron->{borrowernumber},
                amountoutstanding => 1,
            }
        }
    );

    my $fee2 = $builder->build(
        {
            source => 'Accountline',
            value  => {
                borrowernumber => $patron->{borrowernumber},
                amountoutstanding => 1,
            }
        }
    );

    my $sip_patron = C4::SIP::ILS::Patron->new( $patron->{cardnumber} );

    my $all_fine_items = $sip_patron->fine_items;
    is( @$all_fine_items, 2, "Got all fine items" );

    # Should return only the first fine item
    my $fine_items = $sip_patron->fine_items(1,1);
    is( @$fine_items, 1, "Got one fine item" );
    is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct fine item");

    # Should return only the second fine item
    $fine_items = $sip_patron->fine_items(2,2);
    is( @$fine_items, 1, "Got one fine item" );
    is( $fine_items->[0]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct fine item");

    # Should return all fine items
    $fine_items = $sip_patron->fine_items(1,2);
    is( @$fine_items, 2, "Got two fine items" );
    is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct first fine item");
    is( $fine_items->[1]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct second fine item");

    # Check an invalid end boundary
    $fine_items = $sip_patron->fine_items(1,99);
    is( @$fine_items, 2, "Got two fine items" );
    is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct first fine item");
    is( $fine_items->[1]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct second fine item");

    # Check an invalid start boundary
    $fine_items = $sip_patron->fine_items(98,99);
    is( @$fine_items, 0, "Got zero fine items" );
};

$schema->storage->txn_rollback;

subtest "Patron expiration tests" => sub {

    plan tests => 5;

    $schema->storage->txn_begin;
    my $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => {
                opacnote => "",

                # dateexpiry is today when unspecified
            }
        }
    );

    t::lib::Mocks::mock_preference( 'NotifyBorrowerDeparture', 0 );
    my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/^Greetings from Koha. $/,
        "No message is displayed when NotifyBorrowerDeparture is disabled"
    );

    t::lib::Mocks::mock_preference( 'NotifyBorrowerDeparture', 1 );
    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/Your card will expire on/,
        "A message is displayed when the card expires within NotifyBorrowerDeparture days"
    );

    $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => {
                opacnote   => "",
                dateexpiry => "1900-01-01",
            }
        }
    );
    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/Your account has expired as of/,
        "A message is displayed when the card has expired and NotifyBorrowerDeparture is not disabled"
    );

    # Bug 38658 - SIP not marking patrons expired unless NotifyBorrowerDeparture has a positive value
    t::lib::Mocks::mock_preference( 'NotifyBorrowerDeparture', 0 );
    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/Your account has expired as of/,
        "A message is displayed when the card has expired and NotifyBorrowerDeparture has a value of 0"
    );

    t::lib::Mocks::mock_preference( 'NotifyBorrowerDeparture', undef );
    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/Your account has expired as of/,
        "A message is displayed when the card has expired and NotifyBorrowerDeparture has no value"
    );

    $schema->storage->txn_rollback;

};

subtest "NoIssuesChargeGuarantees tests" => sub {

    plan tests => 6;

    t::lib::Mocks::mock_preference( 'borrowerRelationship', 'parent' );

    $schema->storage->txn_begin;

    my $patron_category = $builder->build(
        {
            source => 'Category',
            value  => {
                categorycode   => 'NOT_X', category_type => 'P', enrolmentfee => 0, noissueschargeguarantees => 0,
                noissuescharge => 0,       noissueschargeguarantorswithguarantees => 0
            }
        }
    );

    my $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => {
                categorycode => $patron_category->{categorycode},
            }
        }
    );
    my $child  = $builder->build_object({ class => 'Koha::Patrons' });
    my $sibling  = $builder->build_object({ class => 'Koha::Patrons' });
    $child->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });
    $sibling->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });

    t::lib::Mocks::mock_preference('noissuescharge', 50);
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantees', 11.01);
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', undef);

    my $fee1 = $builder->build_object(
        {
            class => 'Koha::Account::Lines',
            value  => {
                borrowernumber => $patron->borrowernumber,
                amountoutstanding => 11,
                debit_type_code   => 'OVERDUE',
            }
        }
    )->store;

    my $fee2 = $builder->build_object(
        {
            class => 'Koha::Account::Lines',
            value  => {
                borrowernumber => $child->borrowernumber,
                amountoutstanding => 0.11,
                debit_type_code   => 'OVERDUE',
            }
        }
    )->store;

    my $fee3 = $builder->build_object(
        {
            class => 'Koha::Account::Lines',
            value  => {
                borrowernumber => $sibling->borrowernumber,
                amountoutstanding => 11.11,
                debit_type_code   => 'OVERDUE',
            }
        }
    )->store;

    my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );

    is( $sip_patron->fines_amount, 11, "Only patron's fines are reported in total");
    ok( !$sip_patron->charge_ok, "Guarantor blocked");
    like( $sip_patron->screen_msg, qr/Patron blocked by fines \(22\.22\) on guaranteed accounts/,"Screen message includes related fines total");

    $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );

    is( $sip_patron->fines_amount, 0.11,"Guarantee only fines correctly counted");
    ok( $sip_patron->charge_ok, "Guarantee not blocked by guarantor fines");
    unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on guaranteed accounts/,"Screen message does not include blocked message");

    $schema->storage->txn_rollback;
};

subtest "NoIssuesChargeGuarantorsWithGuarantees tests" => sub {

    plan tests => 12;

    t::lib::Mocks::mock_preference( 'borrowerRelationship', 'parent' );

    $schema->storage->txn_begin;

    my $patron_category = $builder->build(
        {
            source => 'Category',
            value  => {
                categorycode   => 'NOT_X', category_type => 'P', enrolmentfee => 0, noissueschargeguarantees => 0,
                noissuescharge => 0,       noissueschargeguarantorswithguarantees => 0
            }
        }
    );

    my $patron = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => {
                categorycode => $patron_category->{categorycode},
            }
        }
    );
    my $child = $builder->build_object(
        {
            class => 'Koha::Patrons',
            value => {
                categorycode => $patron_category->{categorycode},
            }
        }
    );
    $child->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });

    t::lib::Mocks::mock_preference('noissuescharge', 50);
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', 11.01);

    my $fee1 = $builder->build_object(
        {
            class => 'Koha::Account::Lines',
            value  => {
                borrowernumber => $patron->borrowernumber,
                amountoutstanding => 11,
                debit_type_code   => 'OVERDUE',
            }
        }
    )->store;

    my $fee2 = $builder->build_object(
        {
            class => 'Koha::Account::Lines',
            value  => {
                borrowernumber => $child->borrowernumber,
                amountoutstanding => 0.11,
                debit_type_code   => 'OVERDUE',
            }
        }
    )->store;

    my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );

    is( $sip_patron->fines_amount, 11, "Personal fines correctly reported");
    ok( !$sip_patron->charge_ok, "Guarantor blocked");
    like( $sip_patron->screen_msg, qr/Patron blocked by fines \(11\.11\) on related accounts/,"Screen message includes related fines total");

    $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );

    is( $sip_patron->fines_amount, 0.11, "Personal fines correctly reported");
    ok( !$sip_patron->charge_ok, "Guarantee blocked");
    like( $sip_patron->screen_msg, qr/Patron blocked by fines \(11\.11\) on related accounts/,"Screen message includes related fines total");

    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', 12.01);

    $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );

    is( $sip_patron->fines_amount, 0.11, "Personal fines correctly reported");
    ok( $sip_patron->charge_ok, "Guarantee not blocked");
    unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on related accounts/,"Screen message does not indicate block");

    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );

    is( $sip_patron->fines_amount, 11, "Personal fines correctly reported");
    ok( $sip_patron->charge_ok, "Patron not blocked");
    unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on related accounts/,"Screen message does not indicate block");

    $schema->storage->txn_rollback;
};

subtest "Patron messages tests" => sub {
    plan tests => 2;
    $schema->storage->txn_begin;
    my $today         = output_pref( { dt => dt_from_string(), dateonly => 1 } );
    my $patron        = $builder->build_object( { class => 'Koha::Patrons', value => { opacnote => q{} } } );
    my $library       = $builder->build_object( { class => 'Koha::Libraries' } );
    my $new_message_1 = Koha::Patron::Message->new(
        {
            borrowernumber => $patron->id,
            branchcode     => $library->branchcode,
            message_type   => 'B',
            message        => 'my message 1',
        }
    )->store;

    my $new_message_2 = Koha::Patron::Message->new(
        {
            borrowernumber => $patron->id,
            branchcode     => $library->branchcode,
            message_type   => 'B',
            message        => 'my message 2',
        }
    )->store;

    t::lib::Mocks::mock_preference( 'SIP2AddOpacMessagesToScreenMessage', 0 );
    my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    is( $sip_patron->screen_msg, 'Greetings from Koha. ' );

    t::lib::Mocks::mock_preference( 'SIP2AddOpacMessagesToScreenMessage', 1 );
    $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
    like(
        $sip_patron->screen_msg, qr/Messages for you: $today: my message 1 \/ $today: my message 2/,
        "Screen message includes patron messages"
    );

    $schema->storage->txn_rollback;
};
