package LIMS::Model::PrintRun;

use Moose;
extends 'LIMS::Model::Base';
with 'LIMS::Model::Roles::Query';
use namespace::clean -except => 'meta';
__PACKAGE__->meta->make_immutable;

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

#-------------------------------------------------------------------------------
sub get_print_run_size {
	my ($self, $h) = @_; # warn Dumper $h; # hashref of start & end datetimes + optional cfg

	# just forward to get_print_run_request_ids and return count:
	my $ids = $self->get_print_run_request_ids($h);
	my $i = scalar @$ids; # warn $i;
	return $i;
}

#-------------------------------------------------------------------------------
sub get_print_run_request_ids {
	my ($self, $h) = @_; # warn Dumper $h; # hashref of start & end datetimes + optional cfg

    my ($start_date, $end_date, $type) = @{$h}{ qw(start_date end_date type) };

    my %print_run_args = (
        start => $start_date,
        type  => $type,
        end   => $end_date,
    );
    if ( defined $h->{offset} ) { # can be zero
		$print_run_args{$_} = $h->{$_} for qw(offset limit);
	}
    my @request_ids = do {
		my ($sql, @bind) = $self->get_query_params(\%print_run_args);
			# $self->lims_dbix->dump_query($sql, @bind);
		$self->lims_dbix->query($sql, @bind)->flat; # warn Dumper $results;
	};
	# get copy-to's:
	my @copy_tos = do {
		my ($sql, @bind) = $self->get_cc_request_ids(\%print_run_args);
			# $self->lims_dbix->dump_query($sql, @bind);
		$self->lims_dbix->query($sql, @bind)->flat;
	}; # warn Dumper \@copy_tos;
	# combine @request_ids & @copy_tos as list of unique request ids:
	my %h = map { $_ => undef } ( @request_ids, @copy_tos ); # warn Dumper \%h;
	my @all_request_ids = keys %h;
    # return separate request_ids & copy_tos if required, otherwise all_request_ids:
	return wantarray
        ? ( \@request_ids, \@copy_tos )
        : \@all_request_ids;
}

#-------------------------------------------------------------------------------
sub get_cc_request_ids {
	my $self = shift;
	my $args = shift;

    my $start = $args->{start}; # DT object
    my $type  = $args->{type};
    my $end   = $args->{end};   # DT object

	my @rels = (
        # table|alias                  	# FK-PK relationship
        'requests|r' 				,	'rr.request_id = r.id'       ,
        'request_report_view|rr'    ,   'ro.request_id = r.id'       ,
		'request_option|ro'		    ,   'ro.option_id  = ao.id'	     ,
		'additional_options|ao'     ,   'r.status_option_id = so.id' ,
        'status_options|so'         ,
    );
	# set status level required for printing reports:
	my $status = $self->does_authorisation() ? 'authorised'	: 'reported'; # warn $status;
    my %query = (
        'so.description' => { -in => [ $status, 'complete' ] },
		'ao.option_name' => 'copy_to',
		'DATE(rr.updated_at)' => ( $start->ymd eq $end->ymd )
			? $start->ymd # or $end->ymd, both same date
			: { -between => [ $start->ymd, $end->ymd ] },
	);
	my %h = (
        cols  => 'r.id',
        joins => \@rels,
        where => \%query,
    );
    if ( $type eq 'clinical-trial' ) { # include request_trial/clinical_trials:
        push @rels, ( 'rt.request_id=r.id' => 'request_trial|rt' ,
                      'rt.trial_id=ct.id'  => 'clinical_trials|ct' );
    }
    elsif ( $type eq 'non-trial' ) { # left join request_trial to exclude:
        push @rels, ( '=>rt.request_id=r.id' => 'request_trial|rt' );
        $query{'rt.request_id'} = undef;
    }
    my ($sql, @bind) = $self->sqla_query(\%h);
	    # $self->lims_dbix->dump_query($sql, @bind);
    return ($sql, @bind);
}

