#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2012 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------

package RPS::LabelRoyalty::Fast::Final::File::LabelRoyaltySaleReportItem;

use strict;
use Data::Dumper;
use File::Path;
use File::Copy;
use POSIX qw(ceil);

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use lib '/app/tools/raptor/lib';
use Common::RSApp;

use Data::Dumper;
use DB_File;

use Common::Log;
use Common::Assert;
use Common::Client;
use Common::Locale;

use RPS::Statement::Label::Report;

use RPS::LabelRoyalty::Fast::Static::Products;
use RPS::LabelRoyalty::Fast::Static::Services;
use RPS::LabelRoyalty::Fast::Mapped::Data;
use RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyLabelItem;
use RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyStatement;

use base 'RPS::LabelRoyalty::Fast::Final::File';

# JPK - Going to use the fields from the final report file, rather than the LabelRoyaltyStatementSale table.

use constant kLabelName   => 0;
use constant kServiceID   => 1;
use constant kServiceName => 2;
use constant kDistributor => 3;
use constant kTerritory   => 4;  # This is COUNTRY CODE, not REGION.  Region is NOT USED for label royalties...
use constant kPeriodBegin => 5;
use constant kPeriodEnd   => 6;
use constant kProductType => 7;  # These two appear to be straight out of the sale table schema, so not the product type from product table.
use constant kProductFormat     => 8;
use constant kCatalogID         => 9;
use constant kUPC               => 10;
use constant kAlbumID           => 11;
use constant kAlbumName         => 12;
use constant kAlbumArtist       => 13;
use constant kReleaseDate       => 14;
use constant kUPCAlt            => 15;
use constant kAlbumCustom1      => 16;
use constant kAlbumCustom2      => 17;
use constant kAlbumCustom3      => 18;
use constant kISRC              => 19;
use constant kTrackID           => 20;
use constant kDiscNo            => 21;
use constant kTrackNo           => 22;
use constant kTrackName         => 23;
use constant kTrackArtist       => 24;
use constant kTrackCustom1      => 25;
use constant kTrackCustom2      => 26;
use constant kTrackCustom3      => 27;
use constant kUnits             => 28;
use constant kUnitPrice         => 29;
use constant kExtPrice          => 30;
use constant kCurrencyCode      => 31;
use constant kCurrencyConv      => 32;
use constant kCurrencyUnitPrice => 33;
use constant kCurrencyExtPrice  => 34;
use constant kFree              => 35;
use constant kDistFee           => 36;
use constant kCurrencyNetPrice  => 37;
use constant kMediaType         => 38;

use constant kProductID      => 39;
use constant kLabelReference => 40;

# JPK - I'll use the same basic idea, but these will be the headers.
# Note that some of these will need to be modified to include the native currency code on output.
# I'll probably just use a regex, and embed a token (like 'CUR') to replace on.
#
my @gFields = (
    'label-name',     'service-id',     'service-name',   'distributor',    'territory',      'period-begin',
    'period-end',     'product-type',   'product-format', 'catalog-id',     'upc',            'album-id',
    'album-name',     'album-artist',   'release-date',   'upc-alt',        'album-custom-1', 'album-custom-2',
    'album-custom-3', 'isrc',           'track-id',       'disc-no',        'track-no',       'track-name',
    'track-artist',   'track-custom-1', 'track-custom-2', 'track-custom-3', 'units',          'unit-price',
    'ext-price',      'currency-code',  'currency-conv',  'CUR-unit-price', 'CUR-ext-price',  'free',
    'dist-fee',       'CUR-net-price',  'media_type',
);

sub Fields { return \@gFields; }

# !!! We don't need nor HAVE an id, since we're not writing this stuff to a table anymore.
#
sub _MaxIDSql { '' }

my $gProducts;
my $gServices;
my $gRunID;
my $gLastPayeeID;
my $gOutputDirectory;
my $gFD;
my $gLines;

use constant kReportSubDirectory => 'sale_reports';

