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

package RPS::ArtistRoyalty::Fast::Script::RunArtistRoyalties;

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

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

use DB_File;

use Common::Log;
use Common::DB::AutoLock;

use Common::RSApp;
use RPS::DB::Item::ArtistRoyaltyRun;
use RPS::DB::Item::ClientOptions;
use RPS::RoyaltyRun::Status;
use RPS::ArtistRoyalty::Job::CreateReservePipelineReport;
use RPS::ArtistRoyalty::Job::CreatePDFStatements;
use RPS::ArtistRoyalty::Job::CreateExcelStatements;
use RPS::Statement::Artist::PDF;
use RPS::ArtistRoyalty::Job::CreateCurrentPeriodExportReportText;
use RPS::ArtistRoyalty::Job::CreateCurrentPeriodExportReportExcel;
use RPS::ArtistRoyalty::Job::CreateAlbumBalancesExportReportText;
use RPS::ArtistRoyalty::Job::CreateAlbumBalancesExportReportExcel;
use RPS::ArtistRoyalty::Job::CreateLicenseIncomePayableAuditReport;

use base 'Common::Script';
use constant kLockTable => "artist_royalty_assemble_statements_semaphore";

sub _options {
    {
        client_id => {
            short       => 'c',
            required    => 1,
            description => 'Limit to this client id',
            parameter   => 'i'
        },
        run_id => {
            short       => 'r',
            required    => 1,
            description => 'The artist_royalty_run_id we are creating statements for.',
            parameter   => 'i',
        },
        verbosity => {
            short       => 'V',
            required    => 0,
            description => 'Specify log level, old style',
            parameter   => 'i',
        },
    };
}

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

    my $runID = $self->param('run_id');

    Log->warn("Beginning royalty run $runID");

    # Read in the run record, so we can get the correct payor_id.
    #
    my $runData = RPS::DB::Item::ArtistRoyaltyRun->Lookup( artist_royalty_run_id => $runID );

    # JPK - For now, we'll assume that we're going to run everything on a single host.
    #

    # !!! These values should probably come from the config file system, but for the moment
    # !!! I will hard-code them.
    #
    my $staticDataPath = "/app/data/artist_royalty_run/" . Common::RSApp::GetClientID() . "/" . $runID;
    my $outputDataPath = $staticDataPath . "/output";

    if( -e $staticDataPath ) {
        my $msg = "Detected pre-existing run directory for run $runID";
        $runData->end_time(Common::DB::Item::kDateTimeNow);
        $runData->status(RPS::RoyaltyRun::Status::kError);
        $runData->error($msg);
        Log->crit($msg);
        $runData->save();
        Log->warn("Aborted royalty run $runID");
        die;
    }

    # Check for duplicated payee/payor accounts
    #
    my $dbo = Common::RSApp::GetClientDB();
    my $sql = qq/
    SELECT
      artist_payee_id,
      payor_id,
      COUNT(*)
    FROM artist_payee_account
    GROUP BY artist_payee_id, payor_id
    HAVING COUNT(*) > 1
    /;
    my $sth = $dbo->DoCmd($sql);

    if ( $sth->rows > 0 ) {
        while ( my( $payeeID, $payorID, $nRows ) = $sth->fetchrow_array() ) {
            Log->crit("Detected $nRows duplicate artist_payee_account for payee $payeeID, payor $payorID");
        }
        my $msg = "Detected duplicate artist_payee_account";
        $runData->end_time(Common::DB::Item::kDateTimeNow);
        $runData->status(RPS::RoyaltyRun::Status::kError);
        $runData->error($msg);
        Log->crit($msg);
        $runData->save();
        Log->warn("Aborted royalty run $runID");
        die;
    }

    eval { $self->_runRoyalties( $runData, $staticDataPath, $outputDataPath ); };
    if ($@) {
        $runData->end_time(Common::DB::Item::kDateTimeNow);
        $runData->status(RPS::RoyaltyRun::Status::kError);
        $runData->error($@);
        Log->crit($@);
        $runData->save();
        die;
    }

    Log->warn("Finished royalty run $runID");
}

