package LIMS::Model::Storage;

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

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

#-------------------------------------------------------------------------------
sub input_storage {
    my ($self, $data) = @_; # warn Dumper $data;
    
    { # add specimen.id:
        my $o = LIMS::DB::Specimen::Manager->get_specimens();
        my %map = map { $_->sample_code => $_->id } @$o;
        
        my $specimen = $data->{specimen};
        $data->{specimen_id} = $map{$specimen};
    } # warn Dumper $data;

    my $vialID = $data->{vialId}; # 10 chars, or multiples of (input validated)

    if ( length $vialID > 10 ) { # have multiple vials
        my (@ids) = $vialID =~ /([A-Z]{2}\d{8})/g; # warn Dumper \@ids;
        for my $vial_id(@ids) { # warn $vial_id;
            my $c = LIMS::Local::Utils::clone($data);
            $c->{vialId} = $vial_id; # warn Dumper $c;
            
            my $rtn
                = $self->update_object({ class => 'RequestStorage', data => $c });
            return $rtn if $rtn; # only returns true on error
        }
        return 0;
    }
    else {
        return $self->update_object({ class => 'RequestStorage', data => $data });
    }  
}

#-------------------------------------------------------------------------------
sub output_storage {
    my ($self, $data) = @_; # warn Dumper $data;
    
    my $request_id = $data->{request_id};
    my $vial_id    = $data->{vialId};
    
    my $o = LIMS::DB::RequestStorage->new(vialId => $vial_id)->load_speculative
    || return { error => "vialId $vial_id does not exist" };
    
    # first check vialId belongs to request_id:
    unless ( $o->request_id eq $request_id ) {
        return { error => "vialId $vial_id does not belong to this request" }
    }
    
    $o->signed_out(LIMS::Local::Utils::time_now);
    $o->save(changes_only => 1);
    return 'OK';
}

#-------------------------------------------------------------------------------
sub update_storage_vial {
    my ($self, $data) = @_; # warn Dumper $data; 
    
    my $o = LIMS::DB::RequestStorage->new(vialId => $data->{vialId})->load;
        # warn Dumper $o->as_tree;
    my @cols = $o->meta->column_names; # warn Dumper \@cols;
    
    while ( my ($key, $val) = each %$data ) {
        next unless grep $key eq $_, @cols; # skip non-table fields (eg specimen)
        next if $o->$key eq $val; # skip if not changed
        $o->$key($val);
    }
    
    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached;
    my $tx = sub { $o->save(changes_only => 1) };

    return $db->do_transaction($tx)
        ? 0 : 'update_storage_vial() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub update_storage_rack {
    my ($self, $data) = @_; # warn Dumper $data;
    
    my $rack_id = $data->{rack_id}; # warn Dumper $rack_id;
    my $plate   = $data->{plate}; # href of vialId => location (A1 - F12)

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

    my %data; # keys = success & fail
    my $update = sub {
        while ( my($vialId, $location) = each %$plate ) { # warn Dumper [$vialId,$location];
            my $o = LIMS::DB::RequestStorage->new(vialId => $vialId)->load;
                # warn Dumper $o->as_tree;
            $o->rack_id($rack_id);
            $o->vial_location($location);

            my $request_id = $o->request_id;
            my %h = ( vialId => $vialId, request_id => $request_id );

            $o->save(changes_only => 1) 
                ? $data{success}{$location} = \%h 
                : $data{fail}{$location}    = \%h;
                
            $data{requests}{$request_id}++; # add request_id
        } # warn Dumper \%data;
    };

    return $db->do_transaction($update)
        ? \%data # no errors, but maybe some failed to update
        : 'update_storage_rack() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub sign_out_storage_rack {
    my ($self, $data) = @_; # warn Dumper $data;
    
    my $rack_id = $data->{rack_id}; # warn Dumper $rack_id;
    my $vial_id = $data->{vial_id}; # arrayref of vialIds

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

    my $rs = do { # get all request_storage rows from vial_id array:
        my @q = ( vialId => $vial_id );
        LIMS::DB::RequestStorage::Manager->get_objects( query => \@q );
    };
    my %data; # keys = success & fail
    my $update = sub {
        for my $ref (@$rs) { # warn $id;
            $ref->signed_out($NOW);

            my $request_id = $ref->request_id;
            my $location = $ref->vial_location;
            
            my %h = ( vialId => $ref->vialId, request_id => $request_id );

            $ref->save(changes_only => 1) 
                ? $data{success}{$location} = \%h 
                : $data{fail}{$location}    = \%h;
                
            $data{requests}{$request_id}++; # add request_id
        } # warn Dumper \%data;
        { # set plate inactive:
            my $o = LIMS::DB::StorageRack->new(id => $rack_id)->load;
            $o->is_active('no');
            $o->save(changes_only => 1);
        }
    };

    return $db->do_transaction($update)
        ? \%data # no errors, but maybe some failed to update
        : 'sign_out_storage_rack() error - ' . $db->error;
}

#-------------------------------------------------------------------------------
sub get_request_storage {
    my ($self, $request_id) = @_;
    
    my @args = (
        query => [ request_id => $request_id ],
        with_objects => ['specimen', 'rack' ],
    );
    my $o = LIMS::DB::RequestStorage::Manager->get_objects(@args);
    return $o;
}

#-------------------------------------------------------------------------------
sub get_storage_vial {
    my ($self, $vial_id) = @_;
    my $o = LIMS::DB::RequestStorage->new(vialId => $vial_id)->load;
    return $o;
}

#-------------------------------------------------------------------------------
sub get_storage_rack {
    my ($self, $plateId) = @_;
    
    my $o = LIMS::DB::StorageRack->new( plateId => $plateId )->load_speculative;
    return $o ? $o->as_tree : 0; # return hashref for use in session
}

#-------------------------------------------------------------------------------
sub new_storage_rack {
    my ($self, $plateId) = @_;
    # return id if exists, otherwise create new object first:
    my $o = LIMS::DB::StorageRack->new( plateId => $plateId )->load_or_insert;
    return $o->id;
}

#-------------------------------------------------------------------------------
sub get_rack_contents { # find rack contents by rack.id, or list of vialId's:
    my ($self, $args) = @_;
    
    my $query;
    if ( my $rack_id = $args->{rack_id} ) {
        $query = [ rack_id => $rack_id ];
    }
    elsif ( my $vial_ids = $args->{vial_ids} ) {
        $query = [ vialId => $vial_ids ];
    }
    my @args = ( query => $query );
    my $o = LIMS::DB::RequestStorage::Manager->get_objects(@args);
    return $o;
}

1;