#!/usr/bin/perl

=begin -------------------------------------------------------------------------
generates NYCRIS data feed for new and revised diagnoses during previous 1 month
creates pipe-delimited rows, one row per request. SCP file transfer.
=cut ---------------------------------------------------------------------------

use strict;
use warnings;

my $JUST_TESTING = 0; # skips file transfer & file archive

BEGIN {
    use FindBin qw($Bin); # warn $Bin; exit;
    use lib (
        "$Bin/../../../lib",
        '/home/raj/perl5/lib/perl5',
    );
}

use IO::All;
use Config::Auto;
use Data::Dumper;
use LIMS::Local::ScriptHelpers;

#####################################
my $delimiter = q{|}; # pipe        # 
my $duration  = 1; # months ago     #
my $new_line_marker = '[NEW_LINE]'; #
#####################################

my $cfg_file = "$Bin/../lib/settings.cfg";
my $settings = Config::Auto::parse($cfg_file); # warn Dumper $settings;
    
# get tools from LIMS::Local::ScriptHelpers:
my $tools = LIMS::Local::ScriptHelpers->new();

my $sql_lib = $tools->sql_lib();
my $config  = $tools->config(); 
my $dbix    = $tools->dbix();

# get date last month - handles BST:
my $ref_date = $tools->date_subtract(months => $duration); # warn ref_date; exit;

my $data_filename = sprintf 'hmds_%s_%02d.txt',
    $ref_date->year, $ref_date->month; # warn $data_filename;

use constant TMP_DIR => '/tmp'; # where to create temp data file

my @data = ();

# go:
{
    # get request ID's for horizontal (1-to-1) & vertical (1-to-many) tables:
    my $request_ids = get_request_ids(); # warn Dumper $request_ids;
    
    # get request result 1-to-1 data:
    my $query = $sql_lib->retr('nycris_data');
    my $result = $dbix->query($query, @$request_ids);
    
    # get data maps of vertical tables - much faster than tying into main query:
    my $data_map = get_data_map($request_ids); # warn Dumper $data_map;

    while ( my $vars = $result->hash ) { # warn Dumper $vars; next;
        my $request_id = $vars->{request_id};
        # combine horizontal & vertical table data:
        my $row = combine_data($vars, $data_map->{$request_id});
		push @data, $row; # warn $row;
    } # warn Dumper \@data;
}

{ # create output file & push to remote server:
    my $file_path = join '/', TMP_DIR, $data_filename;

    my $contents = join "\n", @data; 
    $contents > io($file_path); # warn $contents;

    # transfer file:
    unless ($JUST_TESTING) {
        my %args = ( local_filename => $file_path );
        transfer_file(\%args);
    }
}

# combine horizontal & vertical table data & return formatted row:
sub combine_data {
    my ($vars, $supplimentary_data) = @_;
    
    { # add data-map fields:
        # result summaries:
        map $vars->{$_} = $supplimentary_data->{result_summary}->{$_},
            qw(immunocytochemistry flow_cytometry fish cytogenetics molecular);
        # reporter:
        map $vars->{$_} = $supplimentary_data->{reporter_data}->{$_},
            qw(reporter_name reporter_code);
        # specimen code(s) & decode(s):
        $vars->{specimen_code}
            = $supplimentary_data->{specimen}->{specimen_code};
        $vars->{specimen_decode}
            = $supplimentary_data->{specimen}->{description};
    }
    
    # tidy up (trim content & substitute new-line markers):
    map { $vars->{$_} = tidy($vars->{$_}) }
        grep $vars->{$_},
            qw(comment immunocytochemistry flow_cytometry fish cytogenetics
                gross_description molecular);
    
    $vars->{gender} ||= 'U';
    
    $vars->{hmds_org_code} = 'RR813';
    
    my @fields = qw(
        request_id
        labno
        request_date
		last_name
        first_name
        middle_name
		unit_number
		dob
		nhs_number
		gender
		referrer
		referrer_code
        location
		organisation_code
		specimen_code
		specimen_decode
        clinical_details
        gross_description
        specimen_quality
        comment
        diagnosis
        icdo3
        flow_cytometry
        immunohistochemistry
        molecular
        cytogenetics
        fish
        reporter_name
        reporter_code
		authorisation_date
        hmds_org_code
	);
    
    no warnings 'uninitialized'; # lots in $vars
    my $row = join $delimiter, map $vars->{$_}, @fields;
    # row needs to start with request ID - in case of unescaped new-line marker:
    die 'malformed row detected' unless $row =~ /\A\d{6,}$delimiter/; 
    return $row;
}

