#!/usr/bin/perl -Ilibs

package API::VendorImageImportAdmin;

use strict;
use warnings;
use autodie;

use Carp;
use File::Temp ();
use Image::Magick;
use IO::Uncompress::Unzip qw($UnzipError);
use MIME::Base64 ();
use Moose;
use Net::SCP ();
use POSIX ();

extends "API::Controller",
        "SOAP::Server::Parameters";

use API::Config;

use lib ($API::Config::Config->{'Website_libs'});
use ECS::Config;
use ECS::Vendor;

# maximum number of images we will process in one batch
my $IMAGE_LIMIT = 250;

__PACKAGE__->meta->make_immutable;

return 1;

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

sub viewIndex {
    my $self = shift->new(@_);

    $self->data->{'vendors'} = ECS::Vendor::getAllVendorsName({
        'DBH' => $self->dbhr,
    });

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

    return $self->result;
}

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

sub productLookup {
    my $self = shift->new(@_);

    $self->data->{'dataType'} = "json";
    $self->data->{'data'} = {};

    my (@where, @params);
    if($self->input->{'productID'}) {
        push @where, "product.productID = ?";
        push @params, $self->input->{'productID'};
    }
    elsif($self->input->{'partnumber'}) {
        push @where, "product.partnumber LIKE CONCAT(?, '%')";
        push @params, $self->input->{'partnumber'};
    }
    else {
        croak "Missing search parameters\n";
    }

    push @where, "productVendor.vendorID = ?";
    push @params, $self->input->{'vendorID'};

    my $sql = "SELECT
            product.productID,
            product.partnumber,
            product.description
        FROM
            product
            JOIN productVendor
                USING (productID)
        WHERE
            " . join(" AND ", @where);
    my $sth = $self->dbhr->prepare($sql);
    $sth->execute(@params);

    $self->data->{'data'}->{'success'} = 1;
    $self->data->{'data'}->{'products'} = $sth->fetchall_arrayref({});

    return $self->result;
}

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

sub processFile {
    my $self = shift->new(@_);

    $self->data->{'dataType'} = "json";
    $self->data->{'data'} = {};

    my @images = $self->_extractZip;
    return $self->result unless @images;

    if( @images > $IMAGE_LIMIT ) {
        my $tooManyImagesError = "Cannot handle more than $IMAGE_LIMIT images at once. "
            . "Please split this file into batches of $IMAGE_LIMIT images or fewer.";
        $self->data->{ 'data' }->{ 'error' } = $tooManyImagesError;
        return $self->result;
    }

    # create thumbnails
    for my $image (@images) {
        my $magick = Image::Magick->new;
        $magick->BlobToImage($image->{'data'});
        $magick->Thumbnail("133x100");
        $image->{'thumbnail'} = $magick->ImageToBlob;
    }

    my $sql = "SELECT
            product.productID,
            product.partnumber,
            product.description
        FROM
            product
            JOIN productVendor
                USING (productID)
        WHERE
            product.partnumber LIKE ?
            AND productVendor.vendorID = ?";
    my $sth = $self->dbhr->prepare($sql);
    for my $image (@images) {
        (my $partnumber = $image->{'name'}) =~ s/\.\w+$//; # remove extension
        $partnumber =~ s/_\d+$//; # remove numeric suffix
        $partnumber =~ s/-//g; # remove dashes

        $sth->execute("$partnumber%", $self->input->{'vendorID'});
        my $matches = $sth->fetchall_arrayref({});

        my $warning;
        if($image->{'width'} < 800) {
            $warning = "Image too small (" . $image->{'width'} . "px)";
        }

        push @{ $self->data->{'data'}->{'images'} }, {
            'file'    => $image->{'name'},
            'data'    => MIME::Base64::encode_base64($image->{'thumbnail'}, ""),
            'matches' => $matches,
            'warning' => $warning,
        };
    }

    $self->data->{'data'}->{'success'} = 1;

    return $self->result;
}

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

sub saveImages {
    my $self = shift->new(@_);

    $self->data->{'dataType'} = "json";
    $self->data->{'data'} = {};

    my $sql = "INSERT INTO
            image_queue
        SET
            status     = 'C',
            filename   = ?,
            partnumber = ?";
    my $sthInsertQueue = $self->dbh->prepare($sql);

    my $config = $API::Config::Config->{'photo'};
    my $scp = Net::SCP->new($config->{'host'}, $config->{'user'});

    my @images = $self->_extractZip;
    return $self->result unless @images;

    my $i = 0;
    
    for my $image (@images) {
        my @productIDs = ();
        for my $field (keys %{ $self->input }) {
            next unless $field =~ m/^include_(\d+)_(\d+)$/;
            push @productIDs, $2 if $1 == $i;
        }

        my $mainProductID = $productIDs[0];
        next unless $mainProductID;

        my $tmp = File::Temp->new;
        $tmp->print($image->{'data'});
        my $source = $tmp->filename;

        my $timestamp = POSIX::strftime "%Y%m%d%H%M%S", localtime;        
        my $filename = "${mainProductID}_$timestamp.jpg";
        my $imageDir = $config->{'path'} . "/" . $mainProductID;
        my $destination = "$imageDir/$filename";
        $destination =~ s/\.jpg$/-orig.jpg/;

        if($ECS::Config::c{'sandbox'}) {
            warn "Sandbox: not actually copying files\n";
        }
        else {
            $scp->mkdir($imageDir)
                or die $scp->{'errstr'};
            $scp->put($source, $destination)
                or die $scp->{'errstr'};
       }

        for my $productID (@productIDs) {
            $sthInsertQueue->execute(
                "$productID/$filename",
                $productID,
            );
        }
    }
    continue { $i ++ }

    $self->data->{'data'}->{'success'} = 1;

    return $self->result;
}

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

sub _extractZip {
    my $self = shift;

    my @images = ();
    for my $attachment (@{ $self->parameters->parts }) {
        my $zipData = $attachment->bodyhandle->as_string;
        my $zip = IO::Uncompress::Unzip->new(\$zipData)
            or croak "Invalid Zip File: $UnzipError";

        while(not $zip->eof) {
            my $name = $zip->getHeaderInfo->{'Name'};

            my ($buf, $data);
            while($zip->read(\$buf)) {
                $data .= $buf;
            }
            return unless $data;

            my $magick = Image::Magick->new;

            # returns truthy values for errors
            return if $magick->BlobToImage($data);

            push @images, {
                'name'   => File::Basename::basename($name),
                'data'   => $data,
                'width'  => $magick->Get("width"),
                'height' => $magick->Get("height"),
            };
            
            $zip->nextStream;
        }
    }

    return @images;
}

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

