#!/usr/bin/perl

use DateTime;
use Data::Dumper;
use Test::Exception;
use Test::Builder::Tester;
use Test::WWW::Mechanize::CGIApp;

use strict;
use warnings;

use Test::More tests => 164;
# use Test::More 'no_plan';

=begin # tests:
0) create new data for tests - outcome not tested
1) update unit_number on single patient entry to unique val - old val replaced by new
2) delete unit_number on single patient entry - old val replaced by default val
3) change unit_number of patient_case with > 1 request attached - single record change
4) select a previous patient_case and set scope to all
5) test effect of resubmitting same patient_case data - should be no change
6) manually change unit_number to match existing patient_case - for scope set to one
7) delete unit number - set scope to all
8) change location to one in same parent_organisation
9) change location to one in different parent_organisation
10) change hospital location to practice
11) change location and unit_number together
12) use back button & re-submit form (should not cause error)
13) emulate consequence of incomplete updates using 2 browser tabs
=cut

require 't/test-lib.pl';

my $mech = get_mech();

# login/logout tests handled by test-lib.pl

do_login();   # print $fh $mech->content; # exit;

my $time_now  = DateTime->now();

# create some new patient/request/patient_case records for this test suite:
{
    my $dbix = get_dbix();

    my $year = $time_now->year();

    # new data:
    my %new = (
        parent_organisations => [ # parent_code description referral_type_id
            [ 'DEF', 'OLDTOWN NHS TRUST', 3 ],
        ],
        patient_case => [ # patient_id, referral_source_id, unit_number
            [ 2, 1, 'ABC123' ],
        ],
        referral_sources => [ # display_name org_code parent_org_id referral_type_id
            [ 'Newtown District Hospital',  'ABC02', 1, 3 ],
            [ 'Oldtown & Region Trust ',    'DEF01', 5, 3 ],
        ],
        requests => [ # yr, request_id, patient_case_id, referrer_dept_id, now
            [ $year, 3, 2, 1, $time_now ], # hospital referral - duplicate of request #2
            [ $year, 4, 3, 1, $time_now ], # hospital referral - same as request #2, diff unit_number
        ],
        request_specimen => [ # request_id, specimen_id
            [ 3, 1 ],
            [ 4, 3 ],
        ],
    );

    my $test_data = get_test_data();

    while ( my ($tbl, $data) = each %new ) { # warn $tbl, "\n";
        my $fields = $test_data->{$tbl}->{fields}; # warn Dumper $fields;

        foreach my $data_set (@$data) { # warn $data_set, "\n";
            my $i = 0;
            # map field_name to its value:
            my %data = map { $fields->[$i++] => $_ } @$data_set; # warn Dumper \%data;

            $dbix->insert($tbl, \%data);
        }
    }

    # create_view();
}

