package Referrer;

use LIMS::Local::ExcelHandler;

use Moose;
    with 'Role::RebuildTables';
use namespace::clean -except => 'meta';

use Data::Dumper;

has $_ => (	is => 'ro', isa => 'HashRef', required => 1 )
    for qw(db sql);

has $_ => ( is  => 'ro', isa => 'HashRef', lazy_build => 1 )
	for qw(
        parent_organisation_id_map
		custom_parent_organisation_id_map
        unknown_referrer_unknown_parent_org_id_map
    );

has unknown_org_codes => (
    is  => 'rw',
    isa => 'HashRef',
    default => sub { {} },
);

__PACKAGE__->meta->make_immutable;

my @tables = qw(referrers referrer_department);

sub convert {
    my $self = shift;

    my $dbh4  = $self->db->{dbh4};
    my $dbix3 = $self->db->{dbix3};
    my $dbix4 = $self->db->{dbix4};
#=begin
	my %SQL = (
		clinician_id => q!SELECT `id` FROM `referrers` WHERE `national_code` = ?!,

		referrer_organisation_combination_exists =>
            q!SELECT 1 FROM `referrer_department` WHERE `referrer_id` = ?
                AND `parent_organisation_id` = ?!,

		hilis3_clinicians => q!
			SELECT `cons_code`, `last_name`, `initials`, `speciality_code`, `org_code`
			FROM `hilis3`.`Clinician`
			WHERE `c_id` > 643 and `org_code` like '%00'!,
	);

    $self->rebuild_tables(\@tables);

    my $parent_org_id_map = $self->parent_organisation_id_map;

    CLINICIAN:
	for my $ref ($dbix3->query( $SQL{hilis3_clinicians} )->arrays) {
        my ($cons_code, $last_name, $inits, $speciality_code, $org_code) = @{ $ref };
		$org_code = uc $org_code; # a few are lc in src table

		my $clinician_id = $dbix4->query( $SQL{clinician_id}, $cons_code)->list;

        # if no referrers.id, create new clinician:
        if (! $clinician_id ) {
			# join with space for consistency with GP's:
            my $name = join ' ', $last_name, $inits;
            my $data = {
                national_code    => $cons_code,
                name             => $name,
                referral_type_id => 1, # clinician
            }; # print Dumper $data;

            $dbix4->insert('referrers', $data) or die $dbix4->error;

            $clinician_id
                = $dbix4->last_insert_id( undef, undef, qw/referrers id/ )
                    || die 'no last_insert_id';
        }

        { # changed Notts. City Hospital org_code to match current etrust file:
            no warnings 'uninitialized';
            $org_code =~ s/^(RCS)/RHA/;
        }

        # only need 1st 3 chars of org_code (eg SEK00, X60000, RR800, ):
        my ($org_code_prefix) = $org_code =~ /\A(...)/;

		# look for prefix in parent_organisation_id_map:
        my $parent_organisation_id = $parent_org_id_map->{$org_code_prefix};

        if (! $parent_organisation_id) {
			# try to match 1st 3 chars of parent_code from restricted list:
			my $custom_parent_organisation_id_map
				= $self->custom_parent_organisation_id_map;

			$parent_organisation_id
				= $custom_parent_organisation_id_map->{$org_code_prefix};
		}

		# if still no $parent_organisation_id:
        if (! $parent_organisation_id) {
			$self->unknown_org_codes->{$org_code_prefix} += 1;
            #warn "Cannot find parent_organisation_id for $org_code"
            #. " (clinician = $cons_code)";
            next CLINICIAN;
        }

        next CLINICIAN if
            $dbix4->query( $SQL{referrer_organisation_combination_exists},
                $clinician_id, $parent_organisation_id )->list;

        my $data = {
            referrer_id              => $clinician_id,
            parent_organisation_id   => $parent_organisation_id,
            hospital_department_code => $speciality_code,
        }; # print Dumper $data;


        $dbix4->insert('referrer_department', $data) or die $dbix4->error;

#        next CLINICIAN if $clinician_id % 1000; print $clinician_id, "\n";
    }

	{ # unknown referrers:
		my $unknown_parent_org_id_map
			= $self->unknown_referrer_unknown_parent_org_id_map;

		foreach my $referrer_type (qw/clinician practitioner/) {
			my $sql = qq!select id, default_unknown from referral_types
				where description = '$referrer_type'!;

			my ($referral_type_id, $default_unknown) = $dbix4->query($sql)->list;

			{ # referrers:
				my %data = (
					name => "Unknown $referrer_type",
					national_code => $default_unknown,
					referral_type_id => $referral_type_id,
				);

				$dbix4->insert('referrers', \%data) or die $dbix4->error;
			}
			{ # referrer_department:
				my $referrer_id
                    = $dbix4->last_insert_id( undef, undef, qw/referrers id/ )
                        || die 'no last_insert_id';

				my $parent_organisation_id
					= $unknown_parent_org_id_map->{$referrer_type};

				my %data = (
					referrer_id => $referrer_id,
					parent_organisation_id => $parent_organisation_id,
					hospital_department_code => 999, # unknown/other
				);

				$dbix4->insert('referrer_department', \%data) or die $dbix4->error;
			}
		}
	}
#=cut
    { # GP's (based on Admin::Config::GeneralPractitioners::regenerate():
        # get map of all practice_codes => parent_organisation.id:
        my $parent_organisation_id_map = $self->parent_organisation_id_map;

        # list of parent_codes from $parent_organisation_id_map keys:
        my @parent_codes = keys %$parent_organisation_id_map;

        my $xl = LIMS::Local::ExcelHandler->new(); # $self->debug($xl);
        $xl->source('GP');

        # get ref to array of all rows in egpcur.csv:
        my $general_practitioners = $xl->fetch_all;

        GP:
        foreach my $row (@$general_practitioners) { # warn Dumper $row; next;
            # skip row unless $row->{practice_code} matches entry in @practice_codes:
            next GP unless grep { $row->{practice_code} eq $_ } @parent_codes;

            my $practice_code = $row->{practice_code};

            { # referrers table:
                my %data = (
                    name             => $row->{name},
                    national_code    => $row->{code},
                    referral_type_id => 3, # practitioner
                );
                $dbix4->insert('referrers', \%data) or die $dbix4->error;
            }
            { # referrer_department table:
                my $referrer_id
                    = $dbix4->last_insert_id( undef, undef, qw/referrers id/ )
                        || die 'no last_insert_id';

                my %data = (
                    referrer_id              => $referrer_id,
                    hospital_department_code => 600,
                    parent_organisation_id
                        => $parent_organisation_id_map->{$practice_code},
                );
				$dbix4->insert('referrer_department', \%data) or die $dbix4->error;
            }
        }
	}
#=cut

    { # known missing GP's in referrers table (not in egpcur):
        my @missing_gps = (
            [ 'MCEWAN A',     'S3299276', 'S78024' ],
            [ 'ALEXANDER CM', 'G3381913', 'B86052' ],
            [ 'TAYLOR PF',    'G3333109', 'B86044' ],
#			[ 'SMITH DA',     'G8908649', 'B87029' ], # now at B87001 in 26/2/2010 egpcur.csv
            [ 'DOWSON DG',    'G8202185', 'B86054' ],
#			[ 'SMITH ML',     'G9501098', 'B83009' ], # now at B84007 in 4/10/2010 egpcur.csv
			# missing from Nov 2009 egpcur update:
			[ 'EISNER MC',	  'G3356694', 'B83063' ],
			[ 'JARVIS EH',    'G3391855', 'B86071' ],
			[ 'BAILEY S',     'G8142056', 'B83058' ],
			[ 'HARRIS AL',    'G3363492', 'B82026' ],
			[ 'CARPENTER RM', 'G3371088', 'B82026' ],
#			[ 'CAMERON AW',   'G8209508', 'B86014' ], # now at Y02002 in 4/10/2010 egpcur.csv
			[ 'SHERWOOD GJ',  'G8105910', 'B86052' ],
			# missing from Oct 2010 egpcur update:
			[ 'FURNESS F',	  'G3301072', 'B87031' ],
			[ 'ANTROBUS RD',  'G3314728', 'B83042' ],
			[ 'MACFIE AJ',	  'G3390328', 'B82026' ],
			[ 'FITZGERALD TA','G9608294', 'B86041' ], # now at B81014 in 4/10/2010 egpcur.csv
			# outreach GP's moved to new practice:
			[ 'WACHSMUTH CW', 'G8135258', 'B86058' ],
			[ 'HAYES G',      'G9406564', 'B82056' ],
			[ 'GRIFFITHS SH', 'G8532350', 'B86054' ],
			[ 'DAVIES RL',    'G8334095', 'B86050' ],
			[ 'GOODMAN HE',   'G8440431', 'B86057' ],
        );

        my $parent_organisation_id_map = $self->parent_organisation_id_map;

        foreach (@missing_gps) {
            my %data = (
                name             => $_->[0],
                national_code    => $_->[1],
                referral_type_id => 3, # GP
                active           => 'no', # assume retired
            );
            $dbix4->insert('referrers', \%data) or die $dbix4->error;

            my $referrer_id
                = $dbix4->last_insert_id( undef, undef, qw/referrers id/ )
                    || die 'no last_insert_id';

            {
                my $parent_organisation_id
                    = $parent_organisation_id_map->{$_->[2]}
				|| die "Cannot find parent_organisation_id for GP $_->[0] ($_->[1])";

                my %data = (
                    referrer_id              => $referrer_id,
                    hospital_department_code => 600,
                    parent_organisation_id   => $parent_organisation_id,
                );
                $dbix4->insert('referrer_department', \%data) or die $dbix4->error;
            }
        }
    }

    # now check all clinicians & gp's from hilis3 are in referrers table:
    { # clinicians:
        my $sql = q!select distinct(c.cons_code) from Main m left join
           Clinician c on m.Clinician = c.c_id where c.cons_code is not null!;

        my $hili3_clinicians = $dbix3->query( $sql )->flat;
        {
            my $sql = 'select 1 from referrers where national_code = ?';
            foreach (@$hili3_clinicians) {
                warn "Cannot find $_ in referrers table"
                    unless $dbix4->query($sql, $_)->list;
            }
        }
    }

    { # GP's:
        my $sql = q!select distinct(g.gp_code) from Main m left join
           GP g on m.Clinician = g.g_id where g.gp_code is not null!;

        my $hili3_gps = $dbix3->query( $sql )->flat;
        {
            my $sql = 'select 1 from referrers where national_code = ?';
            foreach (@$hili3_gps) {
                warn "Cannot find $_ in referrers table"
                    unless $dbix4->query($sql, $_)->list;
            }
        }
    }
    $self->convert_to_InnoDB($_) for @tables;
=begin
    { # dump unknown organisation codes to stdout:
        my $unknown_org_codes = $self->unknown_org_codes;
        print "$_: $unknown_org_codes->{$_}\n"
            foreach sort { $unknown_org_codes->{$b} <=> $unknown_org_codes->{$a} }
                keys %$unknown_org_codes;
    }
=cut
}

# returns hash of parent_org_id for unknown practice & hospital:
sub _build_unknown_referrer_unknown_parent_org_id_map {
	my $self = shift;
	my $dbh  = $self->db->{dbix4};

	my $sql = q!select parent_organisation_id from referral_sources where
		display_name = ?!;

	my %referrer_org_code_id = (
		clinician    => $dbh->query( $sql, 'Hospital (unknown/other)' )->list,
		practitioner => $dbh->query( $sql, 'Practice (unknown/other)' )->list,
	);

	return \%referrer_org_code_id;
}

sub _build_parent_organisation_id_map {
    my $self = shift;
    my $dbh  = $self->db->{dbix4};

    my $map
        = $dbh->query( 'select parent_code, id from parent_organisations' )->map;

    return $map;
}

# parent_organisation prefix -> id map for non-gp_practice org_codes > 3 chars:
sub _build_custom_parent_organisation_id_map {
	my $self = shift;
    my $dbh  = $self->db->{dbix4};

	my $sql = q!select left(parent_code,3), id from parent_organisations
		where parent_code not like 'B_____' and length(parent_code) > 3!;

	my $map = $dbh->query( $sql )->map;

	return $map;
}

1;