sub Init {
    my ( $class, $outputDirectory, $dataPath, $runID ) = @_;
    assert( $dataPath, "MISSING dataPath" );

    $gLastPayeeID = 0;

    $gProducts = RPS::LabelRoyalty::Fast::Static::Products->GetProducts($dataPath);
    $gServices = RPS::LabelRoyalty::Fast::Static::Services->GetServices($dataPath);

    $gRunID = $runID;

    # We're going to extend the output directory and add another level.
    # This will make it easier to find all the files later.

    $gOutputDirectory = $outputDirectory . '/' . kReportSubDirectory;

    if ( !-d $gOutputDirectory ) {
        mkpath($gOutputDirectory) or die "ERROR: Unable to create path $gOutputDirectory: $!\n";
    }

    $class->SUPER::Init($gOutputDirectory);
}

# Going to overload this to behave a bit differently.
# We will be managing more than one file.
#

sub InitFD {
    my ( $class, $outputDirectory ) = @_;

    # This logic kicks in when this method is called by the inherited Init method.
    # Basically, we don't want to ACTUALLY open the file yet.
    #
    return unless $gLastPayeeID;

    # JPK - We shouldn't have to explicitly call 'close' on the previous file descriptor.
    # In theory it will be closed magically when it goes out of scope.
    #
    $class->SUPER::InitFD($outputDirectory);

    # JPK - We also need to add in the header when we first open the file.
    # But I may just override the 'WriteFD' method since that returns the file descriptor.
}

sub WriteFD {
    my ( $class, $dataPath ) = @_;

    my $fd = $class->SUPER::WriteFD($dataPath);
    if ($fd) {
        my $fields = $class->Fields();

        my $header = join( "\t", @$fields );

        # Replace 'CUR' with native currency code.
        #
        my $locale = Common::Client::Current()->Locale();
        my $cc     = lc( $locale->currencyFormat()->currencyCode() );
        $header =~ s/CUR/$cc/g;

        print $fd "$header\n";

        $gFD = $fd;
    }

    return $fd;
}

sub FileName {

    # !!! If we have this structured correctly, this should not get called unless we've seen a statement id.
    #
    assert($gLastPayeeID);

    # We can't use this interface - It tries to look up the 'statement' record (which has not been created yet).
    # So, just use the label_id aka payee_id (which is all it does).
    #    return RPS::Statement::Label::Report::ReportFileNameFromID($gLastStatementID);

    return "$gLastPayeeID.txt";
}

sub initReport {
    my ( $class, $statement ) = @_;

    assert($statement);
    assert( $statement->[RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyStatement::kPayeeID] );

    $class->finalizeReport();
    $gLastPayeeID = $statement->[RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyStatement::kPayeeID];
    $gLines       = 0;
    $class->InitFD($gOutputDirectory);
}

sub finalizeReport {
    my ($class) = @_;

    if ($gLastPayeeID) {
        if ( !$gLines ) {
            print $gFD "There are no sales to report\n";
        }
    }
}

