package LIMS::Controller::Register;

use Moose;
BEGIN { extends 'LIMS::Base'; }
with (
    'LIMS::Controller::Roles::Misc',
);
__PACKAGE__->meta->make_immutable(inline_constructor => 0);

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

#-------------------------------------------------------------------------------
sub default : StartRunmode {
    my $self = shift; $self->_debug_path($self->get_current_runmode);
    my $errs = shift; # $self->stash( errs => $errs ); # for debugging in tmpl

    return $self->forbidden() unless $self->user_can('register');

    # TODO: js_validation doesn't work with 'require_some'
    $self->js_validation_profile('patient_search_data');

    return $self->tt_process($errs);
}

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

    return $self->forbidden() unless $self->user_can('register');

    my $id = $self->query->param('id') || return $self->forward('default');

    my $data = $self->model('Request')->get_request_form($id); # warn Dumper $data->as_tree;
    unless ($data) { # probably incorrectly hand-typed barcode string
        my $msg = $self->messages('registration')->{no_request_form_id};
        $self->flash( warning => $msg );
        return $self->redirect( $self->query->url . '/register' );
    }

    my $nhs_number = $data->nhs_number;
    my $name = sprintf '%s, %s', $data->last_name, $data->first_name;

    # put nhs_number & name into query params in format expected by patient_search():
    my %vars = ( patient_no => $nhs_number, name => $name ); # warn Dumper \%vars;
    $self->query->param( $_ => $vars{$_} ) for keys %vars;

    # get output from patient_search() but don't return view yet:
    my $template = $self->forward('patient_search'); # uses patient_no & name

    # add rest of vars from online request form & redo _extract_params_from_request()
    # in case patient not found in patient_search() - populates tt form with params:
    $vars{$_} = $data->$_ for qw(location_name location_id gender);
    $vars{$_} = $data->dob->$_ for qw(year month day);
    $vars{unit_number} = $data->patient_number;

    # if location requires external_ref (eg GeNEQ requests require NGIS id):
    if ( my $yaml = $self->get_yaml_file('required_external_ref') ) { # warn Dumper $yaml;
        my @location_names = keys %$yaml; # warn Dumper \@location_names;
        my $location_name  = $data->location_name; # warn $location_name;

        if ( $nhs_number && grep $location_name =~ $_, @location_names ) {
            my $key = $yaml->{$location_name}; # warn $key;
            my %h = (
                external_ref => $data->{$key}, # external ref = eg GeNEQ unique id
                nhs_number   => $data->nhs_number, # needed to ensure correct target
            );
            $self->session->param( required_external_ref => \%h );
        }
    } # warn Dumper \%vars;

    $self->_extract_params_from_request(\%vars); # $self->debug($vars);

    # update request_form.imported col:
    $self->model('Request')->request_form_import($id);

    # now return template processed in patient_search():
    return $template;
}

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

    return $self->forbidden() unless $self->user_can('register');

    my $dfv = $self->check_rm('default', $self->validate('patient_search_data') )
    || return $self->dfv_error_page;

    # need form params in scalar (hashref) context:
    my $form_params = $dfv->valid; # $self->debug('form_params:'); # $self->debug($form_params);

    # parse valid form params (extract last_name, first_name, dob, nhs_number, unit_number):
    my $vars = $self->_extract_params_from_request($form_params); # $self->debug($vars);

    # get search params as arrayref for query, or redisplay search page:
    my $search_param = $self->_generate_search_constraints($vars)
    || return $self->forward('default'); # $self->debug($search_param);

    # format search terms as a string for display in template:
    my $search_terms = $self->_convert_search_terms_for_display($vars);

    my %args_for_search = ( query => $search_param ); # $self->debug(\%args_for_search);

    my $patient_matches_count
        = $self->model('Patient')->patient_matches_count(\%args_for_search);
    # $self->debug([$patient_matches_count]);

    if (! $patient_matches_count ) {
        my $msg = sprintf $self->messages('registration')->{no_records_found},
            $search_terms;
        $self->flash( info => $msg );
        return $self->redirect( $self->query->url . '/patient' );
    }

    # override cfg.settings.entries_per_page for this search:
    $self->cfg('settings')->{entries_per_page} = 50;

    if ( $patient_matches_count > $self->cfg('settings')->{entries_per_page} ) {
        # invoke pager for template and add limit & offset params to \%args_for_search:
        $self->pager({ query => \%args_for_search, total => $patient_matches_count });
    }   #  $self->debug(\%args_for_search);

    # do search for previous patients:
    my $patient_cases
        = $self->model('PatientCase')->registration_search(\%args_for_search);

    my $results = $self->extract_patients_and_locations($patient_cases);

    $self->tt_params(
        total_count  => $patient_matches_count,
        search_term  => $search_terms,
        results      => $results,
    );

    return $self->render_view('register/select_patient.tt');
}

