#!/usr/bin/perl

# Copyright 2016 PTFS-Europe Ltd
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.

=head1 stockrotation.pl

 Script to handle stockrotation. Including rotas, their associated stages
 and items

=cut

use Modern::Perl;
use CGI;

use C4::Auth qw( get_template_and_user );
use C4::Context;
use C4::Output qw( output_html_with_http_headers );

use Koha::Libraries;
use Koha::StockRotationRotas;
use Koha::StockRotationItems;
use Koha::StockRotationStages;
use Koha::Item;
use Koha::Util::StockRotation qw( get_branches get_stages move_to_next_stage toggle_indemand remove_from_stage add_items_to_rota get_barcodes_status );

my $input = CGI->new;

unless (C4::Context->preference('StockRotation')) {
    # redirect to Intranet home if self-check is not enabled
    print $input->redirect("/cgi-bin/koha/mainpage.pl");
    exit;
}

my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
    {
        template_name   => 'tools/stockrotation.tt',
        query           => $input,
        type            => 'intranet',
        flagsrequired   => {
            tools => '*',
            stockrotation => '*',
        },
    }
);

# Grab all passed data
# 'our' since Plack changes the scoping
# of 'my'
our %params = $input->Vars();

my $op = $params{op};

if (!defined $op) {

    # No operation is supplied, we're just displaying the list of rotas
    my $rotas = Koha::StockRotationRotas->search(
        undef,
        {
            order_by => { -asc => 'title' }
        }
    )->as_list;

    $template->param(
        existing_rotas => $rotas,
        no_op_set      => 1
    );

} elsif ($op eq 'create_edit_rota') {

    # Edit an existing rota or define a new one
    my $rota_id = $params{rota_id};

    my $rota = {};

    if (!defined $rota_id) {

        # No ID supplied, we're creating a new rota
        # Create a shell rota hashref
        $rota = {
            cyclical => 1
        };

    } else {

        # ID supplied, we're editing an existing rota
        $rota = Koha::StockRotationRotas->find($rota_id);

    }

    $template->param(
        rota => $rota,
        op   => $op
    );

} elsif ($op eq 'cud-toggle_rota') {

    # Find and update the active status of the rota
    my $rota = Koha::StockRotationRotas->find($params{rota_id});

    my $new_active = ($rota->active == 1) ? 0 : 1;

    $rota->active($new_active)->store;

    # Return to rotas page
    print $input->redirect('stockrotation.pl');

} elsif ($op eq 'cud-process_rota') {

    # Get a hashref of the submitted rota data
    my $rota = get_rota_from_form();

    if (!process_rota($rota)) {

        # The submitted rota was invalid
        $template->param(
            error => 'invalid_form',
            rota => $rota,
            op   => 'create_edit_rota'
        );

    } else {

        # All was well, return to the rotas list
        print $input->redirect('stockrotation.pl');

    }

} elsif ($op eq 'manage_stages') {

    my $rota = Koha::StockRotationRotas->find($params{rota_id});

    $template->param(
        rota            => $rota,
        branches        => get_branches(),
        existing_stages => get_stages($rota),
        rota_id         => $params{rota_id},
        op              => $op
    );

} elsif ($op eq 'create_edit_stage') {

    # Edit an existing stage or define a new one
    my $stage_id = $params{stage_id};

    my $rota_id = $params{rota_id};

    if (!defined $stage_id) {

        my $rota = Koha::StockRotationRotas->find($rota_id);
        # No ID supplied, we're creating a new stage
        $template->param(
            branches => get_branches(),
            stage    => {},
            rota     => $rota,
            rota_id  => $rota_id,
            op       => $op
        );

    } else {

        # ID supplied, we're editing an existing stage
        my $stage = Koha::StockRotationStages->find($stage_id);
        my $rota  = Koha::StockRotationRotas->find( $stage->rota->rota_id );

        $template->param(
            branches => get_branches(),
            stage    => $stage,
            rota     => $rota,
            rota_id  => $stage->rota->rota_id,
            op       => $op
        );

    }

} elsif ($op eq 'confirm_remove_from_rota') {

    # Get the stage we're deleting
    $template->param(
        op       => $op,
        rota_id  => $params{rota_id},
        stage_id => $params{stage_id},
        item_id  => $params{item_id}
    );

} elsif ($op eq 'confirm_delete_rota') {

    # Get the rota we're deleting
    my $rota = Koha::StockRotationRotas->find($params{rota_id});

    # Get all items on this rota, for each prefetch their
    # stage and biblio objects
    my $sritems = Koha::StockRotationItems->search(
        { 'stage.rota_id' => $params{rota_id} },
        {
            prefetch => {
                stage => {
                    'stockrotationitems' => {
                        'itemnumber' => 'biblionumber'
                    }
                }
            }
        }
    );

    $template->param(
        rota_id  => $params{rota_id},
        sritemstotal  => $sritems->count,
        op       => $op
    );

} elsif ($op eq 'cud-delete_rota') {

    # Get the rota we're deleting
    my $rota = Koha::StockRotationRotas->find($params{rota_id});

    $rota->delete;

    # Return to the rotas list
    print $input->redirect("/cgi-bin/koha/tools/stockrotation.pl");

} elsif ($op eq 'confirm_delete_stage') {

    # Get the stage we're deleting
    my $stage = Koha::StockRotationStages->find($params{stage_id});

    $template->param(
        op    => $op,
        stage => $stage
    );

} elsif ($op eq 'cud-delete_stage') {

    # Get the stage we're deleting
    my $stage = Koha::StockRotationStages->find($params{stage_id});

    # Get the ID of the rota with which this stage is associated
    # (so we can return to the "Manage stages" page after deletion)
    my $rota_id = $stage->rota->rota_id;

    $stage->delete;

    # Return to the stages list
    print $input->redirect("?op=manage_stages&rota_id=$rota_id");

} elsif ($op eq 'cud-process_stage') {

    # Get a hashref of the submitted stage data
    my $stage = get_stage_from_form();

    # The rota we're managing
    my $rota_id = $params{rota_id};

    if (!process_stage($stage, $rota_id)) {

        # The submitted stage was invalid
        # Get all branches
        my $branches = get_branches();

        $template->param(
            error        => 'invalid_form',
            all_branches => $branches,
            stage        => $stage,
            rota_id      => $rota_id,
            op           => 'create_edit_stage'
        );

    } else {

        # All was well, return to the stages list
        print $input->redirect("?op=manage_stages&rota_id=$rota_id");

    }

} elsif ($op eq 'manage_items') {

    my $rota = Koha::StockRotationRotas->find($params{rota_id});

    # Get all items on this rota, for each prefetch their
    # stage and biblio objects
    my $sritems = Koha::StockRotationItems->search(
        { 'stage.rota_id' => $params{rota_id} },
        {
            prefetch => {
                stage => {
                    'stockrotationitems' => {
                        'itemnumber' => 'biblionumber'
                    }
                }
            }
        }
    );

    $template->param(
        rota_id  => $params{rota_id},
        error    => $params{error},
        sritems  => $sritems,
        branches => get_branches(),
        stages   => get_stages($rota),
        rota     => $rota,
        op       => $op
    );

} elsif ($op eq 'cud-move_to_next_stage') {

    move_to_next_stage($params{item_id}, $params{stage_id});

    # Return to the items list
    print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});

} elsif ($op eq 'cud-toggle_in_demand') {

    # Toggle the item's in_demand
    toggle_indemand($params{item_id}, $params{stage_id});

    # Return to the items list
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});

} elsif ($op eq 'cud-remove_item_from_stage') {

    # Remove the item from the stage
    remove_from_stage($params{item_id}, $params{stage_id});

    # Return to the items list
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});

} elsif ($op eq 'cud-add_items_to_rota') {

    # The item's barcode,
    # which we may or may not have been passed
    my $barcode = $params{barcode};

    # The rota we're adding the item to
    my $rota_id = $params{rota_id};
    my $rota    = Koha::StockRotationRotas->find($rota_id);

    # The uploaded file filehandle,
    # which we may or may not have been passed
    my $barcode_file = $input->upload("barcodefile");

    # We need to create an array of one or more barcodes to
    # insert
    my @barcodes = ();

    # If the barcode input box was populated, use it
    push @barcodes, $barcode if $barcode;

    # Only parse the uploaded file if necessary
    if ($barcode_file) {

        # Call binmode on the filehandle as we want to set a
        # UTF-8 layer on it
        binmode($barcode_file, ":encoding(UTF-8)");
        # Parse the file into an array of barcodes
        while (my $barcode = <$barcode_file>) {
            $barcode =~ s/\r/\n/g;
            $barcode =~ s/\n+/\n/g;
            my @data = split(/\n/, $barcode);
            push @barcodes, @data;
        }

    }

    # A hashref to hold the status of each barcode
    my $barcode_status = {
        ok        => [],
        on_other  => [],
        on_this   => [],
        not_found => []
    };

    # If we have something to work with, do it
    get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);

    # Now we know the status of each barcode, add those that
    # need it
    if (scalar @{$barcode_status->{ok}} > 0) {

        add_items_to_rota($rota_id, $barcode_status->{ok});

    }
    # If we were only passed one barcode and it was successfully
    # added, redirect back to ourselves, we don't want to display
    # a report, redirect also if we were passed no barcodes
    if (
        scalar @barcodes == 0 ||
        (scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1)
    ) {

        print $input->redirect("?op=manage_items&rota_id=$rota_id");

    } else {

        # Report on the outcome
        $template->param(
            barcode_status => $barcode_status,
            rota_id        => $rota_id,
            rota           => $rota,
            op             => $op
        );

    }

} elsif ($op eq 'cud-move_items_to_rota') {

    # The barcodes of the items we're moving
    my @move = $input->multi_param('move_item');

    foreach my $item(@move) {

        # The item we're moving
        my $item = Koha::Items->find($item);

        # Move it to the new rota
        $item->add_to_rota($params{rota_id});

    }

    # Return to the items list
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});

}

