#!/usr/bin/perl

use Test::WWW::Mechanize::CGIApp;
use Test::Builder::Tester;

use strict;
use warnings;

use Data::Printer;
use DateTime;
use POSIX;

use Test::More;

BEGIN {
    require 't/test-lib.pl';
}

my $mech = get_mech();

my $dbh;

eval {
    $dbh = get_dbh() or die 'no database handle recieved from get_dbh';
};

warn $@ if $@;

my $dbix = get_dbix();
my $date = DateTime->now(); # p $date; exit;

my $plateId = 'SA01234567'; # test data

do_login();

{ # scan plate request with no vials registered:
    scan_plate();
    $mech->text_contains(
        'Warning: unknown vial(s) present',
        'OK: detected unknown or signed-out vial IDs',
    );
}
{
    # manually add data to request_storage table:
    my $data = _plate_data();
    my %h = (
        sample        => 'dna',
        volume        => 200,
        source        => 'liquid',
        method        => 'Qiagen',
        vialId        => undef, # supplied in loop
        request_id    => 1,
        specimen_id   => 1,
        part_number   => 1,
        vial_location => undef, # supplied in loop
        concentration => 50,
    );
    while ( ($h{vial_location}, $h{vialId}) = each %$data ) {
        $h{part_number}++; # create unique
        $dbix->insert('request_storage', \%h);
    }
}


{ # scan new plate:
    scan_plate();
    import_data();
    $mech->text_contains(
        'storage location: RealTime/1/1',
        'OK: expected storage_location',
    );
}

{ # repeat:
    scan_plate();
    $mech->text_contains(
        'Rack exists in storage',
        'OK: rack already exists in storage',
    );
    # void plate:
    $mech->form_name('import-data');
    $mech->click_button( name => 'void_plate' );            #  print_and_exit();

    $mech->text_contains(
        'Confirm you want to remove all vials from plate',
        'OK: confirmation message loaded',
    );
    $mech->form_name('import-data');
    $mech->field( confirm_void_plate => 1 );
    $mech->submit();                                          # print_and_exit();
    $mech->text_contains(
        'successfully decoupled all vials from plate',
        'OK: successfully removed vials from plate',
    );
}

# fill RealTime/1/1 location (previous voiding action emptied it):
{
    my $location = 'RealTime/1/1';
    my $plateId  = 'SA00000001';  # starting point
    for ( 1 .. 4 ) {
        my %h = (
            plateId => $plateId++,
            storage_location => $location,
        );
        $dbix->insert('storage_racks', \%h);
    }
    scan_plate();                                            # print_and_exit();
    import_data();                                           # print_and_exit();
    # auto-allocation selects next available tray (1/2):
    $mech->text_contains(
        'storage location: RealTime/1/2',
        'OK: expected storage_location',
    );
}

void_plate(); # do plate voiding action through dbix
{ # add 3 plates to RealTime/1/2 (last action removed solitary plate):
    my $location = 'RealTime/1/2';
    my $plateId  = 'SA00000005';  # starting point
    for ( 1 .. 3 ) {
        my %h = (
            plateId => $plateId++,
            storage_location => $location,
        );
        $dbix->insert('storage_racks', \%h);
    }
    scan_plate();                                            # print_and_exit();
    import_data();                                           # print_and_exit();
    # auto-allocation selects last space in 1/2:
    $mech->text_contains(
        'storage location: RealTime/1/2',
        'OK: expected storage_location',
    );
}

void_plate(); # do plate voiding action through dbix
{ # add more trays, retain empty space in 1/2 due to voiding, check this space re-allocated:
    my $location = 'RealTime/1/3';
    my $plateId  = 'SA00000008';  # starting point
    for ( 1 .. 4 ) {
        my %h = (
            plateId => $plateId++,
            storage_location => $location,
        );
        $dbix->insert('storage_racks', \%h);
    }
    scan_plate();                                            # print_and_exit();
    import_data();                                           # print_and_exit();
    # auto-allocation selects last space in 1/2 NOT 1/3:
    $mech->text_contains(
        'storage location: RealTime/1/2',
        'OK: expected storage_location',
    );
}

# void plate again, remove 1 from 1/1, check space re-allocated:
void_plate(); # do plate voiding action through dbix
{
    $dbix->delete('storage_racks', { plateId => 'SA00000001'});
    scan_plate();                                            # print_and_exit();
    import_data();                                           # print_and_exit();
    # auto-allocation selects last space in 1/1:
    $mech->text_contains(
        'storage location: RealTime/1/1',
        'OK: expected storage_location',
    );
}
{ # sign out plate:
    scan_plate();                                            # print_and_exit();
    $mech->text_contains(
        'Rack exists in storage',
        'OK: rack already exists in storage',
    );
    # select export all:
    $mech->form_name('import-data');
    $mech->click_button( name => 'export' );                 # print_and_exit();
    $mech->text_contains(
        'signed out: ' . $date->dmy('.'),
        'OK: plate signed out',
    );
    my $is_active = $dbix->select('storage_racks', 'is_active',
        { plateId => $plateId })->list;
    is($is_active, 'no', 'OK: signed-out plate status is inactive');

    # re-scan:
    scan_plate();                                            # print_and_exit();
    # can't find vials:
    $mech->text_contains(
        'Warning: unknown vial(s) present',
        'OK: detected unknown or signed-out vial IDs',
    );
}

done_testing(26);

sub scan_plate {
    $mech->get_ok('/storage/read_xtr_96');                   # print_and_exit();
    $mech->form_name('do_scan');
    $mech->submit();                                         # print_and_exit();
}

sub import_data {
    $mech->form_name('import-data');
    $mech->field( storage_location => 'RealTime' );
    $mech->click_button( name => 'import' );                 # print_and_exit();
}

sub void_plate { # do plate voiding action through dbix:
    # no need to specify rack_id as all are the same, and change for each scan:
    # but probably better to find the actual one:
    $dbix->select('storage_racks', 'id', { plateId => $plateId })
        ->into(my $rack_id); # p $rack_id;
    $dbix->update('request_storage',
        { rack_id => undef, vial_location => undef }, { rack_id => $rack_id } );
    $dbix->delete('storage_racks', { plateId => $plateId }); # test data plateId
}

sub _plate_data {
    my $i = 1000000001; # 10 digit number
    my @cells = map { $_ . '01' .. $_ . '12' } ( 'A' .. 'H' ); # p @cells;
    my %h = map { $_ => $i++ } @cells; # p %h;
    return \%h;
}
