package BookPub::Sale::Report::RandomHouse::SalesClosedFiles;

use Data::Dumper;
use Date::Simple;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Date;

use base 'Common::Script';

use lib '/app/tools/bookpub/lib';
use BookPub::Catalog::Product::Book;
use BookPub::Catalog::Book::WithAuthor;
use BookPub::Tracker::File;
use BookPub::Tracker::Service;
use BookPub::DB::Item::File;

sub _options {
    {
        client_id => {
            short       => 'c',
            required    => 1,
            description => 'Client id',
            parameter   => 'i'
        },
        file_id => {
            short       => 'f',
            description => 'Limit to this file id',
            parameter   => 'i'
        },
        output_dir => {
            short       => 'o',
            description => 'Directory to store generated files in (default: .)',
            parameter   => 's'
        },
        zip => {
            short       => 'z',
            description => 'Zip results to a file',
        },
    };
}

sub _process {
    my $self = shift;

    my @closedFiles = $self->_getFiles();

    $self->_preProcess( fileIDs => \@closedFiles );
    $self->_processFiles( fileIDs => \@closedFiles );
    $self->_postProcess();
}

sub _getFiles {
    my $self   = shift;
    my $fileID = $self->param('file_id');

    my $collection = BookPub::DB::Item::File->GetAllClosed( fileID => $fileID );

    my @results;
    while ( my $dbitem = $collection->next ) {
        push( @results, $dbitem->file_id );
    }

    Log->warn("No closed files found") unless (@results);
    return @results;
}

sub _preProcess {
    my $self    = shift;
    my %args    = @_;
    my $fileIDs = $args{fileIDs} || die "fileIDs required";

    Log->info("Running preProcess checks");
    foreach my $id (@$fileIDs) {
        my $count = BookPub::DB::Item::File->ClosedChildCount($id);
        die "ABORTING: file id: $id has closed children" if ($count);
    }

    my $outputDir = $self->param('output_dir');

    if ( defined($outputDir) && -d $outputDir ) {
        my $files = `ls "$outputDir"`;
        if ($files) {
            die "$outputDir contains files already.  Aborting";
        }
    }

    if ( $self->param('zip') && !$outputDir ) {
        die "An output directory must be specified when using the zip option";
    }
}

sub _processFiles {
    my $self    = shift;
    my %args    = @_;
    my $fileIDs = $args{fileIDs} || die "fileIDs required";

    Log->info("Starting File Processing");

    foreach my $file (@$fileIDs) {
        Log->info("Processing File ID $file");

        # Credits and Sales need to be stored in seperate files.
        if ( BookPub::DB::Item::File->HasSales($file) ) {
            Log->info("- processing sales file $file");
            my $outfile = $self->_initOutput( fileID => $file );

            $self->_writeHeader( fileID => $file, outfile => $outfile );
            my $count = $self->_writeDetail( fileID => $file, outfile => $outfile );
            $self->_writeFooter( outfile => $outfile, count => $count + 1 );

            close($outfile) if ($outfile);
        }

        # Process credits
        if ( BookPub::DB::Item::File->HasReturns($file) ) {
            Log->info("- processing credits file $file");
            my $outfile = $self->_initOutput( fileID => $file, credit => 1 );

            $self->_writeHeader( fileID => $file, outfile => $outfile, credit => 1 );
            my $count = $self->_writeDetail( credit => 1, fileID => $file, outfile => $outfile );
            $self->_writeFooter( outfile => $outfile, count => $count + 1 );

            close($outfile) if ($outfile);
        }
    }
}

sub _postProcess {
    my $self = shift;

    if ( $self->param('zip') ) {
        $self->_zipResults();
    }
}

sub _initOutput() {
    my $self   = shift;
    my %args   = @_;
    my $fileID = $args{fileID};
    my $credit = $args{credit};

    assert($fileID);

    my $file = new BookPub::Tracker::File( fileID => $fileID );
    die "File ID $fileID not found" unless ($file);

    my $filename = $file->OrigFileName();

    # Append a 'CR' to a credit file if there are also sales associated with this file.
    if ($credit) {
        $filename =~ s/\./_CR./ if ( BookPub::DB::Item::File->HasSales($fileID) );
    }

    my $path = $self->param('output_dir') ? $self->param('output_dir') : ".";
    unless ( -d $path ) {
        my $res = system("mkdir -p \"$path\"");
        die "Could not create directory '$path'" if ($res);
    }

    $path .= "/$filename";
    $path =~ s/(xls|tsv|csv)$/txt/i;

    Log->debug(" - Output filename: $path");
    die "File exists not overwriting: $path" if ( -e $path );

    open( OUTFILE, ">$path" ) || die "Could not write to file '$path'";

    return \*OUTFILE;

}

sub _writeHeader() {
    my $self    = shift;
    my %args    = @_;
    my $fileID  = $args{fileID};
    my $outfile = $args{outfile};
    my $credit  = $args{credit};

    assert($outfile);
    assert($fileID);

    my $file = new BookPub::Tracker::File( fileID => $fileID );

    my $newestSaleDate = BookPub::DB::Item::File->GetNewestSaleDate($fileID);
    die "Undetermined sale date" unless ($newestSaleDate);
    my $reportingDate = $self->_getReportingDate($newestSaleDate);
    Log->debug("Reporting Date: $reportingDate");

    my ( $serviceName, $customerNumber ) = $self->_getServiceInfo( $file->ServiceID );
    Log->debug(
        sprintf(
            "ID: %s, Name: %s, Number: %s",
            $file->ServiceID,
            $serviceName    ? $serviceName    : '<undef>',
            $customerNumber ? $customerNumber : '<undef>'
        )
    );

    my $date = Date::Simple::today();
    my $processDate = sprintf( "%04d%02d%02d", $date->year, $date->month, $date->day );
    Log->debug("Process Date: $processDate");

    my $po = $customerNumber;
    $po .= $reportingDate;
    $po .= 'CR' if ($credit);

    my $subscription = BookPub::DB::Item::File->HasSubscriptionSales($fileID) ? 'Y' : 'N';
    my $creditFlag = $credit ? 'Y' : 'N';

    $customerNumber = '' unless ($customerNumber);
    my @row = ( 'H', $serviceName, $customerNumber, $processDate, $po, $subscription, $creditFlag );

    my $header = join( '|', @row );
    Log->debug("$header");
    print $outfile "$header\n";
}

