package LIMS::Model::Roles::ScreenUpdate;

use Moose::Role;
with (
    'LIMS::Model::Roles::LabTestUpdate',
);

use Data::Dumper;

#-------------------------------------------------------------------------------
sub do_auto_screen {
    my $self = shift;

    # get hashref or required data (keys = lab_tests, presentation & trial_name):
    my $auto_screen_data = $self->_get_auto_screen_data(); # warn Dumper $auto_screen_data;
	return 0 unless %$auto_screen_data; # warn Dumper $auto_screen_data;

	my $data = $self->form_data; # warn Dumper $data;
    
    my $request_id = $data->{_request_id};
    my $user_id    = $self->user_profile->{id};
    
    my $screen_id = 0; # set below, used in do_lab_test_details() block

    # linked_lab_tests_map:
    my $linked_tests_map = $self->linked_lab_tests; # M::R::LabTestUpdate

    # first save presentation info:
    if ( my $presentation = $auto_screen_data->{presentation} ) {
        my $description = $presentation->{description};
        my $category    = $presentation->{category};
        
        my @args = (
            query => [
                description => $description,
                name        => $category,
            ],
            require_objects => 'category',
        ); # warn Dumper \@args;
        
        my $screen = LIMS::DB::Screen::Manager->get_screens(@args)->[0];
        $screen_id = $screen->id; # used in do_lab_test_details() block
        
        my %data = (
            request_id => $request_id,
            screen_id  => $screen->id,
        ); # warn Dumper \%data;
        LIMS::DB::RequestInitialScreen->new(%data)->save;
        
        # update request status:
        $self->update_request_status('screened', $request_id);
    }
    # if trial name required:
    if ( my $trial_name = $auto_screen_data->{trial_name} ) {
        my $trial_id
            = LIMS::DB::ClinicalTrial->new( trial_name => $trial_name)->load->id;
        my %data = (
            request_id => $request_id,
            trial_id   => $trial_id,
        );
        LIMS::DB::RequestTrial->new(%data)->load_or_insert; # in case already set in form
    }
    # if lab tests generated:
    if ( my $lab_tests = $auto_screen_data->{lab_tests} ) { # warn Dumper $lab_tests;
        for my $lab_test_id ( keys %$lab_tests ) { # vals are just counts, don't need
            my %data = (
                _request_id => $request_id,
                lab_test_id => $lab_test_id,
            ); # warn Dumper \%data;
            $self->do_new_lab_test(\%data);
            
            # add linked tests if configured:
            if ( my $ref = $linked_tests_map->{$lab_test_id} ) { # arrayref of linked test.id's
                $self->do_linked_lab_tests($ref, $request_id);
            }

            # lab test details:
            $data{screen_id} = $screen_id; # required by do_lab_test_details()
            $self->do_lab_test_details(\%data);
        }
    }

    $self->add_to_actions('screened');
}

#-------------------------------------------------------------------------------
sub additional_options {
    my ($self, $action) = @_; # warn Dumper $action;
    
    my $data = $self->form_data; # warn Dumper $data;
    
    my $cfg = $data->{additional_options_config}->{$action} || return 0;

    # get 'screened_as' description:
    my $presentation = LIMS::DB::Screen->new(id => $data->{screen_id})->load;
    my $screened_as = $presentation->description; # warn $screened_as;
    
    # cycle through options to see if screened_as term triggers option flag: 
    while ( my($option, $d) = each %$cfg ) { # warn Dumper [$option, $d];
        # check option (eg DoI) exists and is active:
        my $o = LIMS::DB::AdditionalOption->new(option_name => $option)
            ->load(speculative => 1); # warn Dumper $o->as_tree;
        return 0 unless $o && $o->is_active eq 'yes';

        # set option flag if screened_as term matches one of list:
        if ( grep lc($screened_as) eq lc($_), @$d ) {
            my %data = (
                request_id => $data->{_request_id},
                option_id  => $o->id,
            ); # warn Dumper \%data;
            LIMS::DB::RequestOption->new(%data)->load_or_insert;
        }        
    }
}

