package LIMS::Model::Audit;

use Moose;
with (
    'LIMS::Model::Roles::DBIxSimple', # get_sql_with_constraint()
   	'LIMS::Model::Roles::QueryFormatter', # get_relationships()
);
extends 'LIMS::Model::Base';
use namespace::clean -except => 'meta';
__PACKAGE__->meta->make_immutable;

use Data::Dumper;
use LIMS::Local::Utils;

#-------------------------------------------------------------------------------
sub turnaround_times {
    my $self = shift;
    my $vars = shift || {}; # will be empty on 1st call
    
    $self->params($vars);
    
	my $dbix = $self->lims_dbix;

    # for calculation of delta workings days:
    my $delta_days = sub { LIMS::Local::Utils::delta_business_days(@_) };

    my %h = (
        pnh => [ 'PNH', 'PNH (PB)' ],
        hiv => [ 'HIV', 'HIV monitoring' ],
        cmp => [ 'Community monitoring', 'Outreach' ],
        mol => [
            qw/CMPD Chimerism Molec/, 'CML follow-up', 'Follow-up CML (PB)'
        ],
    );

    my $sql_lib = $self->_get_sql_lib_entry_for_selection($vars->{selection});
 	my @attr = ($sql_lib, 'h1.time'); # h1.action = 'reported'
 	my $sql = $self->get_sql_with_constraint(@attr); # warn $sql; return 1;
 	my $query = $dbix->query( $sql ); # warn Dumper $query;

    my %data = (); # controller needs array for sorting, but easier to create hashref for 'count'
    while ( my $vars = $query->hash ) {
        my $sample = $vars->{sample_code}; # warn $sample;
        my $screen = $vars->{description};
            
        # need to split PB's according to request:
        if ($sample eq 'PB') {
               if ( grep $screen eq $_, @{ $h{hiv} } ) { $sample = 'PB [HIV]' }
            elsif ( grep $screen eq $_, @{ $h{pnh} } ) { $sample = 'PB [PNH]' }
            elsif ( grep $screen eq $_, @{ $h{cmp} } ) { $sample = 'PB [CMP]' }                 
            elsif ( grep $screen =~ /^$_/, @{ $h{mol} } )
                { $sample = 'PB [Mol]' }
        } # warn $screen if $sample eq 'PB [PNH]';

        $data{$sample}{count}++; # increment specimen count
            
        # get registered, reported & authorised dates; using DATE_FORMAT in sql
        # MUCH faster than dt conversion afterwards:
        my ($registered, $reported, $authorised)
            = @{$vars}{ qw(registered reported authorised) };
    
        # calculate registered => reported & registered => auth'ed durations:
        my $delta = &$delta_days($registered, $reported);
        $data{$sample}{delta_report} += $delta;
            
        if ($authorised) { # if using authorisation stage
            my $delta = &$delta_days($registered, $authorised);
            $data{$sample}{delta_authorise} += $delta;
        }
    } # warn Dumper \%data;

    my $title = $self->constraint_title; # set in Role _set_search_constraints()

    return (\%data, $title); # return array format
}

#-------------------------------------------------------------------------------
sub nhs_number_supplied {
    my $self = shift;
    my $vars = shift || {}; # will be empty on 1st call
    
    $self->params($vars);
    
	my $dbix = $self->lims_dbix;
    my $dbh  = $self->lims_db->dbh; # warn Dumper $dbh;
    
    # exempted NHS number:
    if ( my $yaml = $self->get_yaml_file('nhs_number_exempt') ) { # warn Dumper $yaml;
        my $clinical_trials = $yaml->{clinical_trial};
        my $presentation    = $yaml->{presentation};
        
        my $screens = join ',', map $dbh->quote($_), @$presentation;
        my $trials  = join ',', map $dbh->quote($_), @$clinical_trials;
        
        $self->add_constraint( qq!s.description NOT IN ($screens)! );
        $self->add_constraint( qq!ct.trial_name NOT IN ($trials)!  );
    }
    
    my @attr = ('nhs_number_compliance', 'r.created_at');
    my $sql = $self->get_sql_with_constraint(@attr); # warn $sql;

    my $data  = $dbix->query( $sql )->hashes; # warn Dumper $data;
    my $title = $self->constraint_title; # set in Role _get_search_constraints()

    return ($data, $title); # return array format
}

