#!/usr/bin/perl
use strict;

use Data::Dumper;
use Getopt::Std;
use POSIX qw(ceil);
use POSIX ":sys_wait_h";
use Sys::Hostname;
use Carp;

use lib '/app/tools/common/lib';
use Common::DB::Item;
use Common::Assert;
use Common::TextProgressBar;
use Common::Timer;
use Common::DB::Item::Service;

#use lib '/app/tools/data_classes/lib';
#use File::Sale;
use lib '/app/tools/raptor/lib';

#use Raptor::Sale::Sale;
use Raptor::DB::Item::Sale;
use Raptor::DB::Item::File;

use lib '/app/tools/rps/lib';
use RPS::DB::Item::ArtistPayeeAccount;
use RPS::DB::Item::ArtistRoyaltyAlbum;
use RPS::DB::Item::ArtistRoyaltyRun;
use RPS::DB::Item::ArtistRoyaltyStatement;
use RPS::DB::Item::FinanceAccount;
use RPS::DB::Item::FinanceTransaction;
use RPS::DB::Item::ArtistRoyaltyAlbumBalanceAccount;

my $gVerbosityLevel = 1;

# Parse the command-line options.
#
my %options;
_parseCommandLine( \%options );

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

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

    _report("\n---- Converting run $runID");

    my $statements = RPS::DB::Item::ArtistRoyaltyStatement::GetByArtistRoyaltyRunID($runID);
    while ( my $statement = $statements->next() ) {
        _fixStatement($statement);
    }
}

sub _fixStatement {
    my ($statement) = @_;
    _report( "  statement: " . $statement->artist_royalty_statement_id, 2 );

    my $artistPayeeID  = $statement->payee_id;
    my $statementTotal = $statement->total;
    my $amountDue      = $statement->amount_due;

    my $financeAccountID = _artistPayeeAccountID( $artistPayeeID, $statement->payor_id );

    #    my $expectedTransactionAmount = ($amountDue > 0 ? $amountDue : $statementTotal);
    my $expectedTransactionAmount = $statementTotal;

    # If the expected amount is 0, then there shouldn't be any transactions?
    # Or, there should just be 1 transaction that established the 'previous balance'.
    #
    if ( 0 == $expectedTransactionAmount ) {

        # We might end up here if there was an amount due that paid off the entire previous balance.
        # In that case, we want to roll back that transaction, and force them to make that payment the
        # next time we run.
        #
        my $lastTransaction = RPS::DB::Item::FinanceTransaction::GetLastAccountTransaction($financeAccountID);
        my $lastTransactionAmount;
        if ($lastTransaction) {
            $lastTransactionAmount = $lastTransaction->amount;
        }

        my $expectedMemo = "artist statement " . $statement->artist_royalty_statement_id;
        if ( $lastTransaction && $lastTransaction->memo eq $expectedMemo ) {
            _report("   going to eliminate the last transaction");
            $lastTransaction->delete();
        }

    } else {

        # grab the last transaction.
        #
        if ( !$financeAccountID ) {

            # If there is no account, then probably we need to create one:  Let's see if there was an expected amount.
            #
            if ( 0 != $expectedTransactionAmount ) {
                _report("artist $artistPayeeID will need whole new account, with an amount of $expectedTransactionAmount");
                my $newAccount = RPS::DB::Item::FinanceAccount->Create(
                    description => 'account for artist payee ' . $artistPayeeID,
                    type_code   => 1,
                );
                $newAccount->save();

                $financeAccountID = $newAccount->finance_account_id;

                # Should already be an account map, just with no account id.
                #
                my $accountMap = RPS::DB::Item::ArtistPayeeAccount->Lookup(
                    artist_payee_id => $artistPayeeID,
                    payor_id        => $statement->payor_id,
                );
                if ( !$accountMap ) {
                    _report("!!!! No account map created!");
                }
                $accountMap->finance_account_id($financeAccountID);
                $accountMap->save();
            }
        }

        my $lastTransaction = RPS::DB::Item::FinanceTransaction::GetLastAccountTransaction($financeAccountID);
        my $lastTransactionAmount;
        if ($lastTransaction) {
            $lastTransactionAmount = $lastTransaction->amount;
        }

        # !!! sanity check - should be a 'artist statement X' transaction
        #
        my $expectedMemo = "artist statement " . $statement->artist_royalty_statement_id;
        if ( $lastTransaction && $lastTransaction->memo ne $expectedMemo ) {
            _report( " !!! for artist $artistPayeeID, last transaction does not appear to be correct! " . Dumper($lastTransaction) );
            return;
        }

        # Ok, so now we can see whether all is well.
        #
        if ( $lastTransactionAmount != $expectedTransactionAmount ) {
            _report(" !!! artist $artistPayeeID, last transaction amount of $lastTransactionAmount != expected $expectedTransactionAmount");

            # So, this would usually mean that I need to delete the last transaction, and re-post
            #
            $lastTransaction->delete() if $lastTransaction;
            my $newTransaction = RPS::DB::Item::FinanceTransaction->Create(
                finance_account_id => $financeAccountID,
                amount             => $expectedTransactionAmount,
                memo               => $expectedMemo,
                type_code          => RPS::DB::Item::FinanceTransaction::kTypeAdjustment,
            );
            $newTransaction->save();
        }
    }
}

sub _artistPayeeAccountID {
    my ( $payeeID, $payorID ) = @_;

    my $accountMap = RPS::DB::Item::ArtistPayeeAccount->Lookup(
        artist_payee_id => $payeeID,
        payor_id        => $payorID,
    );
    return undef unless defined $accountMap;
    return $accountMap->finance_account_id;
}

sub _usage {
    print "\nusage: $0 -c client_id [-V]\n";
    print "\n";
    print "Arguments:\n";
    print "\t-c <client_id>\t\tThe client_id of the client to process\n";
    print "\t-V <N>\t\t\tVerbosity level - 0 means no output\n";
}

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

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

# Pass dates in 'yyyy-mm-dd' format.
#
sub _parseCommandLine {
    my ($settings) = @_;

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

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

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