sub _runRoyalties {
    my ( $self, $runData, $staticPath, $outputPath ) = @_;

    my $binDir = '/app/tools/rps/lib/RPS/ArtistRoyalty/Fast/bin';

    $runData->status(RPS::RoyaltyRun::Status::kRunning);
    $runData->start_time(Common::DB::Item::kDateTimeNow);
    $runData->pid($$);
    $runData->save();

    my $runID    = $runData->artist_royalty_run_id();
    my $payorID  = $runData->payor_id();
    my $clientID = Common::RSApp::GetClientID();

    # JPK - This should probably be a config file item.
    # Let's set it relatively low to avoid too much contention.
    #
    my $salesMapProcessCount = 12;

    # ES - Setup an alternate temporary directory for sort.
    #
    my $tmpPath = $staticPath . "/tmp";
    if ( !-d $tmpPath ) {
        mkpath($tmpPath);
    }

    my @commands = (
        "$binDir/stageData.pl -c $clientID -r $runID -d $staticPath",
        "$binDir/mapSales.pl  -c $clientID -y $payorID -d $staticPath -o $outputPath -p$salesMapProcessCount",
        "$binDir/mapReserves.pl -c $clientID -d $staticPath -o $outputPath",
        "$binDir/mapExpenses.pl -c $clientID -d $staticPath -o $outputPath",
        "$binDir/mapLicenseIncomeSales.pl  -c $clientID -d $staticPath -o $outputPath",
        "$binDir/mapPendingTransactions.pl  -c $clientID -d $staticPath -o $outputPath",
        "$binDir/mapPayees.pl  -c $clientID -d $staticPath -o $outputPath",
        "$binDir/mapAlbumContractAccounts.pl  -c $clientID -d $staticPath -o $outputPath",
        "sort -T $tmpPath -n -k1 -k2 -k3 -k4 -k5 -k6 -k7 -k8 -k9 -k10 -k11 -k12 -k13 $outputPath/mapped_items_* > $outputPath/mapped_items.sorted",
        "sort -T $tmpPath -n -t'\t' " . "-k1,1 -k2,2 -k3,3 -k4,4 -k5,5 -k6,6 -k7,7 " . "$outputPath/missed_sales_* > $outputPath/missed_sales.sorted",
    );

    foreach my $cmdString (@commands) {
        Log->warn("executing: $cmdString");
        my $status = system($cmdString);
        die "ERROR EXECUTING '$cmdString': ($?) $!" unless 0 == $status;
    }

    # These commands need to execute 'atomically' at the process level.  We are creating new database records, and will be making
    # certain assumptions about what table ids are available.   So we will use a lock on a special table as a semaphore so that only
    # one process (across the entire system) is allowed to do these at a time.
    #
    my @lockedCommands = (
        "$binDir/assembleStatements.pl -c $clientID -r $runID -d $staticPath -o $outputPath",
        "$binDir/assembleMissedSaleLog.pl -c $clientID -r $runID -d $staticPath -o $outputPath",
        "$binDir/commitFinalData.pl -c $clientID -r $runID -o $outputPath",
    );

    Log->warn( "ATTEMPTING TO GET TABLE LOCK ON TABLE " . kLockTable );
    my $lock = Common::DB::AutoLock->new( Common::RSApp::GetClientDB(), kLockTable );
    Log->warn("  OBTAINED THE LOCK");
    foreach my $cmdString (@lockedCommands) {
        Log->warn("executing: $cmdString");
        my $status = system($cmdString);
        die "ERROR EXECUTING '$cmdString': ($?) $!" unless 0 == $status;
    }
    Log->warn("   RELEASING THE LOCK");
    $lock = undef;

    # Fill in some columns in the artist_royalty_statement table for some clients
    my $customStatementSummary = RPS::DB::Item::ClientOptions->Get('custom_statement_summary');

    if ( $customStatementSummary ) {
        Log->warn("Populating custom statement summary fields");
        my $cmdString = "$binDir/customStatementSummary.pl -c $clientID -r $runID";
        my $status = system($cmdString);
        die "ERROR EXECUTING '$cmdString': ($?) $!" unless 0 == $status;
    } else {
        Log->warn("Skipping custom statement summary fields");
    }

    $runData->status(RPS::RoyaltyRun::Status::kComplete);
    $runData->end_time(Common::DB::Item::kDateTimeNow);
    $runData->save();

    # Things to do at the end:
    # - Create reserve pipeline report
    # - Create PDF and excel statements
    # - Create export reports
    #
    my $jobArgs = RPS::ArtistRoyalty::Job::CreateReservePipelineReport->new(
        runID    => $runID,
        clientID => $clientID,
    );
    my $job = $jobArgs->enqueue();
    Log->warn( "added reserve pipeline job to queue, job id: " . $job->id() );

    # Make sure the path exists
    #
    my $filePath = RPS::Statement::Artist::PDF::StatementPathFromRunID($runID);
    if ( !-d $filePath ) {
        mkpath($filePath) or die("Unable to create path $filePath: $!");
    }

    # Create the statements.
    #
    $jobArgs = RPS::ArtistRoyalty::Job::CreatePDFStatements->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();
    Log->warn( "added PDF statement job to queue, job id: " . $job->id() );

    $jobArgs = RPS::ArtistRoyalty::Job::CreateExcelStatements->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();
    Log->warn( "added Excel statement job to queue, job id: " . $job->id() );

    # Create the export reports.
    #
    $jobArgs = RPS::ArtistRoyalty::Job::CreateCurrentPeriodExportReportText->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    $jobArgs = RPS::ArtistRoyalty::Job::CreateCurrentPeriodExportReportExcel->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    $jobArgs = RPS::ArtistRoyalty::Job::CreateAlbumBalancesExportReportText->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    $jobArgs = RPS::ArtistRoyalty::Job::CreateAlbumBalancesExportReportExcel->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    # License Income Payable audit report
    $jobArgs = RPS::ArtistRoyalty::Job::CreateLicenseIncomePayableAuditReport->new(
        runID    => $runID,
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();
}

1;