#-------------------------------------------------------------------------------
sub teaching_cases {
    my $self = shift;
    my $days = shift; # default is 365
	
  	my $dbix = $self->lims_dbix;
    my $sql  = $self->sql_lib->retr('teaching_cases');
    
    my $data = $dbix->query($sql, $days)->hashes; # warn Dumper $data;
    return $data;
}

#-------------------------------------------------------------------------------
sub user_workload {
    my ($self, $args) = @_; # warn Dumper $args;
    
    my $action = $args->{action}; 
    my $days   = $args->{duration};
    my $date   = LIMS::Local::Utils::today->subtract(days => $days);
    
	my @tables = qw( sample_code request_history_user );	
	my $relationships = $self->get_relationships(\@tables);
 
    my @query = (
        action => $action,
        'request_history.time' => { ge => $date },        
    );
    
    my @args = (
        query => \@query,
        multi_many_ok => 1, # to silence warning
		require_objects => $relationships,
		sort_by => 'specimen.sample_code', # to prevent eg PB/BMAT & BMAT/PB
    );
    
    my $data = LIMS::DB::Request::Manager->get_requests(@args); # warn Dumper $data;
    return $data;
}

#-------------------------------------------------------------------------------
sub revised_diagnoses {
    my $self = shift;
    my $vars = shift || {}; # will be empty on 1st call (except sort_by)
    
    $self->params($vars);
    
	my $dbix = $self->lims_dbix;
    
    my $revisions;
    { # get revised diagnoses over duration:
        my @attrs = ('revised_diagnoses', 'dh.time', 'one_month'); # default to 1 month
        my $sql = $self->get_sql_with_constraint(@attrs); # warn $sql;    
        $revisions = $dbix->query($sql)->hashes; # warn Dumper $revisions;
    }
    
    my @request_ids = map $_->{id}, @$revisions; # before $revisions gets revised!!
    
    my %data = (
        request_ids => \@request_ids,
    );
    
    my $constraint = join ' AND ', $self->all_constraints; # set in Role::get_sql_with_constraint
    $constraint =~ s/\w+\.(\w+)/$1/; # remove alias - only have 1 table to search

    my $sql = qq!select count(*) from request_history where $constraint
        and action = 'reported'!; # warn $sql;
    
    { # get total reported over duration:
        my $total = $dbix->query($sql)->list;
        $data{total_reported} = $total;
    }
    # need to manipulate & extend $revisions data if either apply:
    if ( grep $vars->{sort_by} eq $_, qw(reporter reviser) ) {
        my $data = $self->_sort_revisions($revisions); # sort data
        $data{revisions} = $data;
        
        if ( my @usernames = keys %$data ) { # will be empty if no revisions
            my $users = LIMS::DB::User::Manager->get_users(
                query => [ username => \@usernames ]
            );
            { # add user_details to %data:
                my %user_details = map { $_->{username} => $_ } @$users;
                $data{user_details} = \%user_details;
            }
            # add user contstraint to $sql:
            $sql .= q! and user_id = ?!;
            { # need report count for each user:
                for (@$users) {
                    my $username = $_->username;
                    my $user_id  = $_->id;
                    
                    my $report_count = $dbix->query($sql, $user_id)->list;
                    $data{user_report_counts}{$username} = $report_count;
                }                
            }
        }        
    }
    else {
        $data{revisions} = $revisions;
    }
    
    my $title = $self->constraint_title; # set in Role _get_search_constraints()

    return (\%data, $title); # return array format
}

# ------------------------------------------------------------------------------
sub _sort_revisions {
    my $self = shift;
    my $data = shift; # warn Dumper $data;
    
    my $functionary = $self->params->{sort_by}; # reporter / reviser

    my %sorted;
    
    for my $event (@$data) { # warn Dumper $event;
        my $user = $event->{$functionary}; # ie reporter / reviser
        push @{ $sorted{$user} }, $event; # warn Dumper $user;
    }
    
    return \%sorted;    
}

#-------------------------------------------------------------------------------
sub _get_sql_lib_entry_for_selection { # remove this if not selecting by specimen, location, etc
    my ($self, $selection) = @_;
    
    my %t = (
        specimen => 'turnaround_times_specimen',
    );

    my $lib_entry_name = $t{$selection};
    
    # have to modify lib_entry_name if authorisation stage not in use:
    if (! $self->does_authorisation) {
        $lib_entry_name .= '_no_authorisation';
    }
    return $lib_entry_name;
}

1;
