#------------------------------------------------------------
# Copyright (C) 2009 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

package RPS::Mechanical::CA::Process::CreateStatement;

use strict;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use lib '/app/tools/raptor/lib';

use POSIX qw(ceil);

use Common::Assert;
use Common::DB::Item::Client;
use Common::Log;
use Common::RSMath;
use Common::Util;
use Data::Dumper;
use Raptor::DB::Item::File;
use Raptor::DB::Item::Sale;
use RPS::DB::Item::Album;
use RPS::DB::Item::AlbumContract;
use RPS::DB::Item::Artist;
use RPS::DB::Item::BonusTrack;
use RPS::DB::Item::ControlledComposition;
use RPS::DB::Item::FinanceAccount;
use RPS::DB::Item::CALicenseReserve;
use RPS::DB::Item::CALicenseReserveRun;
use RPS::DB::Item::Master;

#use RPS::DB::Item::MechanicalCarryover;
use RPS::DB::Item::CAMechanicalRun;

#use RPS::DB::Item::CAMechanicalRunCarryover;
use RPS::DB::Item::CAMechanicalStatement;
use RPS::DB::Item::CAMechanicalStatementAdjustmentItem;
use RPS::DB::Item::CAMechanicalStatementCrossedLicenseTransaction;
use RPS::DB::Item::CAMechanicalStatementItem;
use RPS::DB::Item::CAMechanicalStatementLicense;
use RPS::DB::Item::CAMechanicalStatementLicenseTransaction;
use RPS::DB::Item::CAMechanicalStatementPublisher;
use RPS::DB::Item::CAMechanicalStatementPublisherTransaction;
use RPS::DB::Item::CAMechanicalStatementTrack;
use RPS::DB::Item::CAMechanicalStatementTransaction;
use RPS::DB::Item::CAPublisherIndirectBalanceAccount;
use RPS::DB::Item::CAPublisherTrackCrossedLicenseAccount;
use RPS::DB::Item::PendingTransaction;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Publisher;
use RPS::DB::Item::PublisherAccount;
use RPS::DB::Item::PublisherTrackCrossedLicenseAccount;
use RPS::DB::Item::RegionCountryMap;
use RPS::DB::Item::ReserveLiquidation;
use RPS::DB::Item::SaleCAPublisherMap;
use RPS::DB::Item::SaleRunMap;
use RPS::DB::Item::Song;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::Track;
use RPS::DB::Item::CATrackLicense;
use RPS::Mechanical::CA::MechanicalRun;
use RPS::Mechanical::Process::CreateStatement;
use RPS::Mechanical::Process::SaleException;

use RPS::Mechanical::Process::CreateStatement::Complex;
use base 'RPS::Mechanical::Process::CreateStatement::Complex';

#sub _defaultIssueStatRateID { return 9; }
sub _defaultIssueStatRateID {

    my ( $self, $licenseType ) = @_;

    # This is the only possible value for CA (for now).
    #
    return 26;
}

sub _getAllPossibleLicenses {
    my ($self) = @_;

    return RPS::DB::Item::CATrackLicense->GetLicensesForPublisherStatement( $self->_publisherID(), $self->_payorID() );
}

sub _currencyCode {
    my ($self) = @_;
    return 'CAD';
}

sub _getStatRates {
    my ($self) = @_;
    return RPS::DB::Item::StatRate->GetRatesCA();
}

sub _createLicenseReserve {
    my ( $self, $statementItem, $units, $periodsRemaining ) = @_;

    my $newReserve = RPS::DB::Item::CALicenseReserve->Create(
        ca_track_license_id        => $statementItem->ca_track_license_id,
        original_statement_item_id => $statementItem->ca_mechanical_statement_item_id,
        product_id                 => $statementItem->product_id,
        sale_stat_rate_id          => $statementItem->sale_stat_rate_id,
        issue_stat_rate_id         => $statementItem->issue_stat_rate_id,
        units                      => $units,
        effective_rate             => $statementItem->net_rate,
        periods_remaining          => $periodsRemaining,
    );
    return $newReserve;
}

sub _getLicensesWithPendingTransactions {
    my ($self) = @_;
    return RPS::DB::Item::CATrackLicense->GetLicensesWithPendingTransactionsForMechanicalStatement(
        $self->_publisherID(),
        $self->_payorID()
    );
}

