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

package RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyStatement;

use strict;
use Data::Dumper;
use File::Path;
use IO::File;

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

use Data::Dumper;
use DB_File;

use RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts;
use RPS::ArtistRoyalty::Fast::Static::ArtistAlbumContractAccountBalances;
use RPS::ArtistRoyalty::Fast::Mapped::Data;
use RPS::DB::Item::ArtistPayee;

use base 'RPS::ArtistRoyalty::Fast::Final';

use constant kArtistRoyaltyStatementID                  => 0;
use constant kArtistRoyaltyRunID                        => 1;
use constant kPayeeID                                   => 2;
use constant kPreviousBalance                           => 3;
use constant kBalance                                   => 4;
use constant kMinPayment                                => 5;
use constant kCrossCollateralizedPreviousBalance        => 6;
use constant kCrossCollateralizedIncomeSubtotal         => 7;
use constant kCrossCollateralizedExpenseSubtotal        => 8;
use constant kCrossCollateralizedSubtotal               => 9;
use constant kTransactionSubtotal                       => 10;
use constant kUncrossedSubtotal                         => 11;
use constant kTotal                                     => 12;
use constant kLicenseIncomeSubtotal                     => 13;
use constant kContractLevelLicenseIncomeSubtotal        => 14;
use constant kContractLevelLicenseIncomePreviousBalance => 15;
use constant kContractLevelLicenseIncomeTotal           => 16;
use constant kAmountDue                                 => 17;
use constant kPayorID                                   => 18;
use constant kOnHold                                    => 19;
use constant kStatus                                    => 20;
use constant kJobID                                     => 21;
use constant kError                                     => 22;
use constant kDistribution                              => 23;

# 'private' constants
use constant kBalanceData => 24;

my @gFields = (
    'artist_royalty_statement_id',            'artist_royalty_run_id',
    'payee_id',                               'previous_balance',
    'balance',                                'min_payment',
    'cross_collateralized_previous_balance',  'cross_collateralized_income_subtotal',
    'cross_collateralized_expense_subtotal',  'cross_collateralized_subtotal',
    'transaction_subtotal',                   'uncrossed_subtotal',
    'total',                                  'license_income_subtotal',
    'contract_level_license_income_subtotal', 'contract_level_license_income_previous_balance',
    'contract_level_license_income_total',    'amount_due',
    'payor_id',                               'on_hold',
    'status',                                 'job_id',
    'error',                                  'distribution',
);
sub Fields { return \@gFields; }

#root@localhost [C_WELK]> desc artist_royalty_statement;
#+------------------------------------------------+---------------------+------+-----+---------+----------------+
#| Field                                          | Type                | Null | Key | Default | Extra          |
#+------------------------------------------------+---------------------+------+-----+---------+----------------+
#| artist_royalty_statement_id                    | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
#| artist_royalty_run_id                          | int(10) unsigned    | NO   | MUL | 0       |                |
#| payee_id                                       | int(10) unsigned    | NO   | MUL | 0       |                |
#| previous_balance                               | decimal(16,4)       | NO   |     | 0.0000  |                |
#| balance                                        | decimal(16,4)       | NO   |     | 0.0000  |                |
#| min_payment                                    | decimal(16,4)       | NO   |     | 0.0000  |                |
#| cross_collateralized_previous_balance          | decimal(16,4)       | NO   |     | 0.0000  |                |
#| cross_collateralized_income_subtotal           | decimal(16,4)       | NO   |     | 0.0000  |                |
#| cross_collateralized_expense_subtotal          | decimal(16,4)       | NO   |     | 0.0000  |                |
#| cross_collateralized_subtotal                  | decimal(16,4)       | NO   |     | 0.0000  |                |
#| transaction_subtotal                           | decimal(16,4)       | NO   |     | 0.0000  |                |
#| uncrossed_subtotal                             | decimal(16,4)       | NO   |     | 0.0000  |                |
#| total                                          | decimal(16,4)       | NO   |     | 0.0000  |                |
#| license_income_subtotal                        | decimal(16,4)       | NO   |     | 0.0000  |                |
#| contract_level_license_income_subtotal         | decimal(16,4)       | NO   |     | 0.0000  |                |
#| contract_level_license_income_previous_balance | decimal(16,4)       | NO   |     | 0.0000  |                |
#| contract_level_license_income_total            | decimal(16,4)       | NO   |     | 0.0000  |                |
#| amount_due                                     | decimal(16,4)       | NO   |     | 0.0000  |                |
#| payor_id                                       | int(10) unsigned    | NO   |     | 0       |                |
#| on_hold                                        | tinyint(3)          | NO   |     | 0       |                |
#| status                                         | tinyint(3)          | YES  |     | NULL    |                |
#| job_id                                         | int(10) unsigned    | NO   |     | 0       |                |
#| error                                          | text                | YES  |     | NULL    |                |
#| distribution                                   | tinyint(3) unsigned | YES  |     | 0       |                |
#+------------------------------------------------+---------------------+------+-----+---------+----------------+
#24 rows in set (0.00 sec)

