#!/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";
    }
}