sub _getCrossedLicensesWithPendingTransactions {
    my ($self) = @_;
    return RPS::DB::Item::CATrackLicense->GetCrossedLicensesWithPendingTransactionsForMechanicalStatement(
        $self->_publisherID(),
        $self->_payorID()
    );
}

sub _reserveType {
    my ($self) = @_;
    return RPS::DB::Item::ReserveLiquidation::kTypeCAPublishingLicense;
}

sub _ringtoneStatRateType {
    my ($self) = @_;
    return RPS::DB::Item::StatRate::kCARingStatRateType;
}

sub _lookupTrackLicense {
    my ( $self, $id ) = @_;
    return RPS::DB::Item::CATrackLicense->Lookup( ca_track_license_id => $id );
}

sub _createStatementItem {
    my ( $self, $statementID, $trackLicenseID, $trackLicensePublisherID, $productID, $upc, $isrc, $saleStatRateID, $issueStatRateID ) = @_;
    my $item = RPS::DB::Item::CAMechanicalStatementItem->Create(
        ca_mechanical_statement_id => $self->{_statementID},
        ca_track_license_id        => $trackLicenseID,
        ca_publisher_id            => $trackLicensePublisherID,
        product_id                 => $productID,
        upc                        => $upc,
        isrc                       => $isrc,
        sale_stat_rate_id          => $saleStatRateID,
        issue_stat_rate_id         => $issueStatRateID,
    );
    return $item;
}

sub _createStatementLicense {
    my ( $self, $trackLicense ) = @_;

    my $item = RPS::DB::Item::CAMechanicalStatementLicense->Create(
        ca_mechanical_statement_id => $self->_statementID(),
        ca_track_license_id        => $trackLicense->ca_track_license_id(),
        region_id                  => $trackLicense->region_id(),
    );
    return $item;
}