# We need to tie this to the Statement class.
# There should actually be a seperate file for each statement.
# I think the easiest way will be to keep track of the last statement_id, and have the 'new'
# method take a reference to the Statement object.
#
sub new {
    my ( $class, $data, $label, $statement ) = @_;

    my $item = $class->BlankItem();

    #    $class->_IncrementID();

    # We can initialize the bulk of the state immediately.
    #
    my $productID = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kProductID];
    my @product = split( "\t", $gProducts->{$productID} );

    my $startDate = substr( $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kStartDate], 0, 7 );
    my $endDate   = substr( $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kEndDate],   0, 7 );

    $item->[kLabelName] = $product[RPS::LabelRoyalty::Fast::Static::Products::kLabelName];
    $item->[kServiceID] = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kServiceID];

    # !!! This is a bit of a hack in order to avoid adding another column
    # !!! We'll keep the distributor ID in the kDistributor column, and replace it with the
    # !!! distributor's name when we output.
    #
    $item->[kDistributor] = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kDistributorID];

    #    $item->[kServiceName] = $gServices->{ $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kServiceID] };
    #    $item->[kDistributor] = $gServices->{ $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kDistributorID] };

    $item->[kTerritory]        = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kCountryCode];
    $item->[kPeriodBegin]      = $startDate;
    $item->[kPeriodEnd]        = $endDate;
    $item->[kProductType]      = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kProductType];
    $item->[kProductFormat]    = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kFormatType];
    $item->[kCatalogID]        = $product[RPS::LabelRoyalty::Fast::Static::Products::kCatalogID];
    $item->[kUPC]              = $product[RPS::LabelRoyalty::Fast::Static::Products::kUPC];
    $item->[kAlbumID]          = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumID];
    $item->[kAlbumName]        = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumName];
    $item->[kAlbumArtist]      = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumArtist];
    $item->[kReleaseDate]      = $product[RPS::LabelRoyalty::Fast::Static::Products::kReleaseDate];
    $item->[kUPCAlt]           = $product[RPS::LabelRoyalty::Fast::Static::Products::kUPCAlt];
    $item->[kAlbumCustom1]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumCustom1];
    $item->[kAlbumCustom2]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumCustom2];
    $item->[kAlbumCustom3]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kAlbumCustom3];
    $item->[kISRC]             = $product[RPS::LabelRoyalty::Fast::Static::Products::kISRC];
    $item->[kTrackID]          = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackID];
    $item->[kDiscNo]           = $product[RPS::LabelRoyalty::Fast::Static::Products::kDiscNo];
    $item->[kTrackNo]          = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackNo];
    $item->[kTrackName]        = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackName];
    $item->[kTrackArtist]      = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackArtist];
    $item->[kTrackCustom1]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackCustom1];
    $item->[kTrackCustom2]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackCustom2];
    $item->[kTrackCustom3]     = $product[RPS::LabelRoyalty::Fast::Static::Products::kTrackCustom3];
    $item->[kUnits]            = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kUnits];               # <- this field will accumulate
    $item->[kUnitPrice]        = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kPrice];
    $item->[kCurrencyCode]     = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kCurrencyCode];
    $item->[kCurrencyConv]     = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kConversionRate];
    $item->[kFree]             = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kFree];
    $item->[kDistFee]          = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kDistributionFee];
    $item->[kMediaType]        = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kMediaType];
    $item->[kExtPrice]         = 0;                                                                    # !!! This is calculated ?
    $item->[kCurrencyNetPrice] = 0;                                                                    # !!! This is calculated
    $item->[kCurrencyExtPrice] = 0;                                                                    # !!! This is calculated

    $item->[kCurrencyUnitPrice] = Common::RSMath::round( $item->[kUnitPrice] * $item->[kCurrencyConv], 8 );

    $item->[kProductID] = $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kProductID];

    # Any extra crap we need 'secretly', we'll just push onto the end of the array.
    #
    push @$item, $label;

    return bless( $item, $class );
}

sub process {
    my ( $self, $data ) = @_;

    # !!! We don't seem to actually care about label ID or label contract ID at this level, which is weird...
    # !!! Presumably we'll check those in the LabelRoyaltyLabel object?

    #    if ($self->[kLabelID]           != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kLabelID]
    #     || $self->[kLabelContractID]   != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kLabelContractID]

    my $startDate = substr( $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kStartDate], 0, 7 );
    my $endDate   = substr( $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kEndDate],   0, 7 );

    if (   $self->[kAlbumID] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kAlbumID]
        || $self->[kTrackID] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kTrackID]
        || $self->[kServiceID] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kServiceID]
        || $self->[kDistributor] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kDistributorID]
        || $self->[kProductType] ne $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kProductType]
        || $self->[kProductFormat] ne $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kFormatType]
        || $self->[kMediaType] ne $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kMediaType]
        || $self->[kTerritory] ne $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kCountryCode]
        || $self->[kCurrencyConv] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kConversionRate]
        || $self->[kDistFee] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kDistributionFee]
        || $self->[kProductID] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kProductID]
        || $self->[kFree] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kFree]
        || $self->[kUnitPrice] != $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kPrice]
        || $self->[kPeriodBegin] ne $startDate
        || $self->[kPeriodEnd] ne $endDate
        || $self->[kCurrencyCode] ne $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kCurrencyCode] ) {
        Log->info("RPS::LabelRoyalty::Fast::Final::File::LabelRoyaltySaleReportItem::process - returning UNDEF");
        return undef;
    }

    $self->[kUnits] += $data->[RPS::LabelRoyalty::Fast::Mapped::Data::kUnits];

    Log->info("RPS::LabelRoyalty::Fast::Final::File::LabelRoyaltySaleReportItem::process - returning UNDEF");
    return 1;
}