#-------------------------------------------------------------------------------
sub _convert_search_terms_for_display {
    my $self = shift; $self->get_current_runmode;
    my $vars = shift || return;

    # convert DT object back to EU date string for template:
    if ( my $dob = $vars->{dob} ) {
        $vars->{dob} = $dob->dmy; # dd-mm-yyyy format
    }

    my $search_terms = join ' & ',
        map { sprintf '%s=%s', $_, $vars->{$_} }
             grep { $vars->{$_} } qw(name dob patient_no);

    return $search_terms;
}

#-------------------------------------------------------------------------------
sub _extract_params_from_request {
    my $self  = shift; $self->_debug_path();
    my $param = shift; # $self->debug($param);

    if ( my $name = $param->{name} ) {
        # split $name on comma or space:
        my @names = split /\,\s?|\s+/, $name; # warn Dumper \@names;
        $param->{last_name}  = shift @names; # 1st element
        $param->{first_name} = shift @names; # 2nd element, if any left:
        my $middle_name = join ' ', @names;
        if ( $middle_name && length $middle_name > 1 ) { # skip if only initial
            $param->{middle_name} = $middle_name;
        } # warn Dumper $param;

        # put last_name & first_name into query param for patient merge link:
        $self->query->param( last_name  => $param->{last_name} );
        $self->query->param( first_name => $param->{first_name} );
    }

    if ( my $patient_no = $param->{patient_no} ) { # DEBUG 'patient_no:' .$patient_no;
        LIMS::Local::Utils::check_nhsno( $patient_no ) # if $patient_no passes nhs_no validation:
            ? $param->{nhs_number}  = $patient_no
            : $param->{unit_number} = $patient_no;
    }

    if ( grep $param->{$_}, qw(day month year) ) {
        # create dob as DT object:
        my $dob = DateTime->new( map { $_ => $param->{$_} } qw(day month year) ); # $self->debug($dob);
        $param->{dob} = $dob;
    } # warn Dumper $param;

    # save extracted search params in session for retrieval if redirected to /patient (if no records found):
    $self->session->param( patient_search_params => $param );

    return $param;
}

#-------------------------------------------------------------------------------
sub _generate_search_constraints {
    my $self  = shift; $self->_debug_path();
    my $param = shift; # $self->debug($param);

    my %search;

    my @form_fields = qw(patient_id last_name first_name dob unit_number nhs_number); # DEBUG $form_fields;

    my @search_keys = grep $param->{$_}, @form_fields; # $self->debug(\@search_keys);

    @search{@search_keys} = map {
        /_name/ ?
            { like => $param->{$_} . '%' } : # create 'like' query
            $param->{$_}; # create regular query
        } @search_keys; # $self->debug(\%search);

    if (%search) {
        return [ %search ]; # query needs arrayref
    }
    else {
        $self->stash->{error_msg}
            = $self->messages('registration')->{empty_submission};
        return 0;
    }
}

=begin
sub _group_cases_by_patient {
    my $self = shift;
    my $iterator = shift;

    my %patients;

    CASE:
    while (my $case = $iterator->next) {
        my $id = $case->patient->id; # $self->debug($id);

        # add referral_source object:
        push @{ $patients{$id}{referral_source} }, $case;

        # only need details once per patient:
        next CASE if $patients{$id}{details};

        my %details = (
            last_name   => $case->patient->last_name,
            first_name  => $case->patient->first_name,
            middle_name => $case->patient->middle_name,
            nhs_number  => $case->patient->nhs_number,
            gender      => $case->patient->gender,
            dob         => $case->patient->dob,
            id          => $case->patient->id,
        );
        $patients{$id}{details} = \%details;
    }

    my @patients;

    sub by_name {
        $patients{$a}{details}{last_name}   cmp $patients{$b}{details}{last_name}
          ||
        $patients{$a}{details}{first_name}  cmp $patients{$b}{details}{first_name}
          ||
        $patients{$a}{details}{middle_name} cmp $patients{$b}{details}{middle_name};
    }

    foreach ( sort by_name keys %patients ) { # $self->debug($patients{$_}{referral_source});
        push @patients, {
            details => $patients{$_}{details},
            referral_source => $patients{$_}{referral_source},
        };
    }

    return \@patients;
}
=cut

1;
