package LIMS::Controller::Result;

use base 'LIMS::Base';

use Moose;
with (
	'LIMS::Controller::Roles::DataMap',
	'LIMS::Controller::Roles::DataFile',
	'LIMS::Controller::Roles::DataImport',
	'LIMS::Controller::Roles::FormData',
	'LIMS::Controller::Roles::RecordHandler',
	'LIMS::Controller::Roles::ResultHandler',
    );

__PACKAGE__->meta->make_immutable(inline_constructor => 0);

# can't use __PACKAGE__->authz->authz_runmodes() - crashes on non-logged-in user

#-------------------------------------------------------------------------------
sub default : StartRunmode {
    my $self = shift;

    # shouldn't be called here - redirect to /
    $self->redirect( $self->query->url );
}

#-------------------------------------------------------------------------------
sub load : Runmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);

	return $self->forbidden() unless $self->user_can('modify_results');
	
    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);

	my $request_data = $self->get_single_request_data($request_id);

	# return to home page if record locked (direct url arrival) & not authorized:
	if ( $request_data->{is_locked} && ! $self->user_can('modify_results') ) {
		return $self->redirect( $self->query->url . '/search/=/' . $request_id );
	}					   

	# send $data to process_results_data() for template
	# - adds 'all_results' & 'results_summary_opts' to $request_data:
	$self->process_raw_lab_test_data($request_data);
	
	{ # datafiles - load sub to save db lookup if not required:
		my $load_data_files = sub {
			my $data_files = $self->get_result_data_file($request_id);
			return @$data_files ? 1 : 0;
		};
		$self->tt_params( have_data_files => $load_data_files );
	}

	# get some data maps:
	my %data_map;
	{ # lab test data type map:
		my $lab_test_data_type_map = $self->lab_test_data_type_map;	
		$data_map{lab_test_data_type} = $lab_test_data_type_map;
	}
	{ # lab test result options map:
		my $lab_test_result_options = $self->lab_test_result_options_map;	
		$data_map{data_options} = $lab_test_result_options;
	}
	#{ # lab section notes map (moved to get_single_request_data):
		# my $lab_section_notes = $self->section_notes_map($request_id);
		# $data_map{lab_section_notes} = $lab_section_notes;
	#}
	$self->tt_params( data_map => \%data_map );	
	
	# $self->_debug_path('timer');
    return $self->render_view('result/default.tt', $request_data);
}

#-------------------------------------------------------------------------------
sub update : Runmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);
    
	return $self->forbidden() unless $self->user_can('modify_results');

    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);

    # required = 2 hidden fields so should never fail, so send to error mode:
    my $dfv = $self->check_rm('load', $self->validate('update_results') )
	|| return $self->error('required hidden field(s) not supplied to rm '
        . $self->get_current_runmode);

    my $data = $dfv->valid(); # $self->debug($data); 
    
    # put request_id into $data:
    $data->{_request_id} = $request_id; # $self->debug($data); 
    
	# get (optional) config file for auto_reportable requests:
	if ( my $cfg = $self->get_yaml_file('auto_reportable') ) {
		$data->{auto_reportable_config} = $cfg;
	}
	
    my $rtn = $self->model('Result')->update_lab_test_results($data);

	if ($rtn) {
		return $self->error($rtn);
	}
	else {
		$self->flash( info => $self->messages('action')->{edit_success});
		return $self->redirect( $self->query->url . '/search/=/' . $request_id );
	}
}

#-------------------------------------------------------------------------------
sub preview_datafile : Runmode {
	my $self = shift; $self->_debug_path($self->get_current_runmode);
	
    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);
	
	# get data file from requests' images dir - in arrayref format:
	my $data_file = $self->get_result_data_file($request_id);

	# check there's only 1 data file:
	if (! @$data_file || @$data_file > 1) {
		my $msg = @$data_file ? 'xs_data_files' : 'no_data_file';
		$self->flash( error => $self->messages('results')->{$msg} );
		$self->redirect( $self->query->url . '/image/=/' . $request_id );
	}
	
	# extract data from datafile into arrayref:
	my $data = $self->get_datafile_contents($data_file->[0]); # deref @data_file	
	unless (@$data) {
		$self->flash( error => $self->messages('results')->{empty_data} );
		$self->redirect( $self->query->url . '/image/=/' . $request_id );
	}
	
	$self->tt_params(
		request_id => $request_id,
		data 	   => $data,
		file 	   => @$data_file, # deref single arrayref
	);
	
	return $self->tt_process;
}

