package ReferralSource;

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

use FindBin qw($Bin);
use Data::Dumper;
use IO::All;

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

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

has other_hospitals => ( is => 'ro', isa => 'ArrayRef', lazy_build => 1 );

has path_to_app => ( is  => 'ro', isa => 'Str', required => 1 );

__PACKAGE__->meta->make_immutable;

my @tables = qw(referral_sources parent_organisations);

sub convert {
    my $self = shift;

    my $dbh4  = $self->db->{dbh4};
    my $dbix3 = $self->db->{dbix3};
    my $dbix4 = $self->db->{dbix4};

    $self->rebuild_tables(\@tables);
    $self->populate_parent_organisations_table;

    my $parent_org_id_map = $self->parent_organisation_id_map;
    # warn Dumper $parent_org_id_map;

    { # unknowns & resolved duplicates:
        my $other_hospitals = $self->other_hospitals; # warn Dumper $other_hospitals;

        foreach my $other (@$other_hospitals) { # warn $other->[0];
            my $parent_organisation_id = $parent_org_id_map->{$other->[2]}
            || die "Cannot find parent_org_id for $other->[0]";

            my %data = (
                display_name      => $other->[0],
                organisation_code => $other->[1],
                referral_type_id  => 4, # hospital
                parent_organisation_id => $parent_organisation_id,
            );
            $dbix4->insert( 'referral_sources', \%data);
        }
    }

    { # hospitals:
        my @hospital_ids =
            $dbix3->query( q!select distinct(Source) from Main!)->flat;

        HOSPITAL:
        foreach my $hospital_id (@hospital_ids) {
            # skip Other, Surgery/Med Centre, Notts. City Hospital:
            next if grep $hospital_id == $_, qw(54 56 57 78);

            # get Location & OrgCode from Source:
            $dbix3->query(
                q!select Location, OrgCode from Source where Src_ID = ?!, $hospital_id
            )->into( my ($location, $org_code) );

            if (! $org_code) {
                my $sql = 'select 1 from referral_sources where display_name = ?';

                unless ( $dbix4->query( $sql, $location )->list ) {
                    warn "no entry for $hospital_id";
                }
                next HOSPITAL;
            }

            # duplicate OrgCodes, resolved by direct entry in @other_hospitals above:
#            next if grep $org_code eq $_, qw(STC01 RCD35 RTGFA RWDDA RTD02 SGA07 RCSLB);
            next if $self->other_hospitals_map->{$org_code};

            my $parent_organisation_id = 0;
            { # get parent_organisation_id:
                my $parent_org_id_map   = $self->parent_organisation_id_map;
                my $local_hospitals_map = $self->local_hospitals_map;

                if (
                        $org_code =~ /\A([RYZ]..)..\Z/ || # England, Ireland, HMP
                        $org_code =~ /\A(S..)\d{2}\Z/ # Scottish sources
                    ) { # warn $org_code;
                    $parent_organisation_id = $parent_org_id_map->{$1};
                }
                if (! $parent_organisation_id) {
                    if (my $mapped_code = $local_hospitals_map->{$org_code}) {
                        # warn $org_code; warn $mapped_code;
                        $parent_organisation_id = $parent_org_id_map->{$mapped_code};
                        # warn $parent_organisation_id;
                    }
                    else { # warn $org_code;
                        $parent_organisation_id = $parent_org_id_map->{$org_code}
                        || die "Cannot find parent_organisation_id for $org_code";
                    }
                }
            }
            # warn Dumper ($org_code, $parent_organisation_id);
            my %data = (
                display_name      => $location,
                organisation_code => $org_code,
                referral_type_id  => 4, # hospital
                parent_organisation_id => $parent_organisation_id,
            );
            $dbix4->insert( 'referral_sources', \%data);
        }
    }

    { # unlisted/unknown referral sources:
        my @other_sources = (
            [ 'Practice (unknown/other)', 'UNKNOWN PRACTICE', 'V81999', 5 ],
            [ 'Hospital (unknown/other)', 'UNKNOWN HOSPITAL', 'X99999', 4 ],
            [
                'East Calder Health Centre, 147 Main Street, West Lothian, EH53 0EW',
                'EAST CALDER HC, EH53 0EW',
                'S78024',
                5,
            ],
        );

        foreach my $other (@other_sources) {
            {
                my %data = (
                    description => $other->[1],
                    parent_code => $other->[2],
                    referral_type_id => $other->[3],
                );
                $dbix4->insert('parent_organisations', \%data);
            }

            my $parent_organisation_id
                = $dbix4->last_insert_id(undef, undef, 'parent_organisations', 'id')
                || die "Cannot retrieve parent_organisation_id for $other->[2]";

            my %data = (
                display_name      => $other->[0],
                organisation_code => $other->[2],
                referral_type_id  => $other->[3],
                parent_organisation_id => $parent_organisation_id,
            );
            $dbix4->insert( 'referral_sources', \%data);
        }
    }

    { # general practices:
        my @hilis_practices = $dbix3->query(
            q!select DISTINCT(practice_code) from Main m, GP g
                where m.Clinician = g.G_ID and m.Clinician like 'G%'
                    and Clinician <> 'G1'!
        )->flat; # warn scalar @hilis_practices;

        # add community_monitoring practices:
        my @outreach_practices = $dbix3->query(
            q!select DISTINCT(practice_id) FROM community_monitoring.patient_details
            where practice_id <> ?!, 'B00000'
        )->flat; # warn scalar @outreach_practices;
        
        # combine into unique practices array:
        my %seen = ();        
        my @practice_codes
            = grep { ! $seen{$_}++ } (@hilis_practices, @outreach_practices);
            # warn scalar @practice_codes; warn Dumper \%seen; exit;
        PRACTICE:
        foreach my $practice_code (@practice_codes) {
            $dbix3->query(
                q!select address, post_code from community_monitoring.gp_practices
                    where practice_id = ?!, $practice_code
            )->into( my ($address, $post_code) );

            if (! $address) {
                my $sql = 'select 1 from referral_sources where organisation_code = ?';

                unless ( $dbix4->query( $sql, $practice_code )->list ) {
                    warn "no address details for $practice_code";
                }
                next PRACTICE;
            }

            { # add practice to parent_organisations & retrieve new id:
                my @address = split ', ', $address;

                my $description = join ', ', uc $address[0], $post_code;

                my %data = (
                    parent_code => $practice_code,
                    description => $description,
                    referral_type_id => 5,
                );
                $dbix4->insert('parent_organisations', \%data);
            }

            my $parent_organisation_id
                = $dbix4->last_insert_id(undef, undef, 'parent_organisations', 'id')
                  || die "Cannot retrieve parent_organisation_id for $practice_code";

            # warn Dumper ($practice_code, $parent_organisation_id);
            my %data = (
                display_name      => (join ', ', $address, $post_code),
                organisation_code => $practice_code,
                referral_type_id  => 5, # practice
                parent_organisation_id => $parent_organisation_id,
            );
            $dbix4->insert( 'referral_sources', \%data);
        }
    }
    # now check all Sources & GP practices from hilis3 are in referral_sources table:
    {
        my $sql = q!select distinct(s.OrgCode) from Main m left join
           Source s on m.Source = s.Src_ID where s.OrgCode is not null!;

        my $hilis3_locations = $dbix3->query( $sql )->flat;
        {
            my $sql = 'select 1 from referral_sources where organisation_code = ?';
            foreach (@$hilis3_locations) {
                next if $_ eq 'RCSLB'; # Notts. City Hospital -> RHALB 
                warn "Cannot find $_ in referral_sources table"
                    unless $dbix4->query($sql, $_)->list;
            }
        }
    }
    {
        my $sql = q!select distinct(g.practice_code) from Main m left join GP g
            on g.g_id = m.Clinician where m.Clinician like 'G%' and m.Clinician <> 'G1'!;

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

sub populate_parent_organisations_table {
    my $self = shift;

    my $path_to_app = $self->path_to_app;
    my $path_to_src_file
        = "$path_to_app/setup/schema/parent_organisations_updated.sql";

    my @data = io($path_to_src_file)->slurp;
    my $dbh = $self->db->{dbh4};

    foreach (@data) {
        next unless /\A(INSERT INTO)/;
        $dbh->do($_);
    }
}

sub _build_other_hospitals {
    my $self = shift;

    my @other_hospitals = (
        [ 'Ghana (Kumasi)',                         'GHANA', 'GHANA' ],
        [ 'Ninewells / Tayside, Dundee',            'STC01', 'STC',  ],
        [ 'Harrogate & District',                   'RCD35', 'RCD',  ],
        [ 'Derby Royal Infirmary',                  'RTGFA', 'RTG',  ],
        [ 'Lincoln County',                         'RWDDA', 'RWD',  ],
        [ 'PathLinks (Lincoln)',                    'RWD00', 'RWD',  ],
        [ 'Royal Victoria Infirmary, Newcastle',    'RTD02', 'RTD',  ],
        [ 'University of Newcastle (RVI)',          'RTD00', 'RTD',  ],
        [ 'Western / Gartnavel, Glasgow',           'SGA07', 'SGA',  ],
        [ 'Nottingham City Hospital',               'RHALB', 'RHA',  ],
#       [ 'Mid Yorks Nuffield',                     '8AV07'  ], # = NT225
#       [ 'Hospital (unknown/other)',               'X99999' ],
    );

    return \@other_hospitals;
}

sub _build_other_hospitals_map {
    my $self = shift;

    my %duplicates;
    my $other_hospitals = $self->other_hospitals;

    map { $duplicates{ $_->[1] }++ } @$other_hospitals;

    return \%duplicates;
}

sub _build_local_hospitals_map {
    my $self = shift;

    my %local_hospitals_map = (
        NT217 => 'RCD', # Harrogate
        NT220 => 'RWY', # Huddersfield
        NT221 => 'RWA', # Hull
        NT225 => 'RR8', # Leeds
        NT226 => 'RWE', # Leicester
        NT239 => 'RWF', # Tunbridge Wells
        NT242 => 'RL4', # Wolverhampton
        NT243 => 'RCB', # York
        NT308 => 'RTP', # East Surrey
        NT320 => 'RR1', # Parkway (Solihull)
        NT321 => 'RR1', # Little Aston (Sutton Coldfield)
        NT322 => 'RWE', # Leicester
        NT328 => 'RAE', # Elland
        NT330 => 'RXF', # Methley Park (Wakefield)
        NT331 => 'RWA', # Hull
        NT332 => 'RR8', # Leeds
        NT401 => 'RM2', # Univ. South Manchester - TODO: check this
        NT408 => 'RVV', # Kent & Canterbury
        NT409 => 'RGZ', # Chelsfield Park (Kent)
        NT438 => 'RWF', # Maidstone
        NT448 => 'RWY', # Huddersfield
        NT818 => 'RAE', # Yorkshire clinic (Bradford)
        NTY65 => 'RAX', # Kingston (upon Thames)
        NV106 => 'RWF', # Blackheath (Maidstone)
        69390 => 'RJE', # North Staffs pathology lab
        S314H => 'SSC', # Edinburgh Royal Infirmary
        RCSLB => 'RHA', # Nottingham City Hospital
        L308H => 'SLC', # Wishaw -> North Lanarkshire
    );

    return \%local_hospitals_map;
}

sub _build_parent_organisation_id_map {
    my $self = shift;

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

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

    return $parent_organisation_id_map;
}

1;