# Override _output to do some final calculations.
#
sub _output {
    my ( $self, $ITEMS ) = @_;

    my $label = pop @$self;

    # Don't need the product_id in the output file.
    #
    my $productID = pop @$self;

    $self->[kServiceName] = $gServices->{ $self->[kServiceID] };
    $self->[kDistributor] = ( $self->[kDistributor] == $self->[kServiceID] ) ? '' : $gServices->{ $self->[kDistributor] };

    $self->[kTrackID] = '' unless ( $self->[kTrackID] );
    $self->[kDiscNo]  = '' unless ( $self->[kDiscNo] );
    $self->[kTrackNo] = '' unless ( $self->[kTrackNo] );
    $self->[kFree] = $self->[kFree] ? 'Y' : 'N';

    # The 'ext_price' is essentially the gross sale amount.
    #
    my $price          = $self->[kUnitPrice];
    my $units          = $self->[kUnits];
    my $conversionRate = $self->[kCurrencyConv];
    my $grossSales     = Common::RSMath::round( $units * $price, 8 );
    my $feePercent     = $self->[kDistFee] / 100;

    my $fee            = Common::RSMath::round( $grossSales * $feePercent, 8 );
    my $netSales       = $grossSales - $fee;
    my $baseNetSales   = Common::RSMath::round( $netSales * $conversionRate, 8 );
    my $baseFee        = Common::RSMath::round( $fee * $conversionRate, 8 );
    my $basePrice      = Common::RSMath::round( $price * $conversionRate, 8 );
    my $baseGrossSales = Common::RSMath::round( $grossSales * $conversionRate, 8 );

    $self->[kDistFee]          = $feePercent;
    $self->[kExtPrice]         = $grossSales;
    $self->[kCurrencyExtPrice] = $baseGrossSales;
    $self->[kCurrencyNetPrice] = $baseNetSales;

    $label->[RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyLabelItem::kGrossSales] += $baseGrossSales;
    $label->[RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyLabelItem::kNetSales]   += $baseNetSales;
    $label->[RPS::LabelRoyalty::Fast::Final::DB::LabelRoyaltyLabelItem::kFee]        += $baseFee;

    $self->SUPER::_output($ITEMS);

    $gLines++;
}

sub Commit {
    my ( $class, $sourceBasePath, $runID ) = @_;

    # JPK - Where do we get the final location of the file from?
    # Well, we might as well use the same interface the old code dose (the config file).
    #
    my $outFilePath = RPS::Statement::Label::Report::ReportPathFromRunID($runID);
    if ( !-d $outFilePath ) {
        mkpath($outFilePath) or die "ERROR: Unable to create path $outFilePath: $!\n";
    }

    # !!! Actually, we're going to copy a whole lotta files.
    #    my $fileName = RPS::Statement::Label::Report::ReportFileNameFromID($statementID);
    #    my $filePath = $outFilePath . $fileName;

    my $sourceFullPath = $sourceBasePath . '/' . kReportSubDirectory;

    # Now, in theory, we can copy the files.
    # JPK - Not going to make this 'portable' - just going to let the shell do the work.
    #
    my $cmd    = "cp $sourceFullPath/*.txt $outFilePath";
    my $status = system($cmd);
    # RSD-7518. Since We may have statements with min payment set and no activity there are no statements files being generated.
    # So, let's warn instead of die if any of directory is not found

    warn "ERROR EXECUTING '$cmd': ($?) $!" unless 0 == $status;
}

1;

