package LIMS::Model::Screen;

use Moose;
extends 'LIMS::Model::Base';
with (
    'LIMS::Model::Roles::SessionData', # provides $self->user_profile
    'LIMS::Model::Roles::ScreenUpdate',
    'LIMS::Model::Roles::RequestUpdate',
	'LIMS::Model::Roles::ReportUpdate',
);

has form_data => ( is => 'rw', isa => 'HashRef', default => sub { {} } );

use namespace::clean -except => 'meta';
__PACKAGE__->meta->make_immutable;

use Rose::DateTime::Util qw(:all);

#-------------------------------------------------------------------------------
sub get_screen {
    my $self = shift;
    my $id   = shift;

    my $screen = LIMS::DB::Screen->new(id => $id)->load;

    return $screen;
}

#-------------------------------------------------------------------------------
sub get_all_screens {
    my $self   = shift;
    my $params = shift || {};

    # get all screen rows as arrayref:
    my $data = LIMS::DB::Screen::Manager->get_screens(%$params); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub get_assigned_tests {
    my $self      = shift;
    my $screen_id = shift;

    my %args = (
        query => [ 'screen_id' => $screen_id ],
    );

    # get all screen_lab_test rows for submitted $screen_id as arrayref:
    my $data = LIMS::DB::ScreenLabTest::Manager->get_screen_lab_tests(%args); # DEBUG $data;

    return $data;
}

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

    my %args = (
        require_objects => [ qw(screen lab_test) ],
    );

    my $data = LIMS::DB::ScreenLabTest::Manager->get_screen_lab_tests_iterator(%args);

    return $data;
}

#-------------------------------------------------------------------------------
sub get_assigned_test_details {
    my $self      = shift;
    my $screen_id = shift;

    my %args = (
        query => [ 'screen_id' => $screen_id ],
    );

    # get all screen_lab_test rows for submitted $screen_id as arrayref:
    my $data = LIMS::DB::ScreenLabTestDetail::Manager->get_screen_lab_test_details(%args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub update_lab_tests {
    my $self = shift;
    my $args = shift; # DEBUG $args;

    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached;

    my $screen_id = $args->{screen_id}
    || die 'No screen_id value passed to update_lab_tests()';

    # update lab_tests & section_details in a single transaction:
    my $update = sub {
        # first clear existing entries for this screen_id:
        LIMS::DB::ScreenLabTest::Manager->delete_screen_lab_tests(
            where => [ screen_id => $screen_id ],
        );
        # insert any new tests:
        my $test_ids = $args->{test_ids}; # arrayref
        foreach my $id ( @{ $test_ids } ) {
            LIMS::DB::ScreenLabTest->new(
                lab_test_id => $id,
                screen_id   => $screen_id,
            )->save;
        }
        # first clear existing entries for this screen_id:
        LIMS::DB::ScreenLabTestDetail::Manager->delete_screen_lab_test_details(
            where => [ screen_id => $screen_id ]
        );
        # insert any new section details:
        my $lab_section_details = $args->{details}; # $self->debug($section_notes)
        while ( my ($lab_section_id, $value) = each %$lab_section_details ) {
            next unless $value; # skip empty fields
            LIMS::DB::ScreenLabTestDetail->new(
                lab_section_id => $lab_section_id,
                test_details   => $value,
                screen_id      => $screen_id,
            )->save;
        }
    };
    
    my $ok = $db->do_transaction($update);

    # don't need return value unless error:
    return $ok ? 0 : 'update_lab_tests() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub update_screening_terms {
    my $self = shift;
    my $data = shift || return; # DEBUG $data; # shouldn't receive empty data from controller

    my %args = ( class => 'Screen', data  => $data );
    
    return $self->update_object(\%args);
}

#-------------------------------------------------------------------------------
sub do_initial_screen {
    my $self = shift;
    my $data = shift; # $self->debug($data);

    # put $dfv->valid into $self:
    $self->form_data($data);    

    # update request_lab_test, request_audit & report tables in a single transaction:
    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached; 
    
    my $initial_screen = sub {        
        # set request status first:
        $self->update_request_status('screened', $data->{_request_id});

		# auto-report (if required) before lab-tests (would prevent auto_reporting): 
		if ( $data->{auto_reportable_config} ) {
			$self->do_auto_report(); 
		}
        # additional options (eg screened as HIV sets DoI flag):
		if ( $data->{additional_options_config} ) {
			$self->additional_options('screen'); 
		}

        # lab tests:
        $self->do_lab_tests($data);
        
        # lab test details:
        $self->do_lab_test_details($data);
        
        LIMS::DB::RequestInitialScreen->new(
            request_id => $data->{_request_id},
            screen_id  => $data->{screen_id},
        )->save;

        LIMS::DB::RequestHistory->new(
            request_id  => $data->{_request_id},
            user_id     => $self->user_profile->{id},
            action      => 'screened'
        )->save;

        LIMS::DB::RequestAudit->new(
            request_id => $data->{_request_id},
            audit_request_option_id => $data->{option_id},
        )->save;
    };

    # do_transaction() returns true if succeeds; sets $db->error on failure:
#$self->set_rose_debug(1);
    my $ok = $db->do_transaction($initial_screen);
#$self->set_rose_debug(0);

    return $ok ? 0 : 'do_initial_screen() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub update_screen {
    my $self = shift;
    my $args = shift; # DEBUG $args;

    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached;
    
    my $screen_id  = $args->{screen_id}
    || die 'No screen_id value passed to update_screen()';
    my $request_id = $args->{request_id}
    || die 'No request_id value passed to update_screen()';
 
    my %args = (
        request_id => $request_id,
    );
    
    # get original screen object:
    my $original_screen = $self->get_initial_screen($request_id)->screen;

    # check it's changed:
    return unless $screen_id != $original_screen->id;

    my $action = 'updated initial screen from ' . $original_screen->description;
    
    my $update = sub {
        { # RequestInitialScreen:
            my $o = LIMS::DB::RequestInitialScreen->new(%args)->load;
            $o->screen_id($screen_id);
            $o->save(changes_only => 1);
        }
        { # RequestHistory:
            $args{user_id} = $self->user_profile->{id};
            $args{action}  = $action;
            
            LIMS::DB::RequestHistory->new(%args)->save;
        }
        { # update lab tests & lab test details:
            my %data = ( # requires screen_id & request_id:
                screen_id   => $screen_id,
                _request_id => $request_id, # methods require underscored var
            );

            $self->do_lab_tests(\%data);        
            $self->do_lab_test_details(\%data);
        }
    };
    
    my $ok = $db->do_transaction($update);

    # don't need return value unless error:
    return $ok ? 0 : 'update_screen() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub get_initial_screen {
    my $self       = shift;
    my $request_id = shift;

    # load speculative => 1 in case not yet screened:
    my $screen = LIMS::DB::RequestInitialScreen->new(request_id => $request_id)
        ->load(speculative => 1, with => 'screen' );

    return $screen;
}

1;