sub _getStatementLicenseItemsByStatementID {
    my ( $self, $statementID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementLicense->GetByMechanicalStatementID($statementID);
}

sub _getStatementTrackItemsByStatementID {
    my ( $self, $statementID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementTrack->GetByMechanicalStatementID($statementID);
}

sub _getFileLabel {
    my ( $self, $run ) = @_;
    return RPS::Mechanical::CA::MechanicalRun::GenerateFileLabel($run);
}

sub _lookupPublisher {
    my ( $self, $publisherID ) = @_;
    return RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $publisherID );
}

sub _lookupPublisherAccount {
    my ( $self, $publisherID, $payorID ) = @_;
    return RPS::DB::Item::CAPublisherAccount->Lookup( ca_publisher_id => $publisherID, payor_id => $payorID );
}

sub _createPublisherAccount {
    my ( $self, $publisherID, $payorID ) = @_;
    return RPS::DB::Item::CAPublisherAccount->Create(
        ca_publisher_id => $publisherID,
        payor_id        => $payorID,
    );
}

sub _lookupStatementPublisherAccount {
    my ( $self, $publisherID, $payorID, $indirectPublisherID ) = @_;
    return RPS::DB::Item::CAPublisherIndirectBalanceAccount->Lookup(
        ca_publisher_id       => $publisherID,
        payor_id              => $payorID,
        indirect_publisher_id => $indirectPublisherID
    );
}

sub _createStatementPublisherAccount {
    my ( $self, $publisherID, $payorID, $indirectPublisherID ) = @_;
    return RPS::DB::Item::CAPublisherIndirectBalanceAccount->Create(
        ca_publisher_id       => $publisherID,
        payor_id              => $payorID,
        indirect_publisher_id => $indirectPublisherID,
    );
}

sub _getStatementTransactionItemsByStatementID {
    my ( $self, $statementID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementTransaction->GetByMechanicalStatementID($statementID);
}

sub _getStatementPublisherTransactionItems {
    my ( $self, $statementID, $publisherID ) = @_;

    return RPS::DB::Item::CAMechanicalStatementPublisherTransaction->GetByMechanicalStatementAndPublisherID( $statementID, $publisherID );
}

sub _publisherIDFromStatementTrackItem {
    my ( $self, $trackItem ) = @_;
    return $trackItem->ca_publisher_id;
}

sub _publisherIDFromTrackLicense {
    my ( $self, $trackLicense ) = @_;
    return $trackLicense->ca_publisher_id;
}

sub _publisherIDFromStatementItem {
    my ( $self, $statement ) = @_;
    return $statement->ca_publisher_id();
}

sub _publisherIDFromStatementPublisher {
    my ( $self, $statementPublisher ) = @_;
    return $statementPublisher->ca_publisher_id();
}

sub _idFromTrackLicense {
    my ( $self, $trackLicense ) = @_;
    return $trackLicense->ca_track_license_id();
}

sub _idFromStatementItem {
    my ( $self, $statementItem ) = @_;
    return $statementItem->ca_mechanical_statement_item_id();
}

sub _idFromStatement {
    my ( $self, $statement ) = @_;
    return $statement->ca_mechanical_statement_id();
}

sub _publisherIDFromStatement {
    my ( $self, $statement ) = @_;
    return $statement->ca_publisher_id();
}

sub _idFromStatementLicense {
    my ( $self, $statementLicense ) = @_;
    return $statementLicense->ca_mechanical_statement_license_id();
}

sub _lookupPublisherTrackCrossedLicenseAccount {
    my ( $self, $publisherID, $trackID, $payorID ) = @_;

    return RPS::DB::Item::CAPublisherTrackCrossedLicenseAccount->Lookup(
        ca_publisher_id => $publisherID,
        track_id        => $trackID,
        payor_id        => $payorID,
    );
}

sub _createPublisherTrackCrossedLicenseAccount {
    my ( $self, $publisherID, $trackID, $payorID, $accountID ) = @_;

    return RPS::DB::Item::CAPublisherTrackCrossedLicenseAccount->Create(
        ca_publisher_id    => $publisherID,
        track_id           => $trackID,
        payor_id           => $payorID,
        finance_account_id => $accountID,
    );
}

sub _getStatementCrossedLicenseTransactions {
    my ( $self, $statementID, $publisherID, $trackID ) = @_;

    return RPS::DB::Item::CAMechanicalStatementCrossedLicenseTransaction->GetByMechanicalStatementIDPublisherIDTrackID( $statementID,
        $publisherID, $trackID );
}

sub _idFromStatementLicenseItem {
    my ( $self, $licenseItem ) = @_;
    return $licenseItem->ca_mechanical_statement_license_id();
}

sub _trackLicenseIDFromStatementLicenseItem {
    my ( $self, $licenseItem ) = @_;
    return $licenseItem->ca_track_license_id();
}

sub _getStatementLicenseTransactionsByStatementLicenseID {
    my ( $self, $statementLicenseID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementLicenseTransaction->GetByMechanicalStatementLicenseID($statementLicenseID);
}

sub _createStatementLicenseTransaction {
    my ( $self, $licenseItemID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementLicenseTransaction->Create( 'ca_mechanical_statement_license_id' => $licenseItemID, );
}

sub _getAllCommittedLicenseReservesByTrackLicenseID {
    my ( $self, $trackLicenseID ) = @_;
    return RPS::DB::Item::CALicenseReserve->GetAllCommittedByTrackLicenseID($trackLicenseID);
}

sub _createLicenseReserveRunItem {
    my ( $self, $reserve ) = @_;
    return RPS::DB::Item::CALicenseReserveRun->Create(
        ca_license_reserve_id => $reserve->ca_license_reserve_id,
        ca_mechanical_run_id  => $self->_runID(),
    );
}

# !!! I am NOT going to implement this.
# !!! We don't want to have _any_ carryover in canadian mechanicals.
#
sub _getAllMechanicalCarryoverByTrackLicenseID {
}

#
#sub _createMechanicalRunCarryover
#{
#    my ($self, $carryover) = @_;
#
#    my $mechanicalRunCarryoverItem = RPS::DB::Item::MechanicalRunCarryover->Create
#    (
#        mechanical_carryover_id => $carryover->mechanical_carryover_id,
#        mechanical_run_id => $self->_runID(),
#    );
#}

sub _getLicensesForPublisherStatementWithTrackAndRegion {
    my ( $self, $trackID, $regionID, $startDate, $endDate, $publisherID ) = @_;
    return RPS::DB::Item::CATrackLicense->GetLicensesForPublisherStatementWithTrackAndRegion( $trackID, $regionID, $startDate, $endDate,
        $publisherID, $self->_payorID() );
}

sub _getPaidSaleRunMapForLicense {
    my ( $self, $license ) = @_;

    return RPS::DB::Item::SaleRunMap->GetPaidCAMechanicalsWithTrackLicense( $license->ca_track_license_id );
}

sub _lookupMechanicalStatementItem {
    my ( $self, $statementItemID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementItem->Lookup( ca_mechanical_statement_item_id => $statementItemID );
}

sub _getLastStatementAdjustmentItem {
    my ( $self, $statementItemID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementAdjustmentItem->GetLastStatementAdjustment($statementItemID);
}

sub _idFromStatementAdjustmentItem {
    my ( $self, $lastAdjustment ) = @_;
    return $lastAdjustment->ca_mechanical_statement_adjustment_item_id;
}

sub _idFromRunItem {
    my ( $self, $run ) = @_;
    return $run->ca_mechanical_run_id;
}

sub _getStatementPublisherFromAccount {
    my ( $self, $account ) = @_;
    my $publisherID = $account->ca_publisher_id;
    my $publisher;

    my $statementPublishers =
      RPS::DB::Item::CAMechanicalStatementPublisher->GetByMechanicalStatementAndPublisherID( $self->_statementID(), $publisherID );
    while ( my $statementPublisher = $statementPublishers->next() ) {
        $publisher = $statementPublisher;
    }

    if ( !$publisher ) {
        $publisher = $self->_createStatementPublisher($publisherID);
    }

    return $publisher;
}

sub _getSubPublisherIDs {
    my ( $self, $statementID ) = @_;

    #   my @ids = RPS::DB::Item::CAMechanicalStatementPublisherTransaction->GetSubPublisherIDs($statementID);
    my @ids;
    my $allPublishers = RPS::DB::Item::CAMechanicalStatementPublisher->GetByMechanicalStatementID($statementID);
    while ( my $p = $allPublishers->next() ) {
        push @ids, $p->ca_publisher_id;
    }

    return @ids;
}

sub _getPublisherStatement {
    my ( $self, $statementID, $publisherID ) = @_;

    my $publisherStatement;

    $publisherStatement =
      RPS::DB::Item::CAMechanicalStatementPublisher->Lookup( ca_mechanical_statement_id => $statementID, ca_publisher_id => $publisherID );

    return $publisherStatement;
}

sub _getIndirectAccountByPayorIndirectPublisherID {
    my ( $self, $payorID, $publisherID ) = @_;
    my $indirectAccounts = RPS::DB::Item::CAPublisherIndirectBalanceAccount->GetByPayorIndirectPublisherID( $payorID, $publisherID );

    return $indirectAccounts;
}

sub _createStatementAdjustmentItem {
    my ( $self, $license, $originalStatementItemID, $lastAdjustmentID, $adjustmentAmount, $total ) = @_;

    return RPS::DB::Item::CAMechanicalStatementAdjustmentItem->Create(
        ca_mechanical_statement_id                          => $self->_statementID(),
        ca_track_license_id                                 => $license->ca_track_license_id(),
        original_ca_mechanical_statement_item_id            => $originalStatementItemID,
        previous_ca_mechanical_statement_adjustment_item_id => $lastAdjustmentID,
        amount                                              => $adjustmentAmount,
        new_total                                           => $total,
        adjustment_type_code =>
          RPS::DB::Item::CAMechanicalStatementAdjustmentItem::kAdjustmentTypeCCompNewLicense,    #!!! is this really accurate?
    );
}

sub _createStatementPublisher {
    my ( $self, $publisherID ) = @_;
    return RPS::DB::Item::CAMechanicalStatementPublisher->Create(
        ca_mechanical_statement_id => $self->_statementID(),
        ca_publisher_id            => $publisherID,
    );
}

sub _createStatementTrack {
    my ( $self, $publisherID, $trackID, $albumID, $songTitle ) = @_;
    return RPS::DB::Item::CAMechanicalStatementTrack->Create(
        ca_mechanical_statement_id => $self->_statementID(),
        ca_publisher_id            => $publisherID,
        track_id                   => $trackID,
        album_id                   => $albumID,
        song_title                 => $songTitle,
    );
}

sub _createStatementCrossedLicenseTransaction {
    my ( $self, $statementLicense, $publisherID, $trackID, $pendingTrans ) = @_;

    return RPS::DB::Item::CAMechanicalStatementCrossedLicenseTransaction->Create(

        #ca_mechanical_statement_license_id => $statementLicense->ca_mechanical_statement_license_id,
        ca_mechanical_statement_id => $self->_statementID(),
        ca_publisher_id            => $publisherID,
        track_id                   => $trackID,
        amount                     => $pendingTrans->amount,
        memo                       => $pendingTrans->memo,
        check_number               => $pendingTrans->check_number,
        pending_transaction_id     => $pendingTrans->pending_transaction_id,
        transaction_date           => $pendingTrans->transaction_date,
        type_code                  => $pendingTrans->type_code,
    );
}

sub _createStatementTransactionFromPendingTransaction {
    my ( $self, $pendingTrans ) = @_;
    return RPS::DB::Item::CAMechanicalStatementTransaction->Create(
        ca_mechanical_statement_id => $self->_statementID(),
        amount                     => $pendingTrans->amount(),
        memo                       => $pendingTrans->memo(),
        check_number               => $pendingTrans->check_number(),
        pending_transaction_id     => $pendingTrans->pending_transaction_id(),
        transaction_date           => $pendingTrans->transaction_date(),
        type_code                  => $pendingTrans->type_code(),
    );
}

sub _createPublisherStatementTransactionFromPendingTransaction {
    my ( $self, $pendingTrans, $account ) = @_;
    return RPS::DB::Item::CAMechanicalStatementPublisherTransaction->Create(
        ca_mechanical_statement_id => $self->_statementID(),
        ca_publisher_id            => $account->ca_publisher_id(),
        amount                     => $pendingTrans->amount(),
        memo                       => $pendingTrans->memo(),
        check_number               => $pendingTrans->check_number(),
        pending_transaction_id     => $pendingTrans->pending_transaction_id(),
        transaction_date           => $pendingTrans->transaction_date(),
        type_code                  => $pendingTrans->type_code(),
    );
}

sub _getLicensesByTrackIDAndDate {
    my ( $self, $trackID, $startDate, $endDate ) = @_;
    return RPS::DB::Item::CATrackLicense->GetLicensesByTrackIDAndDate( $trackID, $startDate, $endDate );
}

sub _getLicensesByTrackID {
    my ( $self, $trackID ) = @_;
    return RPS::DB::Item::CATrackLicense->GetLicensesByTrackID($trackID);
}

sub _testAlreadyPaidSaleWithLicense {
    my ( $self, $sale, $trackLicense ) = @_;

    return RPS::DB::Item::SaleRunMap->HavePaidCAMechanicalsForSaleWithTrackLicense( $sale->sale_id, $trackLicense->ca_track_license_id );
}

sub _getStatementDBItem {
    my ($self) = @_;

    my $statement = RPS::DB::Item::CAMechanicalStatement->Lookup( ca_mechanical_statement_id => $self->{_statementID} );
    return $statement;
}

sub _runType {
    my ($self) = @_;
    return RPS::DB::Item::SaleRunMap::kRunTypeCAMechanical;
}

sub _getRunDBItem {
    my ($self) = @_;

    my $statement = $self->_getStatementDBItem();
    my $runID     = $statement->ca_mechanical_run_id;
    my $run       = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $runID );
    return $run;
}

sub _getSaleIDs {
    my ($self) = @_;

    # Read the sales to process from the SalePublisherMap table
    # !!! Actually, I am going to put this interface into Raptor::Sale, so it makes more sense.
    #
    my $statement = $self->_getStatementDBItem();

    return RPS::DB::Item::SaleCAPublisherMap->GetPublisherMappedSaleIDs( $statement->ca_publisher_id, $statement->ca_mechanical_run_id );
}

sub _getControlledCompContractByAlbumID {
    my ( $self, $albumID ) = @_;
    return RPS::DB::Item::AlbumContract->GetCACComp($albumID);
}

sub _trackIsExemptFromMechanicals {
    my ( $self, $trackID ) = @_;

    my $track = $self->_getTrack($trackID);
    return $track->ca_mechanical_exempt;
}

sub _findMatchingLicensesFast {
    my ( $self, $sale ) = @_; 
    return $self->_findMatchingLicenses( $sale );
}

sub _readSaleLicenseMap {
    my ( $self, $saleIDs ) = @_; 
    $self->_report( "RSD7859 Not implemented for CA mechanicals");
} # _readSaleLicenseMap

sub _readSalePublisherLicenseMap {
    my ( $self, $statement ) = @_; 
    $self->_report( "RSD7859 Not implemented for CA mechanicals");
} # _readSalePublisherLicenseMap

sub _isASubPublisher {
    my ( $self, $statement ) = @_; 
    my $o = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $statement->ca_publisher_id );
    return  1 if ( $o->admin_id || $o->agent_id );
}

1;