# edit request patient_case:
{ # update unit_number only:

    # load request_edit page direct:
    $mech->get_ok('/request/edit/1');                         # print_and_exit();

    # no changes, just tests 'reason for edit':
    $mech->submit_form();                                     # print_and_exit();

    has_dfv_errors();
    has_missing();

    # change unit number to unqiue - will create new record:
    $mech->field( unit_number => 'ABC123');
    $mech->field( error_code_id =>1 );
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check fields are as expected:
    my %params = (
        name => 'green',
        old_unit_number => 1011,
        new_unit_number => 'ABC123',
        location => 'The Surgery, Newtown, NT1 1NT',
        old_val_retained => 0, # old unit_number should not be detected
    );
    check_edit(\%params);                                     # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/1');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended unit number \[1011 -> ABC123\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

{ # delete unit_number on single-patient entry:
    $mech->get_ok('/request/edit/1');                         # print_and_exit();

    # delete unit number:
    $mech->field( unit_number => '');
    $mech->field( error_code_id =>1 );
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check fields are as expected:
    my %params = (
        name => 'green',
        old_unit_number => 'ABC123',
        new_unit_number => 'UNKNOWN',
        location => 'The Surgery, Newtown, NT1 1NT',
        old_val_retained => 0, # old unit_number should not be detected
    );
    check_edit(\%params);

    # check history:
    $mech->get_ok('/history/=/1');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(deleted unit number \[ABC123\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

{ # change unit number of patient_case with > 1 request attached:
    # load request_edit page direct:
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    # check other patient_case data loaded:
    my %args = (
        unit_number => 'ABC123',
        patient_case_id => 3,
        record_count => 2,
    );
    check_previous(\%args);

    # change unit number to unqiue - will create new record:
    $mech->field( unit_number => '11223344');
    $mech->field( error_code_id =>1 );

    # just this record - no point testing missing 'scope' as it's not deselectable
    # should default to 'one' if unit_number not 'unknown' - tests opposite below
    # $mech->field( scope => 'one');

    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check fields are as expected:
    my %params = (
        name => 'brown',
        old_unit_number => 1234,
        new_unit_number => 11223344,
        location => 'Newtown General Infirmary',
        old_val_retained => 1, # old unit_number should still be detected
    );
    check_edit(\%params);                                     # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended unit number \[1234 -> 11223344\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

# select a previous patient_case and change all records to match:
{
    # load page direct:
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    # check other patient_cases data loaded:
    {
        my %args = (
            unit_number => '1234',
            patient_case_id => 2,
        );
        check_previous(\%args);
    }
    {
        my %args = (
            unit_number => 'ABC123',
            patient_case_id => 3,
        );
        check_previous(\%args);
    }                                                         # print_and_exit();

    $mech->field( use_patient_case_id => 2);
    $mech->field( scope => 'all');
    $mech->field( error_code_id =>1 );
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check fields are as expected:
    my %params = (
        name => 'brown',
        new_unit_number => 1234,
        old_unit_number => 11223344,
        location => 'Newtown General Infirmary',
        old_val_retained => 0, # old unit_number should not exist
    );
    check_edit(\%params);                                     # print_and_exit();
    # should also be detectable:
    $params{new_unit_number} = 'ABC123';
    check_edit(\%params);                                      # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended unit number \[11223344 -> 1234\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

# test effect of resubmitting same patient_case data - should be no change:
{
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    $mech->field( scope => 'all');
    $mech->field( error_code_id =>1 );
    $mech->submit_form();                                     # print_and_exit();

    # should have 'no changes' msg instead of 'edit success':
    $mech->content_lacks(
        get_messages('action')->{edit_success},
        'OK: edit success message not present',
    );
    $mech->content_contains(
        get_messages('request_edit')->{edit_failed},
        'OK: no changes detected message present',
    );

    $mech->has_tag(
        td => 1234,
        'OK: original unit number still present',
    );                                                        # print_and_exit();
}

# manually change unit_number to match existing patient_case - for scope set to all:
{
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    # check other patient_cases data loaded:
    {
        my %args = (
            unit_number => 'ABC123',
            patient_case_id => 3,
            record_count => 2,
        );
        check_previous(\%args);
    }

    # change unit number to one match one previously used:
    $mech->field( unit_number => 'ABC123' );
    $mech->field( scope => 'all');
    $mech->field( error_code_id =>1 );
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(2);

    # check fields are as expected:
    my %params = (
        name => 'brown',
        new_unit_number => 'ABC123',
        old_unit_number => 1234,
        location => 'Newtown General Infirmary',
        old_val_retained => 0, # old unit_number should not exist
    );
    check_edit(\%params);                                     # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended unit number \[1234 -> ABC123\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

{ # delete unit_number - scope = all:
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    # should have 3 records:
    $mech->text_contains(
        'all 3 records',
        'OK: correct number of records',
    );

    # delete unit number:
    $mech->field( unit_number => '');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'all');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(3);

    # check fields are as expected:
    my %params = (
        name => 'brown',
        old_unit_number => 'ABC123',
        new_unit_number => 'UNKNOWN',
        location => 'Newtown General Infirmary',
        old_val_retained => 0, # old unit_number should not be detected
    );
    check_edit(\%params);

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(deleted unit number \[ABC123\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

{ # update unit_number from 'unknown' - tests scope defaults to 'all':
    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    # should have 3 records:
    $mech->text_contains(
        'all 3 records',
        'OK: correct number of records',
    );                                                        # print_and_exit();

    # change unit number to one match one previously used:
    $mech->field( unit_number => 123456 );
    $mech->field( error_code_id =>1 );
    # should default to 'all' if unit_number = 'unknown'
    # $mech->field( scope => 'all');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(3);

    # check fields are as expected:
    my %params = (
        name => 'brown',
        new_unit_number => 123456,
        old_unit_number => 'UNKNOWN',
        location => 'Newtown General Infirmary',
        old_val_retained => 0, # old unit_number should not exist
    );
    check_edit(\%params);                                     # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended unit number \[UNKNOWN -> 123456\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

# change location to one in same parent_organisation:
{
    $mech->get_ok("/register/patient_search?name=brown");     # print_and_exit();
    $mech->follow_link(text => 'Select', n => 1);             # print_and_exit();
=begin TODO: # need to test for following before edit:
    #1.  	 Newtown General Infirmary  	123456
=cut

    $mech->get_ok('/search/=/2');                              # print_and_exit();

    $mech->has_tag(
        td => 'Newtown General Infirmary',
        'OK: expected location value found'
    );
    $mech->has_tag(
        td => 123456,
        'OK: expected unit_number value found'
    );
    $mech->has_tag_like(
        span => qr(Brown CC),
        'OK: expected referrer value found'
    );                                                        # print_and_exit();

    $mech->follow_link_ok(
        # {n => 8}, "Logout $_ via eighth link on page",
        { url_regex => qr(request/edit), },
        'edit request using link',
    );                                                        # print_and_exit();

    # change location to one in same parent_org as current:
    $mech->field( referral_source_id => 5 );
    # this would be done automatically by ajax function:
    $mech->field( _location_name => 'Newtown District Hospital');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'one');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check new details:
    $mech->has_tag(
        td => 'Newtown District Hospital',
        'OK: expected location value found'
    );
    $mech->has_tag(
        td => 123456,
        'OK: expected unit_number value found'
    );
    $mech->has_tag_like(
        span => qr(Brown CC),
        'OK: expected referrer value found'
    );                                                        # print_and_exit();
    # check old location not found:
    $mech->content_lacks(
        'Newtown General Infirmary',
        'OK: old location value not found'
    );                                                        # print_and_exit();

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended referral source \[Newtown General Infirmary -> Newtown District Hospital\]),
        'OK: expected history found',
    );                                                        # print_and_exit();

    $mech->get_ok("/register/patient_search?name=brown");    # print_and_exit();
    $mech->follow_link(text => 'Select', n => 1);            # print_and_exit();
=begin # TODO: need to test for following after edit:
    #1.  	 Newtown General Infirmary  	123456
    #2.  	 Newtown District Hospital  	123456 # <= new patient_case
=cut

}

# change location to one in different parent_organisation:
{
    # check referrer & location 1st:
    $mech->get_ok('/search/=/2');                             # print_and_exit();
    $mech->has_tag(
        td => 'Newtown District Hospital',
        'OK: expected location value found'
    );
    $mech->has_tag(
        td => 123456,
        'OK: expected unit_number value found'
    );
    $mech->has_tag_like(
        span => qr(Brown CC),
        'OK: expected referrer value found'
    );                                                        # print_and_exit();

    # going to create new referrer_department, so check correct type:
    $mech->content_like(
        qr(span class="acronym"(\s+)title="Haematology"),
        'OK: correct hospital_department attribute detected',
    );

    $mech->get_ok('/request/edit/2');                         # print_and_exit();

    $mech->field( referral_source_id => 6 );
    # this would be done automatically by ajax function:
    $mech->field( _location_name => 'Oldtown & Region Trust');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'one');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    $mech->has_tag(
        td => 'Oldtown & Region Trust',
        'OK: new location detected',
    );
    $mech->has_tag(
        span => 'Unknown',
        'OK: unknown referrer detected',
    );                                                        # print_and_exit();

    # have created new referrer_department, so check correct type:
    $mech->content_like(
        qr(span class="acronym"(\s+)title="Unknown/other"),
        'OK: changed hospital_department attribute correct',
    );

    # check history:
    $mech->get_ok('/history/=/2');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended referral source \[Newtown District Hospital -> Oldtown & Region Trust\]),
        'OK: expected history found',
    );                                                        # print_and_exit();

    $mech->get_ok("/register/patient_search?name=brown");     # print_and_exit();
    $mech->follow_link(text => 'Select', n => 1);             # print_and_exit();
=begin # TODO: need to test for following after edit:
    1.  Newtown General Infirmary  	123456
    2. 	Newtown District Hospital 	123456
    3. 	Oldtown & Region Trust 	    123456 <= new patient_case
=cut
}

# change hospital to gp:
{
    # check referrer & location 1st:
    $mech->get_ok('/search/=/3');                             # print_and_exit();
    $mech->has_tag(
        td => 'Newtown General Infirmary',
        'OK: expected location value found'
    );
    $mech->has_tag_like(
        span => qr(Brown CC),
        'OK: expected referrer value found'
    );                                                       # print_and_exit();

    # going to create new referrer_department, so check correct type:
    $mech->content_like(
        qr(span class="acronym"(\s+)title="Haematology"),
        'OK: correct hospital_department attribute detected',
    );

    $mech->get_ok('/request/edit/3');                         # print_and_exit();

    $mech->field( referral_source_id => 2 );
    # this would be done automatically by ajax function:
    $mech->field( _location_name => 'The Surgery, Newtown, NT1 1NT');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'one');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    $mech->has_tag(
        td => 'The Surgery, Newtown, NT1 1NT',
        'OK: new location detected',
    );
    $mech->has_tag(
        span => 'Unknown',
        'OK: unknown referrer detected',
    );                                                        # print_and_exit();
    $mech->content_lacks(
        'Newtown General Infirmary',
        'OK: old location not detected',
    );
    $mech->content_lacks(
        'Brown, CC',
        'OK: old referrer not detected',
    );                                                        # print_and_exit();

    # have created new referrer_department, so check correct type:
    $mech->content_like(
        qr(span class="acronym"(\s+)title="General Medical Practice"),
        'OK: changed hospital_department attribute correct',
    );

    # check history:
    $mech->get_ok('/history/=/3');                           #  print_and_exit();

    $mech->has_tag_like(
        td => qr(amended referral source \[Newtown General Infirmary -> The Surgery, Newtown, NT1 1NT\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

# change location and unit_number together:
{
    $mech->get_ok('/request/edit/1');                         # print_and_exit();

    is(
       $mech->value('unit_number', 1),
       'UNKNOWN',
       'OK: expected unit number detected',
    );                                                        # print_and_exit();
    is(
       $mech->value('_location_name', 1),
       'The Surgery, Newtown, NT1 1NT',
       'OK: expected location detected',
    );                                                        # print_and_exit();

    $mech->field( referral_source_id => 6 );
    $mech->field( unit_number => 54321 );
    # this would be done automatically by ajax function:
    $mech->field( _location_name => 'Oldtown & Region Trust');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'one');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    # check new details:
    $mech->has_tag(
        td => 'Oldtown & Region Trust',
        'OK: expected location value found'
    );
    $mech->has_tag(
        td => 54321,
        'OK: expected unit_number value found'
    );
    $mech->has_tag_like(
        span => qr(Unknown),
        'OK: expected referrer value found'
    );                                                        # print_and_exit();
    # check old location not found:
    $mech->content_lacks(
        'The Surgery, Newtown, NT1 1NT',
        'OK: old location value not found'
    );                                                        # print_and_exit();
    # check old referrer not found:
    $mech->content_lacks(
        'Brown, CC',
        'OK: old referrer not found'
    );                                                        # print_and_exit();

    # have created new referrer_department, so check correct type:
    $mech->content_like(
        qr(span class="acronym"(\s+)title="Unknown/other"),
        'OK: changed hospital_department attribute correct',
    );

    # check history:
    $mech->get_ok('/history/=/1');                            # print_and_exit();

    $mech->has_tag_like(
        td => qr(amended referral source \[The Surgery, Newtown, NT1 1NT -> Oldtown & Region Trust\]),
        'OK: expected history found',
    );
    $mech->has_tag_like(
        td => qr(amended unit number \[UNKNOWN -> 54321\]),
        'OK: expected history found',
    );                                                        # print_and_exit();
}

# use back button & re-submit form (should not cause error):
{
    $mech->get_ok('/request/edit/3');                         # print_and_exit();

    $mech->field( unit_number => 'ABC123');
    $mech->field( error_code_id =>1 );
    $mech->field( scope => 'one');
    $mech->submit_form();                                     # print_and_exit();

    has_edit_success(1);

    $mech->back();                                            # print_and_exit();
    $mech->field( error_code_id =>1 ); # still need to add this
    $mech->submit_form();                                     # print_and_exit();

    $mech->content_contains(
        get_messages('request_edit')->{edit_failed},
        'OK: no changes detected message present',
    );
}
# emulate what happens when request data loaded in tab1 then update on another
# record done in tab2, then tab1 update completed:
{
    # load request/patient_case data into session (1st tab):
    $mech->get_ok('/request/edit/2');                         # print_and_exit();
    # submit data for a different request (emulating 2nd tab):
    $mech->post_ok('/request/update/3',
        { unit_number => 'ABC1234', error_code_id => 1 });    # print_and_exit();
    $mech->content_contains(
        'session data belongs to a different request',
        'OK: incorrect request data',
    );
    my $yr = $time_now->year - 2000;
    $mech->content_like(
        qr{expected 3/$yr, retrieved 2/$yr},
        'OK: expected vs retrieved request mismatch',
    );                                                        # print_and_exit();
}

# do_logout(); # fails as last test caused 500 error so no logout link

sub check_edit {
    my $params = shift;
    my $name = $params->{name};

    # check new unit_number loaded
    $mech->has_tag(
        td => $params->{new_unit_number},
        'OK: new unit_number loaded',
    );
    # check old location still present:
    $mech->has_tag(
        td => $params->{location},
        'OK: original refereral_source still present',
    );                                                       # print_and_exit();

    # do reg lookup for same patient to check old record doesn't exist:
    $mech->get_ok("/register/patient_search?name=$name");    # print_and_exit();
    # should only be 1 record:
    $mech->content_like(
        qr(Found <b>1</b> record matching <b>name=$name</b>),
        'OK: only 1 matching record found',
    );                                                       # print_content();

    $mech->follow_link(text => 'Select', n => 1); # if ($name eq 'brown') { print_and_exit(); }
    # check it's the new record:
    $mech->has_tag(
        td => $params->{new_unit_number},
        'OK: new unit_number record found',
    );                                                       # print_and_exit();
    # old unit_number should be retained:
    if ($params->{old_val_retained}) {
        $mech->has_tag(
            td => $params->{old_unit_number},
            'OK: old unit_number record found',
        );                                                       # print_and_exit();
    }
    # old unit_number should be deleted:
    else {
        test_out( 'not ok 1 - foo' );
        test_fail( +1 );
        $mech->has_tag( td => $params->{old_unit_number}, 'foo' );
        test_test( 'OK: old unit_number record not found' );  # print_and_exit();
    }                                                           # print_content();
}

sub check_previous {
    my $args = shift;

    # check we have 'previous record' option:
    $mech->content_like(
        qr(name="use_patient_case_id"(\s+)value="$args->{patient_case_id}"),
        'OK: previous record case_id loaded'
    );
    $mech->content_like(
        qr(value="$args->{unit_number}"),
        'OK: previous record unit_number loaded'
    );
    $mech->has_tag_like(
        span => qr(select),
        "OK: 'select' label present",
    );                                                          # print_and_exit();

    if ( my $count = $args->{record_count} ) {
        # check we have 'Apply change to':
        $mech->has_tag_like(
            span => qr(Apply change to),
            'OK: scope options loaded',
        );
        $mech->text_contains(
            "all $count records",
            'OK: correct number of records',
        );                                                    # print_and_exit();
    }
}

sub has_edit_success {
    my $i = shift;
    my $msg = sprintf get_messages('request_edit')->{edit_success}, $i;

    $msg =~ s/([\(\)])/\\$1/g; # need to escape parens in edit_success msg or qr fails

    $mech->has_tag_like(
        p => qr(INFO: $msg),
        'OK: edit action succeeded',
    );
}

sub create_view {
    my $sql = <<SQL;
select
    `rq`.`request_number`,
    `rq`.`year`,
     `p`.`last_name`,
     `p`.`first_name`,
     `p`.`dob`,
    `pc`.`unit_number`,
     `p`.`nhs_number`,
    `rf`.`name` AS `referrer`,
    `rs`.`display_name` AS `location`,
    `hd`.`display_name` AS `department`
from `requests` `rq`
    join `patient_case` `pc` on(`rq`.`patient_case_id` = `pc`.`id`)
    join `patients` `p` on(`pc`.`patient_id` = `p`.`id`)
    join `referrer_department` `rd` on(`rq`.`referrer_department_id` = `rd`.`id`)
    join `referral_sources` `rs` on(`pc`.`referral_source_id` = `rs`.`id`)
    join `referrers` `rf` on(`rd`.`referrer_id` = `rf`.`id`)
    join `hospital_departments` `hd` on(`rd`.`hospital_department_code` = `hd`.`id`)
order by request_number
SQL

    my $dbh = get_dbh();
    $dbh->do( qq!CREATE OR REPLACE ALGORITHM=UNDEFINED SQL SECURITY DEFINER VIEW
        `test`.`_requests` AS $sql! );
}
