#!/usr/bin/perl -Ilibs -I./API/libs/API

package API::PrinterListAdmin;

use strict;
use warnings;
use autodie;
use namespace::autoclean;

use Carp;
use Moose;
use Try::Tiny;

extends "API::Controller";

use API::Config;
use ECS::DB::Schema;

__PACKAGE__->meta->make_immutable;

###############################################################################

sub index {
    my $self = shift->new({'input' => shift || {}});

    # error/success messages from save function get sent from index.cgi via the input params
    $self->data->{'error'} = $self->input->{'error'}
        if $self->input->{'error'};

    $self->data->{'success'} = $self->input->{'success'}
        if $self->input->{'success'};

    $self->data->{'templates'} = [ "index.html" ];

    return $self->result;
}

###############################################################################

sub saveChanges {
    my $self = shift->new({ 'input' => shift || {} });

    # parse input based on html input field names
    my %printerSaveData;
    my %casesByCol = ();
    my %casesById = ();
    for my $field (keys %{ $self->input }) {
        # input name format is productPrinter/{dbColumn}/{queueName}
        # accounts for queue names having - and _ in them
        if ($field =~ m/^productPrinter\/(\w+)\/([\w-]+)$/) {
            my ( $col, $queue ) = ( $1, $2 );
            $printerSaveData {'product'}->{ $queue }->{ $col } = $self->input->{ $field };
            next;
        } elsif ($field =~ m/^purchasingPrinter\/(\w+)\/([\w-]+)$/) {
            my ( $col, $queue ) = ( $1, $2 );
            $printerSaveData {'purchasing'}->{ $queue }->{ $col } = $self->input->{ $field };
        } elsif ($field =~ m/^printerAdminPrinter\/(\w+)\/([\w-]+)$/) {
            my ( $col, $queue ) = ( $1, $2 );
            $printerSaveData {'printer'}->{ $queue }->{ $col } = $self->input->{ $field };
        }
    }

    if (keys %printerSaveData > 0) {
        if (not $self->_validateProductPrinterSaveData({
            'saveData' => \%printerSaveData,
        })) {
            my $errMsg = "Printer list out of date - please refresh the page and try again.";
            $self->data->{'redirect'} = "./?error=$errMsg";
            return $self->result;
        }

        $self->_savePrinters({
            'saveData' => \%printerSaveData,
        });
    }

    my $successMsg = "Successfully updated printer list!";
    $self->data->{'redirect'} = ".?success=$successMsg";
    return $self->result;
}

###############################################################################

# ensures that all fields passed in exist in the matching schema before updating DB
sub _validateProductPrinterSaveData {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";

    my $productQueuesValid = $self->_validateSaveDataAgainstExistingQueues({
            'saveData' => $saveData->{'product'},
            'schemaName' => "ProductAdminPrinter",
        });

    my $purchasingQueuesValid = $self->_validateSaveDataAgainstExistingQueues({
            'saveData' => $saveData->{'purchasing'},
            'schemaName' => "PurchasingAdminPrinter"
        });

    my $printerQueuesValid = $self->_validateSaveDataAgainstExistingQueues({
            'saveData' => $saveData->{'printer'},
            'schemaName' => "PurchasingAdminPrinter",
        });

    if (!$productQueuesValid || !$purchasingQueuesValid || !$printerQueuesValid) {
        return 0;
    }

    return 1;
}

###############################################################################

sub _validateSaveDataAgainstExistingQueues {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";
    my $schemaName = $p->{'schemaName'}
        or croak "schemaName required";

    # queue names passed through form
    my @saveDataQueues = keys %{ $saveData };

    # get all queue names from schema that match queue names passed through form
    my $schema = ECS::DB::Schema::getSchema({
        'dbh' => $self->dbhr,
        'schema' => $schemaName,
    });
    my $results = $schema->search(
        {
            'queue' => \@saveDataQueues,
        }
    );

    my @existingQueues;
    while (my $printer = $results->next) {
        push(@existingQueues, $printer->queue);
    }

    # if true then all queue names passed through form are also found in DB, making the updates valid
    return @saveDataQueues == @existingQueues;
}

###############################################################################

sub _savePrinters {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";

    $self->_savePrinterInfo({
        'saveData' => $saveData->{'product'},
        'schemaName' => 'ProductAdminPrinter',
        'queueColumnName' => 'queue',
        'activeColumnName' => 'active',
        'dbTableName' => 'productAdminPrinters',
    });

    $self->_savePrinterInfo({
        'saveData' => $saveData->{'purchasing'},
        'schemaName' => 'PurchasingAdminPrinter',
        'queueColumnName' => 'queue',
        'activeColumnName' => 'purchasingAdminActive',
        'dbTableName' => 'purchasingAdminPrinters',
    });

    $self->_savePrinterInfo({
        'saveData' => $saveData->{'printer'},
        'schemaName' => 'PurchasingAdminPrinter',
        'queueColumnName' => 'queue',
        'activeColumnName' => 'printerAdminActive',
        'dbTableName' => 'purchasingAdminPrinters',
    });

    return;
}

###############################################################################

sub _savePrinterInfo {
    my $self = shift;
    my ($p) = @_;

    my $saveData = $p->{'saveData'}
        or croak "saveData required";

    my $schemaName = $p->{'schemaName'}
        or croak "schemaName required";

    my $queueColumnName = $p->{'queueColumnName'}
        or croak "queueColumnName required";
    
    my $activeColumnName = $p->{'activeColumnName'}
        or croak "activeColumnName required";

    my $dbTableName = $p->{'dbTableName'}
        or croak "dbTableName required";

    my @prepares;
    my @values;
    my @formQueues;
    # establish prepares and values for all queues to activate
    foreach my $queue (keys %{ $saveData }) {
        push @prepares, "(?,?)";
        push @values, $queue, 1;
        push @formQueues, $queue;
    }

    # account for unchecked checkboxes not being passed through HTML form
    # get all queues from DB, any not present in the update form fields should be de-activated
    my $schema = ECS::DB::Schema::getSchema({
        'dbh' => $self->dbhr,
        'schema' => $schemaName,
    });
    my $results = $schema->search();

    my @dbQueues;
    while (my $result = $results->next) {
        push @dbQueues, $result->$queueColumnName;
    }

    my %differenceMap;
    for my $field (@dbQueues, @formQueues) {
        $differenceMap{$field}++;
    }

    my @differentFields;
    for my $field (keys %differenceMap) {
        # due to _validateProductPrinterSaveData, we can guarantee that if a queue name only appears once between DB and form info, it's from the DB and should be de-activated
        if ($differenceMap{$field} == 1) {
            push @differentFields, $field;
        }
    }

    # establish prepares and values for all product queues to de-activate
    foreach my $queue (@differentFields) {
        push @prepares, "(?,?)";
        push @values, $queue, 0;
    }

    # perform DB update
    my $sth = $self->dbh->prepare(
        "INSERT INTO $dbTableName (
            $queueColumnName,
            $activeColumnName
        ) VALUES ".join(',', @prepares)."
        ON DUPLICATE KEY UPDATE
            $activeColumnName = VALUES($activeColumnName)"
    );

    $sth->execute( @values );
}

1;