# We're going to need to get the artist_payee_account data, for min payment
# and previous balance.
# And we will need all the account Transactions, in a MappedData
# representation.   I don't think I'll bother with mapping the min_payment and
# previous balance into the MappedData.  Let's just use a berkeley db file.
# Even if we have to eval the artist_payee_account, there's just one per
# statement.

my $gArtistPayeeAccounts;
my $gBalances;

sub FileName { 'artist_royalty_statement' }

# We're going to use some globals to manage this class.
# This method initializes those.
#
# If this was intended to live in the web app, I'd probably store these inside the App singleton.
# But for now I will just put this in the package's global scope.
#

sub _MaxIDSql { 'SELECT MAX(artist_royalty_statement_id) FROM artist_royalty_statement' }

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

    # Call the inherited method first.
    #
    $class->SUPER::Init($outputDirectory);

    # Initialize the artist payee account data.
    #
    $gArtistPayeeAccounts = RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts->GetArtistPayeeAccounts($dataPath);

    # Initialize the balance account data.
    #
    $gBalances = RPS::ArtistRoyalty::Fast::Static::ArtistAlbumContractAccountBalances->GetAccountBalances($dataPath);
}

sub new {
    my ( $class, $data, $runID, $payorID ) = @_;

    # Basically, we'll bless the 'BlankItem' array reference into this class
    # Then we can invoke methods on it.
    #
    my $item = $class->BlankItem();
    $class->_IncrementID();

    my $payeeID = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kPayeeID];
    my $payee = RPS::DB::Item::ArtistPayee->Lookup( artist_payee_id => $payeeID );

    # We're going to attempt to grab the statement distribution setting from the payee now.
    # We'll use this later when deciding which statements should be included in the 'manual' zip.
    my $distribution;
    if ($payee) {
        $distribution = $payee->distribution;
    }

    $item->[kArtistRoyaltyStatementID] = $class->_CurrentID();
    $item->[kArtistRoyaltyRunID]       = $runID;
    $item->[kPayeeID]                  = $payeeID;
    $item->[kPayorID]                  = $payorID;
    $item->[kDistribution]             = $distribution;

    my $accountData = $gArtistPayeeAccounts->{ int( $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kPayeeID] ) };
    my $min_payment;
    my $crossedPreviousBalance   = 0;
    my $uncrossedPreviousBalance = 0;
    if ($accountData) {
        my @bits = split( "\t", $accountData );
        $item->[kMinPayment]      = $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kMinPayment];
        $min_payment = $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kMinPayment];
        $item->[kPreviousBalance] = $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kBalance];
        $item->[kContractLevelLicenseIncomePreviousBalance] =
          $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kContractLicenseIncomeBalance];
        $item->[kOnHold] = $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kOnHold];

        # Also seed the cross collateralized previous balance.
        # In theory every Album object will be incrementing this if it is carrying a crossed balance.
        #
        $item->[kCrossCollateralizedPreviousBalance] =
          $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kContractLicenseIncomeBalance];