output_html_with_http_headers $input, $cookie, $template->output;

sub get_rota_from_form {

    return {
        id          => $params{id},
        title       => $params{title},
        cyclical    => $params{cyclical},
        description => $params{description}
    };
}

sub get_stage_from_form {

    return {
        stage_id    => $params{stage_id},
        branchcode  => $params{branchcode},
        duration    => $params{duration}
    };
}

sub process_rota {

    my $sub_rota = shift;

    # Fields we require
    my @required = ('title','cyclical');

    # Count of the number of required fields we have
    my $valid = 0;

    # Ensure we have everything we require
    foreach my $req(@required) {

        if (exists $sub_rota->{$req}) {

            chomp(my $value = $sub_rota->{$req});
            if (length $value > 0) {
                $valid++;
            }

        }

    }

    # If we don't have everything we need
    return 0 if $valid != scalar @required;

    # Passed validation
    # Find the rota we're updating
    my $rota = Koha::StockRotationRotas->find($sub_rota->{id});

    if ($rota) {

        $rota->title(
            $sub_rota->{title}
        )->cyclical(
            $sub_rota->{cyclical}
        )->description(
            $sub_rota->{description}
        )->store;

    } else {

        $rota = Koha::StockRotationRota->new({
            title       => $sub_rota->{title},
            cyclical    => $sub_rota->{cyclical},
            active      => 0,
            description => $sub_rota->{description}
        })->store;

    }

    return 1;
}

sub process_stage {

    my ($sub_stage, $rota_id) = @_;

    # Fields we require
    my @required = ('branchcode','duration');

    # Count of the number of required fields we have
    my $valid = 0;

    # Ensure we have everything we require
    foreach my $req(@required) {

        if (exists $sub_stage->{$req}) {

            chomp(my $value = $sub_stage->{$req});
            if (length $value > 0) {
                $valid++;
            }

        }

    }

    # If we don't have everything we need
    return 0 if $valid != scalar @required;

    # Passed validation
    # Find the stage we're updating
    my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});

    if ($stage) {

        # Updating an existing stage
        $stage->branchcode_id(
            $sub_stage->{branchcode}
        )->duration(
            $sub_stage->{duration}
        )->store;

    } else {

        # Creating a new stage
        $stage = Koha::StockRotationStage->new({
            branchcode_id  => $sub_stage->{branchcode},
            rota_id        => $rota_id,
            duration       => $sub_stage->{duration}
        })->store;

    }

    return 1;
}

=head1 AUTHOR

Andrew Isherwood <andrew.isherwood@ptfs-europe.com>

=cut