sub _writeDetail() {
    my $self    = shift;
    my %args    = @_;
    my $fileID  = $args{fileID};
    my $outfile = $args{outfile};
    my $credit  = $args{credit};
    my $count   = 0;

    assert($outfile);
    assert($fileID);

    my $sales = BookPub::DB::Item::Sale->GetAllByFileID($fileID);

    while ( my $dbitem = $sales->next ) {
        if ($credit) {
            next if ( $dbitem->units >= 0 );
        } else {
            next if ( $dbitem->units < 0 );
        }

        my $product = $self->_getBookProduct($dbitem);
        my $book    = $self->_getBook($product);

        my $isbn = $product->ISBN13 ? $product->ISBN13 : $product->ISBN10;
        $isbn = '' unless ($isbn);

        my $format = $self->_parseFormat( $dbitem->r_format, $dbitem->format_type ) || '';

        my $listPrice = sprintf( "%0.2f", $dbitem->r_list_price );
        my $discount = $dbitem->r_discount ? sprintf( "%0.2f", $dbitem->r_discount ) : '';
        my $author = $book->AuthorName || '';
        my $title  = $book->Title      || '';

        my @row = (
            'D',        $dbitem->date_begin,    $isbn,     '',                    $format, '',
            $listPrice, $dbitem->currency_code, $discount, abs( $dbitem->units ), '',      $author,
            $title,     $dbitem->country_code
        );

        my $out = join( '|', @row );

        #Log->error( $out );
        print $outfile "$out\n";

        $count++;
    }

    return $count;
}

sub _writeFooter() {
    my $self    = shift;
    my %args    = @_;
    my $outfile = $args{outfile};
    my $count   = $args{count};

    assert($outfile);
    assert($count);

    # make sure to count this footer line
    $count++;

    print $outfile "C|$count\n";
}

sub _getServiceInfo {
    my $self      = shift;
    my $serviceID = shift;

    assert($serviceID);

    return
        $serviceID == BookPub::Tracker::Service::BAKERANDTAYLOR() ? ( "BAKERANDTAYLOR",              '0229900000' )
      : $serviceID == BookPub::Tracker::Service::CHRISTIANBOOK()  ? ( "Christian Book Distributors", '0765620001' )
      : $serviceID == BookPub::Tracker::Service::DNAML()          ? ( "DNAML",                       '0223260000' )
      : $serviceID == BookPub::Tracker::Service::FICTIONWISE()    ? ( "Fictionwise",                 '0294900000' )
      : $serviceID == BookPub::Tracker::Service::GOSPOKEN()       ? ( "GoSpoken",                    '0031130000' )
      : $serviceID == BookPub::Tracker::Service::KOBO()           ? ( "Kobo",                        '5563040000' )
      : $serviceID == BookPub::Tracker::Service::MOBIPOCKET()     ? ( "Mobipocket eBookBase",        '0451270000' )
      : $serviceID == BookPub::Tracker::Service::OVERDRIVE()      ? ( "Overdrive",                   '0513530000' )
      : $serviceID == BookPub::Tracker::Service::SONY()           ? ( "Sony Connect",                undef )
      :   ( BookPub::Tracker::Service::GetDefaultServiceName( service_id => $serviceID ), undef );
}

sub _getReportingDate {
    my $self = shift;
    my $date = shift || return;

    my $reportingDate = new Common::Date($date);
    my $d             = $reportingDate->monthEnd();
    $d =~ s/-//g;

    return $d;
}

sub _parseFormat {
    my $self           = shift;
    my $reportedFormat = shift;
    my $format         = shift;

    # Filter some known types, otherwise just pass the value through
    my $result =
        $reportedFormat =~ /kindle.*/i      ? 'KINDLE'
      : $reportedFormat =~ /adobe ebook.*/i ? 'AAER'
      : $reportedFormat =~ /msreader.*/i    ? 'MSR'
      : $reportedFormat =~ /palm.*/         ? 'PALM'
      : $reportedFormat
      if ($reportedFormat);

#  For now we are just going to base this off r_format.  If it's blank then
#  pass the blank through.
#
# This will catch any imports that used static format types.
#unless( $result ) {
#    die( "No reported format, but format type set: '$format'" ) if( $format && $format ne 'pdb ' && $format ne 'mobi' && $format ne 'pdf ' );
#}

    return $result;
}

sub _zipResults {
    my $self = shift;
    my $path = $self->param('output_dir') || die;

    my $date = Date::Simple::today;
    my $filename = sprintf( "RH_%04d%02d%02d", $date->year, $date->month, $date->day );

    my $res = system("cd $path && zip $filename *");

    die "Failed to create archive" if ($res);

    print "Results archived in $path/$filename.zip";
}

sub _getBookProduct {
    my $self = shift;
    my $dbitem = shift || die;

    my $product = new BookPub::Catalog::Product::Book( productID => $dbitem->product_id );

    return $product;
}

sub _getBook {
    my $self = shift;
    my $product = shift || die;

    my $book = new BookPub::Catalog::Book::WithAuthor( bookID => $product->BookID );

    return $book;
}

1;