#        $crossedPreviousBalance = $item->[kCrossCollateralizedPreviousBalance];
        $crossedPreviousBalance =
           $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kPreviousCrossedBalance];
        $uncrossedPreviousBalance =
           $bits[RPS::ArtistRoyalty::Fast::Static::ArtistPayeeAccounts::kPreviousUncrossedBalance];
    }

    # Pop a slot onto the array to hold a reference to this payee's album
    # account balances.
    #
    my $hashRef = eval( $gBalances->{ int( $item->[kPayeeID] ) } );
    Log->info( "ALBUM BALANCE HASHREF FOR PAYEE: " . $item->[kPayeeID], $hashRef );

    push @$item, $hashRef;

    # We don't want to generate statement with only min payment in its activity.
    # We need to check if statement doesn't contain any values other than zero in activity.
    # So, we need to check some constants (3 - 17) from the top of this modules (kPreviousBalance, kBalance, etc..)
    # And since the item structure doesn't contain transaction info, we add it separately.

    my $transaction = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kPendingTransactionID];
    my $statement_activity = join (':', grep {/[^0]+/} (@{$item}[3..17]));
    $statement_activity .= $transaction if $transaction;

    Common::Log::Debug("*** Skipping statement with min payment but no activity ***") if ($statement_activity eq $min_payment);

    return bless $item, $class if ($min_payment ne $statement_activity);

    # Allow balances to appear on statement even if there is no statement activity
    return bless $item, $class if ( $hashRef );

    if (( $crossedPreviousBalance && $crossedPreviousBalance != 0 ) ||
        ( $uncrossedPreviousBalance && $uncrossedPreviousBalance != 0 ) ) {
        return bless $item, $class;
    }

    return undef;
}

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

    # Test whether we're still looking at data for 'this' object.
    #
    if ( $self->[kPayeeID] != $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kPayeeID] ) {
        return undef;
    }

    return 1;
}

sub previousBalance {
    my ( $self, $albumID, $artistContractID ) = @_;

    $albumID          = int($albumID);
    $artistContractID = int($artistContractID);

    Log->info( "STATEMENT SELF: ", $self );

    my $hashRef = $self->[kBalanceData];
    Log->info( "previousBalance hashref:", $hashRef );

    my $previousBalance = $self->[kBalanceData]->{$albumID}{$artistContractID};
    Log->info("  previousBalance for '$albumID', '$artistContractID' = '$previousBalance'");
    return $previousBalance;
}

sub _output {
    my ( $self, $fd ) = @_;

    # !!! Funny, we don't need this in this scope.
    # !!! But we need to remove it or we'll try to print it.
    #
    my $albumAccountBalances = pop @$self;

    my $payeeID = $self->[kPayeeID];
    Log->info( " DEBUG: PAYEE $payeeID 1:  ", $self );

    # !!! need to take the contract level license income into account here, too.
    # !!! need to calculate the total based on subtotal and previous balance - look at the old code's logic.
    #
    $self->[kContractLevelLicenseIncomeTotal] =
      $self->[kContractLevelLicenseIncomeSubtotal] + $self->[kContractLevelLicenseIncomePreviousBalance];
    $self->[kLicenseIncomeSubtotal] += $self->[kContractLevelLicenseIncomeSubtotal];

# !!! This line may be the problem !!!
# !!! The kContractLevelLicenseIncomeTotal already has the previous balance - Why add this twice?
#    $self->[kCrossCollateralizedSubtotal] += $self->[kContractLevelLicenseIncomePreviousBalance] + $self->[kContractLevelLicenseIncomeTotal];
    $self->[kCrossCollateralizedSubtotal] += $self->[kContractLevelLicenseIncomeTotal];

    $self->[kTotal] += $self->[kCrossCollateralizedSubtotal]
      if $self->[kCrossCollateralizedSubtotal] > 0;

    $self->[kBalance] = $self->[kPreviousBalance] + $self->[kTotal] + $self->[kTransactionSubtotal];

    my $amountDue = $self->[kBalance];
    if ( $self->[kOnHold] || $self->[kMinPayment] > $amountDue ) {
        $amountDue = 0;
    }
    $self->[kAmountDue] = $amountDue;

    Log->info( " DEBUG: 2:  ", $self );

    $self->SUPER::_output($fd);
}

1;

