#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
use strict;

use Data::Dumper;
use Getopt::Std;
use POSIX qw(ceil);
use Carp;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Client;
use Common::RSApp;

use lib '/app/tools/rps/lib';
use RPS::LabelRoyalty::Job::CreatePDFStatements;
use RPS::RoyaltyRun::Status;
use RPS::Statement::Label::PDF;
use RPS::Statement::Label::Report;
use RPS::DB::Item::LabelPayee;
use RPS::DB::Item::LabelPayeeAccount;
use RPS::DB::Item::LabelRoyaltyLabelItem;
use RPS::DB::Item::LabelRoyaltyTransactionItem;
use RPS::DB::Item::PendingTransaction;
use RPS::DB::Item::LabelRoyaltyRun;
use RPS::DB::Item::LabelRoyaltyStatement;
use RPS::DB::Item::Expense;
use RPS::DB::Item::FinanceAccount;
use RPS::DB::Item::FinanceTransaction;
use RPS::DB::Item::SaleRunMap;

use lib '/app/tools/raptor/lib';
use Raptor::DB::Item::Sale;

my $gVerbosityLevel = 1;
my $gRunLabel;

# Get the id from the command-line
#
my %options;
_parseCommandLine( \%options );
_commitRun( $options{clientID}, $options{runID} );

sub _parseCommandLine {
    my ($settings) = @_;

    my %opt;
    getopts( 'r:c:V:', \%opt );

    if ( !$opt{r} || !$opt{c} ) {
        _usage();
        exit(1);
    }
    $settings->{runID}    = $opt{r};
    $settings->{clientID} = $opt{c};

    if ( defined $opt{V} ) {
        $gVerbosityLevel = $opt{V};
    }
}

sub _usage {
    print "\nusage: $0 -c <client_id> -r <label_royalty_run_id>\n";
    print "\n";
    print "Arguments:\n";
    print "\t-c <client_id>\t\tThe client_id of the client to process\n";
    print "\t-r <label_royalty_run_id>\t\tThe id of the royalty run to commit\n";
}

sub _commitRun {
    my ( $clientID, $runID ) = @_;

    # Instantiate the application singleton object.
    #
    my $appSingleton = Common::RSApp->new( clientID => $clientID );

    # Get the run state, and make sure it isn't already committed.
    #
    my $run = RPS::DB::Item::LabelRoyaltyRun->Lookup( label_royalty_run_id => $runID );
    die "royalty run $runID does not exist!" unless $run;

    if ( RPS::RoyaltyRun::Status::kWaitingToCommit != $run->status ) {
        die "run $runID is not in the 'waiting to commit' state";
    }

    _report("*** Committing Label Royalty Run $runID ***\n");
    $gRunLabel = $run->label;

    # Fetch all the statements in this run, and commit them.
    #
    my $statements = RPS::DB::Item::LabelRoyaltyStatement->GetByLabelRoyaltyRunID($runID);

    _report( "statements to commit: " . $statements->size(), 2 );
    while ( $statements->hasNext() ) {
        my $statement = $statements->next();
        _commitStatement($statement);
    }

    # Tag all sales processed this run as 'processed'.
    #
    my $saleMapItems = RPS::DB::Item::SaleRunMap->GetPaidByLabelRoyaltyRunID($runID);
    if ($saleMapItems) {
        _report( "marking " . $saleMapItems->size() . " sales as processed", 2 );
        while ( my $saleMapItem = $saleMapItems->next() ) {
            my $sale = Raptor::DB::Item::Sale->Lookup( sale_id => $saleMapItem->sale_id );
            $sale->label_royalty_status(Raptor::DB::Item::Sale::kProcessed);
            $sale->save();
        }
    }

    # Mark this run as 'committed'
    #
    _report("setting run status to committed");
    $run->status(RPS::RoyaltyRun::Status::kCommitted);
    $run->save();

    # Mark any previous 'committed' runs as 'closed', if they have the same payor profile.
    #
    my $payorIDProfile = _getRunPayorIDProfile($runID);

    my $allRuns = RPS::DB::Item::LabelRoyaltyRun->GetAll();
    while ( my $oldRun = $allRuns->next() ) {
        my $oldRunID = $oldRun->label_royalty_run_id;
        next if ( $runID == $oldRunID );
        next unless ( RPS::RoyaltyRun::Status::kCommitted == $oldRun->status() );

        my $oldProfile = _getRunPayorIDProfile($oldRunID);
        if ( $oldProfile eq $payorIDProfile ) {
            _report("setting status of previous run $oldRunID to Closed");
            $oldRun->status(RPS::RoyaltyRun::Status::kClosed);
            $oldRun->save();
        }
    }

    # Re-generate the pdf statements, to remove the 'DRAFT' watermark.
    #
    my $statementsPath = RPS::Statement::Label::PDF::StatementPathFromRunID( $runID, $clientID );
    if ( !-d $statementsPath ) {
        mkpath($statementsPath) or die "ERROR: Unable to create path $statementsPath: $!\n";
    }

    # !!! I don't think we need to nuke the old files - They should be overwritten anyway.
    #
    #    if (-d $statementsPath)
    #    {
    #        system("rm -rf $statementsPath/*.pdf");
    #    }

    my $jobArgs = RPS::LabelRoyalty::Job::CreatePDFStatements->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $statementsPath,
    );
    my $job = $jobArgs->enqueue();

    _report("done");
}