#-------------------------------------------------------------------------------
sub get_query_params { # returns $sql & @bind params - shared with print_run.pl
    my $self = shift; # warn ref $self;
    my $args = shift; # trial or non-trial cases, start & end dates

    my $start = $args->{start}; # DT object
    my $type  = $args->{type};  # trial or non-trial
    my $end   = $args->{end};   # DT object

    my $settings = $self->lims_cfg->{settings}; # warn Dumper $settings;
    my $tbl_rels = get_tbl_rels($type);

	# set status level required for printing reports:
	my $status = $self->does_authorisation() ? 'authorised'	: 'reported'; # warn $status;

    my %query = ( 'so.description' => { -in => [ $status, 'complete' ] } );
	$query{'DATE(rr.updated_at)'} = ( $start->ymd eq $end->ymd )
		? $start->ymd # or $end->ymd, both same date
	    : { -between => [ $start->ymd, $end->ymd ] }; # warn Dumper \%query;

    my @sort_by;
    if ( $type eq 'clinical-trial' ) { # don't exclude any locations
        @sort_by = qw(ct.trial_name r.year r.request_number);
    }
    elsif ( $type eq 'non-trial' ) {
        if ( $settings->{print_run_skip_trials} ) { # skip trial cases if configured
            $query{'rt.request_id'} = undef;
        }
=begin # skip_paper_reports.yml - replaced by get_email_report_details() using email_contacts table:
        if ( my $cfg = $self->get_yaml_file('skip_paper_reports') ) { # p $cfg;
            if ( my $org_codes = $cfg->{organisation} ) { # p $org_codes;
                $query{'po.parent_code'} = { -not_in => $org_codes };
            }
            if ( my $hospitals = $cfg->{hospital} ) { # p $hospitals;
               $query{'rs.organisation_code'} = { -not_in => $hospitals };
            }
            if ( my $departments = $cfg->{department} ) { # p $departments;
                for (@$departments) { # eg RWA/823
                    my ($location, $department) = split '/'; # p $location; p $department;
                    # 5-digits (hospital) or 3-digits (organisation) code:
                    my $site_type = length $location > 3
                        ? 'rs.organisation_code'
                        : 'po.parent_code';
                    my %h = (
                        $site_type => $location,
                        'rd.hospital_department_code' => $department,
                    );
                    push @{ $query{-not_bool} }, \%h;
                }
            }
        }
=cut
        { # exclude any locations/departments configured for email (email_contacts table):
			my $ref = $self->get_email_report_details; # warn Dumper $ref; # hashref
			my %all_depts; # set in 1st 2 blocks below, used for skipping location departments

			# organisations - all departments:
			if ( my $org_codes = $ref->{organisation_all} ) { # aref
				my $parent_ids = $all_depts{parent} = $org_codes;
				$query{'rs.parent_organisation_id'} = { -not_in => $parent_ids };
			}
			# hospitals - all departments:
			if ( my $org_codes = $ref->{hospital_all} ) { # aref
				my $hospital_ids = $all_depts{hospital} = $org_codes;
				$query{'rs.id'} = { -not_in => $hospital_ids };
			}
			# specific organisation/department combinations:
			if ( my $organisation = $ref->{organisation_dept} ) { # HoA
				while ( my($parent_id, $dept_ref) = each %$organisation ) {
					# skip if already in organisation/all-departments list:
					next if grep $parent_id == $_, @{ $all_depts{parent} };

					push @{ $query{-not_bool} }, {
						'rs.parent_organisation_id' => $parent_id,
						'rd.hospital_department_code' => $_,
					} for @$dept_ref;
				}
			}
			# specific hospital/department combinations:
			if ( my $hospital = $ref->{hospital_dept} ) {
				while ( my($source_id, $dept_ref) = each %$hospital ) {
					# skip if already in hospital/all-departments list:
					next if grep $source_id == $_, @{ $all_depts{hospital} };

					push @{ $query{-not_bool} }, {
						'rs.id' => $source_id,
						'rd.hospital_department_code' => $_,
					} for @$dept_ref;
				}
			}
		} # warn Dumper \%query;
        @sort_by = qw(ref.name r.year r.request_number);
    }
    my %sqla_args = (
        cols  => 'r.id',
        joins => $tbl_rels,
        where => \%query,
        order_by => \@sort_by,
    );
    if ( defined $args->{offset} ) { # can be zero
		$sqla_args{$_} = $args->{$_} for qw(offset limit);
	} # warn Dumper \%sqla_args;
    my ($sql, @bind) = $self->sqla_query(\%sqla_args);
		# $self->lims_dbix->dump_query($sql, @bind);
    return ($sql, @bind);
}