sub get_request_ids {
    my @request_ids = ();
    { # new diagnoses:
        my $sql = $sql_lib->retr('nycris_new_diagnosis_request_ids');
        my $request_ids = $dbix->query($sql, $duration, $duration)->flat;
        push @request_ids, @$request_ids; # warn Dumper $request_ids;
    }    
    { # get revised diagnoses:
        my $sql = $sql_lib->retr('nycris_revised_diagnosis_request_ids');
        my $request_ids = $dbix->query($sql, $duration, $duration)->flat;
        push @request_ids, @$request_ids; # warn Dumper $request_ids;
    }
    # combine new & revised diagnosis request ID's; eliminate duplicates:
    my %request_ids = map { $_ => 1 } @request_ids; # warn Dumper \%request_ids;
    # return unique ID's as arrayref:
    return [ keys %request_ids ];    
}

sub transfer_file { # only called if not $JUST_TESTING
    my $args = shift;
    
    my $cfg = $settings->{nycris_server};
    
    my %params = (
        local_filename => $args->{local_filename},
        server_addr    => $cfg->{remote_address},
        username       => $cfg->{username},
        password       => $cfg->{password},
    );

    # scp file (returns str on failure, undef on success):
    my $rtn = $tools->scp_file(\%params);

    my $script = $tools->script_filename;

    if ($rtn) {
        $tools->mail_admin({ script => $0, msg => $rtn });
        warn "$script - $rtn\n"; # dump to logs but don't die!!
    }
    else {
        archive_and_delete_file($args->{local_filename});
        
        my $month = $ref_date->month_name; # for cron log:
        print "$script reports data for $month succesfully uploaded\n";
    }
}

# archive data file (only called if not $JUST_TESTING):
sub archive_and_delete_file {
    my $src_file = shift; # warn $src_file;

	my $path_to_archive = $settings->{nycris_server}->{path_to_archive};

    # make sure it exists otherwise get error trying to tar non-existant file
    if (-e $src_file ) { # warn 'it exists';
        my $tar_file = sprintf '%s/%s.tar',
            $path_to_archive, $data_filename; # warn $tar_file;
	
		chdir TMP_DIR; # so we can use relative path for source directory

		system( sprintf 'tar -cf %s %s', $tar_file, $data_filename ); # using relative $data_file.* not full path
		system( sprintf "gzip $tar_file" ); # compress tar file
        # delete source file:
        io($src_file)->unlink;
    }
}

sub get_user_code {
    my $username = shift;
    
    return $settings->{national_codes}->{$username}
    || 'H9999998'; # OTHER HEALTH CARE PROFESSIONAL
}

sub get_data_map {
    my $request_ids = shift;
    
    my $map = {};
    
    { # request_specimens:
        my $sql = $sql_lib->retr('nycris_request_specimens'); # warn $sql;
        my $result = $dbix->query($sql, @$request_ids);
        
        while ( my $vars = $result->hash ) { # warn Dumper $vars;
            my $request_id = $vars->{request_id};
            
            my %data = (
                specimen_code => $vars->{code},
                description   => $vars->{description},
            ); # warn Dumper \%data;
            $map->{$request_id}->{specimen} = \%data;
        }  
    }
    { # reporter data:
        my $sql = q!select rh.request_id, u.username, u.first_name, u.last_name
            from request_history rh join users u on ( rh.user_id = u.id and
            action = 'reported' ) where request_id in (??)!;
            
        my $result = $dbix->query($sql, @$request_ids);

        while ( my $vars = $result->hash ) { # warn Dumper $vars;
            my $request_id = $vars->{request_id};
            
            my $reporter = join ' ',
                ucfirst $vars->{first_name},
                ucfirst $vars->{last_name};

            my $reporter_code = get_user_code($vars->{username});
            
            my %data = (
                reporter_name => $reporter,
                reporter_code => $reporter_code,
            );
            $map->{$request_id}->{reporter_data} = \%data;
        } # warn Dumper $map->{reporter_data};
    }
    { # result summaries:
        my $sql = $sql_lib->retr('nycris_result_summaries');
        my $result = $dbix->query($sql, @$request_ids);
        
        while ( my $vars = $result->hash ) { # warn Dumper $vars;
            my $request_id  = $vars->{request_id};
            my $section     = $vars->{section_name};
            my $result      = $vars->{results_summary};              
           
            $section =~ s/ /_/g; # make section suitable for use as hash key 
            $map->{$request_id}->{result_summary}->{lc $section} = $result;
        } # warn Dumper $map->{result_summary}; 
    }
    
    return $map;
}

sub tidy {
    my $str = shift;
    
    # trim:
    $str =~ s/\A\s+//; # leading
    $str =~ s/\s+\Z//; # trailing
    
    $str =~ s/(\r\n)/$new_line_marker/g;

    return $str;
}