sub _getRunPayorIDProfile {
    my ($runID) = @_;

    my @payorIDs = RPS::DB::Item::LabelRoyaltyStatement->GetDistinctPayorsByRunID($runID);
    @payorIDs = sort @payorIDs;
    my $profile = join( ',', @payorIDs );
    return $profile;
}

sub _commitStatement {
    my ($statement) = @_;

    # Fetch the default currency code to use for new account.
    #
    my $defaultCurrencyCode = Common::Client::Current()->Locale()->currencyFormat()->currencyCode();

    my $statementID = $statement->label_royalty_statement_id;
    my $payorID     = $statement->payor_id;

    _report( "\n------------------------------------\n", 2 );

    _report( "committing statement id $statementID", 2 );

    # Handle the holdover account
    #
    my $total        = $statement->total;
    my $labelPayeeID = $statement->payee_id;

    my $payeeAccountMap = RPS::DB::Item::LabelPayeeAccount->Lookup( label_payee_id => $labelPayeeID, payor_id => $payorID );
    if ( !$payeeAccountMap ) {
        $payeeAccountMap = RPS::DB::Item::LabelPayeeAccount->Create(
            label_payee_id => $labelPayeeID,
            payor_id       => $payorID,

            # !!! Will need to account for currency_code
        );
        $payeeAccountMap->save();
    }

    my $payeeAccountID = $payeeAccountMap->finance_account_id;
    if ( !$payeeAccountID ) {
        my $account = RPS::DB::Item::FinanceAccount->Create(
            description   => "advance account for label payee $labelPayeeID",
            type_code     => RPS::DB::Item::FinanceAccount::kAccountTypeAdvance,
            currency_code => $defaultCurrencyCode,
        );
        $account->save();
        $payeeAccountID = $account->finance_account_id;
        _report( "created new account, id $payeeAccountID", 2 );

        # Save the account id in the mapping table.
        #
        $payeeAccountMap->finance_account_id($payeeAccountID);
        $payeeAccountMap->save();
    }

    # Apply the royalties total as an adjustment
    #
    my $statementSubtotal = $statement->total;
    if ( $statementSubtotal < 0 or $statementSubtotal > 0 ) {
        _report( "adjusting balance by total amount of $statementSubtotal", 2 );
        my $transaction = RPS::DB::Item::FinanceTransaction->Create(
            finance_account_id => $payeeAccountID,
            amount             => $statementSubtotal,
            type_code          => RPS::DB::Item::FinanceTransaction::kTypeClosingBalance,
            memo               => "label royalty run $gRunLabel",
            currency_code      => $defaultCurrencyCode,

            # currency_code?
        );
        $transaction->save();
    }

    # Now apply all pending transactions, and delete them.
    #
    my $transactionItems = RPS::DB::Item::LabelRoyaltyTransactionItem->GetByLabelRoyaltyStatementID($statementID);
    while ( my $item = $transactionItems->next() ) {

        # Fetch the 'real' pending transaction.
        #
        my $pending = RPS::DB::Item::PendingTransaction->Lookup( pending_transaction_id => $item->pending_transaction_id );

        # !!! Note that amount == amount. no sign flipping.  We have to get the sign right when we create the
        # !!! pending transaction in the first place.
        #
        _report( "applying pending transaction id " . $pending->pending_transaction_id . " amount of " . $pending->amount(), 2 );
        my $transaction = RPS::DB::Item::FinanceTransaction->Create(
            finance_account_id => $pending->finance_account_id,
            amount             => $pending->amount,
            type_code          => $pending->type_code,
            memo               => $pending->memo,
            check_number       => $pending->check_number,
            currency_code      => $pending->currency_code,
        );
        $transaction->save();

        $pending->delete();
    }
}

sub _report {
    my ( $string, $verbosity ) = @_;
    $verbosity = 1 unless defined $verbosity;

    if ( $gVerbosityLevel >= $verbosity ) {
        print $string . "\n";
    }
}