#-------------------------------------------------------------------------------
sub import_datafile : Runmode {
	my $self = shift; $self->_debug_path($self->get_current_runmode);

    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);

	my $filename = $self->query->param('datafile')
	|| return $self->error('no filename passed to ' . $self->get_current_runmode);
	
	# extract contents of datafile to object accessor:
	$self->parse_data_file($filename);
	
	# check internal datafile refs (filename & patient ids) match
	$self->_check_datafile_integrity()
	|| return $self->redirect( $self->query->url . '/image/=/' . $request_id );
	
	# get flow data (as str) from datafile, or redirect (flash set in sub):
	my $flow_data_result = $self->_get_flow_data_result()
	|| return $self->redirect( $self->query->url . '/image/=/' . $request_id );
	
	# create hash to hold data for db update:
	my %results_data = ( request_id => $request_id ); 
	
	# if lab_test sign_off required, need to get additional params:
	if ( my $lab_test_data = $self->get_lab_test_data() ) { # warn $lab_test;
		# get 'acquired_by' user:
		my $acquired_by = $self->get_analysis_user(); # no need to check, it's next
		$lab_test_data->{acquired_by} = $acquired_by;

		# get 'user_id' value:
		my $user_id = $self->get_analysis_user_id($acquired_by)
		|| $self->_set_flash_msg('no_userid'); # set flash msg on failure

		# get 'date_acquired' value:			
		my $date_acquired = $self->get_date_acquired() 
		|| $self->_set_flash_msg('empty_acquired'); # set flash msg on failure
		
		# check both params, or redirect (flash msg already set on either failure):
		unless ($date_acquired && $user_id) {
			return $self->redirect( $self->query->url . '/image/=/' . $request_id );
		}

		$lab_test_data->{date_acquired} = $date_acquired;
		$lab_test_data->{acquisition_userid} = $user_id;

		$results_data{lab_test_data} = $lab_test_data;
	}
	
	{ # request_report data:
		my $report_data = $self->get_report_params(); # $self->debug($data);
		$results_data{report_data} = $report_data;
	}
	{ # request_result_summaries data:
		my $lab_section = $self->get_lab_section;
		my %result_summary_data = (
			lab_section     => $lab_section,
			results_summary => $flow_data_result,
		); # $self->debug(\%result_summaries_data);
		$results_data{result_summary_data} = \%result_summary_data;
	}
	
    my $rtn = $self->model('Result')->import_results(\%results_data);  

	if ($rtn) {
		return $self->error($rtn);
	}
	else {
		$self->flash( info => $self->messages('action')->{edit_success});
		return $self->redirect( $self->query->url . '/report/=/' . $request_id );
	}
}

#-------------------------------------------------------------------------------
sub notes : Runmode {	
	my $self = shift; $self->_debug_path($self->get_current_runmode);
	
    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);
	
	# don't need to validate, either have content or not:
	my $profile = $self->validate('general_notes');
	
    my $data = $self->get_data_from_dfv($profile); # $self->debug($data);
	$data->{_request_id} = $request_id;
	
	my $rtn = $self->model('Result')->general_notes($data);
	
	if ($rtn) {
		return $self->error($rtn);
	}
	else {
		$self->flash( info => $self->messages('action')->{edit_success});
		return $self->redirect( $self->query->url . '/result/=/' . $request_id );
	}
}

#-------------------------------------------------------------------------------
sub data_entry : Runmode {
	my $self = shift; $self->_debug_path($self->get_current_runmode);
	
	return $self->forbidden() unless $self->user_can('modify_results');

    my $request_id = $self->param('id')
    || return $self->error('no id passed to ' . $self->get_current_runmode);
	
	my $lab_test_data = $self->_get_test_results_data_map;
	
	my %data = (
		lab_test_data => $lab_test_data,
		request_id    => $request_id,
	);
	
	my $rtn = $self->model('Result')->update_request_lab_test_results(\%data);
	
	if ($rtn) {
		return $self->error($rtn);
	}
	else {
		$self->flash( info => $self->messages('results')->{update_success} );
		return $self->redirect( $self->query->url . '/result/=/'.$request_id);
	}
}

#-------------------------------------------------------------------------------
# check internal datafile refs (filename & patient ids) match
# sets $self->param_mismatch() arrayref on error:
sub _check_datafile_integrity {
	my $self = shift;
	
	# check_datafile_integrity() returns names of any errors:
	if ( my $error_names = $self->check_datafile_integrity() ) {
		my @errs = map $self->messages('results')->{$_}, @$error_names;
		$self->flash( error => join '; ', @errs );
		return 0;
	}
	return 1;
}

#-------------------------------------------------------------------------------
# data passed from form fields as lab_test_id_(\d)
sub _get_test_results_data_map {
	my $self = shift;
	
	my $params = $self->query->Vars; # can't use $dfv->valid for dynamic params

	my %map;
	while ( my($param, $value) = each %$params ) { # warn $param;
		# look for param lab_test_id_(\d):
		my ($lab_test_id) = $param =~ /lab_test_id_(\d+)/; # don't do '|| next' here
		next unless $lab_test_id; # warn $lab_test_id; warn $value;
		
		$map{$lab_test_id} = $value;
	}  $self->debug(\%map);
	
	return \%map;
}

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

	if ( my $flow_data_result = $self->get_flow_data_result() ) {
		return $flow_data_result;
	}
	else {
		$self->flash( error => $self->messages('results')->{empty_result} );
		return 0;
	}
}

#-------------------------------------------------------------------------------
sub _set_flash_msg {
	my $self = shift;
	my $msg  = shift;
	
	$self->flash( error => $self->messages('results')->{$msg} );
	return 0; # IMPORTANT - return value is captured by caller
}

1;