#-------------------------------------------------------------------------------
sub _get_auto_screen_data {
    my $self = shift;

    my $form_data = $self->form_data; # warn Dumper $form_data;
    
    my $cfg = $form_data->{auto_screen_config}; # warn Dumper $cfg; # AoH

    my $request_id = $form_data->{_request_id};

    # get full request data (request, patient_case & patient tables) as a flat
    # data stucture, in case we need to find any mandatory field (eg nhs_number):
    my $request_data = do {
        my $r = LIMS::DB::Request->new(id => $request_id)
            ->load(with => 'patient_case.patient')->as_tree;
        LIMS::Local::Utils::traverse_hash($r); # uses Data::Traverse to flatten $r
    }; # warn Dumper $request_data;
    
    my %data = ();

	# method for determining if any previous diagnosis matches supplied icdo3:
	my $has_previous_diagnosis_icdo3 = sub {
		my $icdo3 = shift;
		
		my @args = (
			require_objects => [
				'request_report.diagnosis', 'patient_case.patient',
			],
			query => [
				'request_report.diagnosis.icdo3' => $icdo3,
				'patient_case.patient_id' => $form_data->{patient_id},
			],
		); 
		LIMS::DB::Request::Manager->get_requests_count(@args);
	};
	# method for determining if required specimen(s) exists in supplied specimens:
	my $has_specimen = sub { LIMS::Local::Utils::is_array_subset(@_) };

    RULE: for my $rule (@$cfg) { # $rule is hashref of presentation, match_criteria, etc
        # check any mandatory request/patient fields (eg nhs_number):
        if ( my $required_fields = $rule->{required_fields} ) {
            for my $field(@$required_fields) { # warn $field;
                # try to find required value in submitted form data, or else in
                # request/patient_case/patient data fields:
                next RULE unless $form_data->{$field} || $request_data->{$field};
            }
        }

        my $match_criteria = $rule->{match_criteria}; # HoH
        my $presentation   = $rule->{presentation}->{description};
        my $category       = $rule->{presentation}->{category};

        # $match_criteria has 1 or more hashref entries:
        while ( my ($field, $value) = each %$match_criteria ) { # warn Dumper [$field,$value];
            # a match on specimen takes priority - may also generate trial name:
            if ( $field eq 'specimen_type' ) {
                my $required_specimens = $value; # always arrayref
				my @supplied = split /\,\s?|\s+/, $form_data->{specimen};
					# warn Dumper [$required_specimens, \@supplied];
				# check required specimen(s) exists in supplied specimens, or skip to next:
				next RULE if ! &$has_specimen($required_specimens, \@supplied);
            }
            elsif ( $field eq 'trial_name' ) {
                # get trial id from form, or skip rule:
                my $trial_id = $form_data->{trial_id} || next RULE;
                my $trial # get trials.trial_name for trial id:
                    = LIMS::DB::ClinicalTrial->new(id => $trial_id)->load;
                # skip rule unless submitted trial is in config:
                next RULE unless $value eq $trial->trial_name;
            }
            elsif ( $field eq 'previous_diagnosis_icdo3' ) { # warn $value;
                # if previous_diagnosis ICDO3 supplied (eg used for CML's):
                next RULE unless &$has_previous_diagnosis_icdo3($value); # icdo3
            }
            else {
                next RULE; # no matches
            }
        }

        # add config presentation & trial_name fields to %data (unless already set
        # from previous cycle through RULES array (ie *1st one wins*), so ensure
		# rules not inconsistent:
        map { $data{$_} ||= $rule->{$_} }
			grep $rule->{$_}, qw(presentation trial_name);

        { # add lab tests:
            my @args = (
                with_objects => 'screen',
                query        => [ description => $presentation ],
            ); # warn Dumper \@args;
            my $o = LIMS::DB::ScreenLabTest::Manager->get_objects(@args);
                # warn Dumper $_->as_tree for @$o;
            $data{lab_tests}{$_->lab_test_id}++ for @$o;
        }
    } # warn Dumper \%data;
    return \%data;
}

1;