sub get_tbl_rels {
    my $type = shift; # trial or non-trial cases

    my @rels = (
        # table|alias                  	# FK-PK relationship
        'requests|r' 				,	'r.referrer_department_id = rd.id'  ,
		'referrer_department|rd'	,	'rd.referrer_id = ref.id'		    ,
		'referrers|ref'				,	'r.patient_case_id = pc.id'         ,
		'patient_case|pc'			,	'rr.request_id = r.id'              ,
        'request_report_view|rr'    ,   'r.status_option_id = so.id'        ,
        'status_options|so'         ,   'pc.referral_source_id = rs.id'     ,
		'referral_sources|rs'       ,
    );
    if ( $type eq 'clinical-trial' ) { # include request_trial/clinical_trials:
        push @rels, ( 'rt.request_id=r.id' => 'request_trial|rt' ,
                      'rt.trial_id=ct.id'  => 'clinical_trials|ct' );
    }
    elsif ( $type eq 'non-trial' ) { # left join request_trial to exclude:
        push @rels, ( '=>rt.request_id=r.id' => 'request_trial|rt' );
    }
    return \@rels;
}

sub get_email_report_details {
	my $self = shift;

	my @contacts = do {
		my @cols = qw(
			ec.display_name
			ec.referral_source_id|source_id
			rs.parent_organisation_id|parent_id
			ec.scope
			ec.department_id
			ec.status
		);
		my @rels = (
			'email_contacts|ec' => 'ec.referral_source_id=rs.id',
			'referral_sources|rs'
		);
		my %args = (
			cols  => \@cols,
			joins => \@rels,
			where => {
				'ec.is_active'    => 'yes',
				'ec.type'         => 'report',
				'ec.display_name' => { -not_rlike => 'CWT$' }, # cancer waiting times
			},
		); # p @args;
        my ($sql, @bind) = $self->sqla_query(\%args); # warn $sql; warn Dumper \@bind;
			# $self->lims_dbix->dump_query($sql, @bind);
		$self->lims_dbix->query($sql, @bind)->hashes;
	};

	my @contact_cols = qw( scope parent_id department_id source_id status );
	my %h; # HoH to provide unique location/department entries if multiple contact addresses
	for my $contact (@contacts) { # warn Dumper $contact; next;
		my ($scope, $parent_id, $department_id, $source_id, $want_status)
			= @{$contact}{@contact_cols};
			# warn Dumper [$scope, $parent_id, $department_id, $source_id, $want_status];
		if ( $scope eq 'organisation' ) {
			$department_id
				? $h{organisation_dept}{$parent_id}{$department_id}++ # organisation/department
				: $h{organisation_all}{$parent_id}++ # entire organisation
		}
		else { # scope = 'hospital'
			$department_id
				? $h{hospital_dept}{$source_id}{$department_id}++ # hospital/department
				: $h{hospital_all}{$source_id}++ # entire hospital
		}
	} # warn Dumper \%h;

	my @all_organisation_ids = keys %{ $h{organisation_all} };
	my @all_hospital_ids     = keys %{ $h{hospital_all} };

    my $organisation_dept_ref = do { # transform $h{organisation_dept} internal hashrefs to arrayrefs:
        my $o = $h{organisation_dept};
		+{ map +($_ => [ keys %{$o->{$_}} ]), keys %$o }
    }; # warn Dumper $organisation_dept_ref;
	my $hospital_dept_ref = do { # transform $h{hospital_dept} internal hashrefs to arrayrefs:
		my $o = $h{hospital_dept};
		+{ map +($_ => [ keys %{$o->{$_}} ]), keys %$o }
	}; # warn Dumper $hospital_dept_ref;

	my %data = (
		organisation_dept => $organisation_dept_ref, # HoA
		organisation_all  => \@all_organisation_ids, # aref
		hospital_dept     => $hospital_dept_ref,     # HoA
		hospital_all      => \@all_hospital_ids,     # aref
	); # warn Dumper \%data;
    return \%data;

	# flatten organisation and hospital location_id hashrefs to array(ref) so
	# $data just contains lists of arrayrefs (generates same as above %data):
	my $data = LIMS::Local::Utils::convert_hashrefs_to_arrayrefs(\%h); # warn Dumper $data;
	# return $data
}

1;
