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

# This class encapsulates the 'master process' for a UK Mechanicals run.
# In here, we'll set up the database for the run, then spawn individual
# processes for each statement.
#
# So the theory here is that we'll be able to take advantage of horizontal
# scalable resources if we break a run into a series of smaller jobs, rather
# than 1 humonguous run.
# This will also give us the ability to run a single statement at a time.
# The downside is that every process will end up iterating over the sale table,
# which might be a bottleneck.  Might be some way we can fix that - Perhaps
# we can craft a query into a 'temporary' table to pre-divvy the sale ids?
#
package RPS::Mechanical::UK::Process::RunController;
use strict;
use Data::Dumper;
use File::Path;

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

use lib '/app/tools/rps/lib';
use RPS::DB::Item::UKMechanicalRun;
use RPS::DB::Item::McpsStatement;
use RPS::DB::Item::McpsLicenseRun;
use RPS::DB::Item::McpsLicenseRetention;
use RPS::DB::Item::McpsLicense;
use RPS::DB::Item::ReportQueries::UKMechRetentionRunSummary;
use RPS::Mechanical::UK::Job::CreateStatement::AP1;
use RPS::Mechanical::UK::Job::CreateStatement::DVD1;
use RPS::Mechanical::UK::Job::CreateStatement::AVP;
use RPS::Mechanical::UK::Job::CreatePDFStatements;
use RPS::Statement::UKMechanical::McpsPDF;
use RPS::Mechanical::UK::Job::CreateMcpsTextStatement;
use RPS::Statement::UKMechanical::McpsText;
use RPS::Product::Price;

use RPS::Mechanical::UK::Process::CreateStatement::Mcps::AP1;
use RPS::Mechanical::UK::Process::CreateStatement::Mcps::DVD1;
use RPS::Mechanical::UK::Process::CreateStatement::Mcps::AVP;

use lib '/app/tools/job/lib';
use Job::Status;

use base 'RPS::Mechanical::Process::RunController';

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

    # !!! At the moment, we simply don't support direct statements for UK mechanicals.
    #     So we return an empty list.
    #
    my @ids;

    return @ids;
}

sub _getRunDBItem {
    my ($self) = @_;
    assert( $self->{_runID} );

    my $dbItem = RPS::DB::Item::UKMechanicalRun->Lookup( uk_mechanical_run_id => $self->{_runID} );
    return $dbItem;
}

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

    return 1;
}

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

    my $allLicenses = RPS::DB::Item::McpsLicenseRun->GetByRunID( $self->{_runID} );
    while ( my $licenseRunMap = $allLicenses->next() ) {
        my $licenseID = $licenseRunMap->mcps_license_id;
        my $license = RPS::DB::Item::McpsLicense->Lookup( mcps_license_id => $licenseID );

        # Delete any carryover associated with this license (making room for new entries, if any)
        #
        RPS::DB::Item::McpsLicenseCarryover->DeleteAllForLicense( $license->mcps_license_id );
    }
}

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

    # We don't delete/modify reserves the same way we do for US and CA mechanicals.
    #

    my $allLicenses = RPS::DB::Item::McpsLicenseRun->GetByRunID( $self->{_runID} );
    while ( my $licenseRunMap = $allLicenses->next() ) {
        my $licenseID = $licenseRunMap->mcps_license_id;
        my $license = RPS::DB::Item::McpsLicense->Lookup( mcps_license_id => $licenseID );

        # Find all the 'historical' retentions associated with this license,
        # and 'freeze' their price.
        #
        my $allRetentions = RPS::DB::Item::McpsLicenseRetention->GetByMcpsLicenseID($licenseID);
        while ( my $retention = $allRetentions->next() ) {
            if ( 0 == $retention->price() ) {
                my $price = RPS::Product::Price::GetWholesaleProductPrice( $license->product_id, $retention->price_level_id );
                $retention->price($price);
                $retention->save();
            }
        }
    }
}

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

    my $statementProcessor;
    if ( RPS::DB::Item::McpsStatement::kSchemeAP1 eq $statement->mcps_scheme ) {
        $statementProcessor =
          RPS::Mechanical::UK::Process::CreateStatement::Mcps::AP1->new( statementID => $statement->mcps_statement_id() );
    } elsif ( RPS::DB::Item::McpsStatement::kSchemeDVD1 eq $statement->mcps_scheme ) {
        $statementProcessor =
          RPS::Mechanical::UK::Process::CreateStatement::Mcps::DVD1->new( statementID => $statement->mcps_statement_id() );
    } elsif ( RPS::DB::Item::McpsStatement::kSchemeAVP eq $statement->mcps_scheme ) {
        $statementProcessor =
          RPS::Mechanical::UK::Process::CreateStatement::Mcps::AVP->new( statementID => $statement->mcps_statement_id() );
    }

    $statementProcessor->commit();
}

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

    my $statements = RPS::DB::Item::McpsStatement->GetByRunID( $self->{_runID} );
    return $statements;
}

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

    RPS::DB::Item::SaleRunMap->DeleteByUKMechanicalRunID( $self->{_runID} );
}

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

    my $logBasePath = RPS::Mechanical::UK::Job::Base::LogFilePath( Common::RSApp::GetClientID(), $self->{_runID} );
    return $logBasePath;
}

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

    # Delete from the McpsLicenseRun table, then invoke the inherited method
    # to finish the job.
    #
    RPS::DB::Item::McpsLicenseRun->DeleteByRunID( $self->{_runID} );

    $self->SUPER::delete();
}

sub _deleteStatement {
    my ( $self, $statement ) = @_;

    my $statementID = $statement->mcps_statement_id;

    $self->_report("deleting statement $statementID");
    $statement->delete();

    # Get rid of the sale/statement_item map entries
    #
    $self->_report( "  ... deleting statement items", 2 );
    RPS::DB::Item::McpsStatementItem->DeleteByMCPSStatementID($statementID);
}

sub _checkForFeatureFlags {
    my $self = shift;
    my $err  = 0;  # default to no error
    return $err;
}

sub _createSalePublisherLicenseEntry {
    my ( $self, $saleID, $publisherID, $trackLicenses ) = @_;
    $self->_report( " >> _createSalePublisherLicenseEntry not implemented for UK mechanicals", 2 );
}

sub _initializeSalePublisherLicenseMap {
    my ( $self ) = shift;

    $self->_report( " >> _initializeSalePublisherLicenseMap not implemented for UK mechanicals", 2 );
}

sub _clearLicenseCache {
    my ( $self ) = shift;
    $self->_report( " >> _clearLicenseCache not implemented for UK mechanicals", 2 );
}

sub _createSaleLicenseMapping {
    my ($self) = @_;
    $self->_report( " >> _createSaleLicenseMapping not implemented for UK mechanicals", 2 );
}

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

    my $statement = RPS::DB::Item::McpsStatement->Lookup( mcps_statement_id => $statementID );
    return $statement;
}

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

    # For now, we only have 3 statements - AP1,DVD1, and AVP.
    #
    $self->_addJobToJobList( 'AP1',  $self->_createAP1StatementJob() );
    $self->_addJobToJobList( 'DVD1', $self->_createDVD1StatementJob() );
    $self->_addJobToJobList( 'AVP',  $self->_createAVPStatementJob() );

    $self->SUPER::_createStatementsAndJobs();
}

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

    # The AP1 uses a 'normal' statement record.
    # But each sort of Job will have their own Job subclass.
    #

    my $statementObj = RPS::DB::Item::McpsStatement->Create(
        uk_mechanical_run_id => $self->{_runID},
        mcps_scheme          => RPS::DB::Item::McpsStatement::kSchemeAP1,
        status               => RPS::DB::Item::McpsStatement::kStatusNotQueued,
    );
    $statementObj->save();
    my $statementID = $statementObj->mcps_statement_id();

    # Create the Job object
    #
    my $job = RPS::Mechanical::UK::Job::CreateStatement::AP1->new(
        runID       => $self->{_runID},
        statementID => $statementID,
        logLevel    => $self->{_logLevel}
    );
    $job->enqueue();

    # Update the statement record with the job id, and
    # update the status to 'in queue'
    #
    $statementObj->job_id( $job->id() );
    $statementObj->status(RPS::DB::Item::McpsStatement::kStatusInQueue);
    $statementObj->save();

    return $job;
}

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

    # The AP1 uses a 'normal' statement record.
    # But each sort of Job will have their own Job subclass.
    #

    my $statementObj = RPS::DB::Item::McpsStatement->Create(
        uk_mechanical_run_id => $self->{_runID},
        mcps_scheme          => RPS::DB::Item::McpsStatement::kSchemeDVD1,
        status               => RPS::DB::Item::McpsStatement::kStatusNotQueued,
    );
    $statementObj->save();
    my $statementID = $statementObj->mcps_statement_id();

    # Create the Job object
    #
    my $job = RPS::Mechanical::UK::Job::CreateStatement::DVD1->new(
        runID       => $self->{_runID},
        statementID => $statementID,
        logLevel    => $self->{_logLevel}
    );
    $job->enqueue();

    # Update the statement record with the job id, and
    # update the status to 'in queue'
    #
    $statementObj->job_id( $job->id() );
    $statementObj->status(RPS::DB::Item::McpsStatement::kStatusInQueue);
    $statementObj->save();

    return $job;
}

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

    my $statementObj = RPS::DB::Item::McpsStatement->Create(
        uk_mechanical_run_id => $self->{_runID},
        mcps_scheme          => RPS::DB::Item::McpsStatement::kSchemeAVP,
        status               => RPS::DB::Item::McpsStatement::kStatusNotQueued,
    );
    $statementObj->save();
    my $statementID = $statementObj->mcps_statement_id();

    # Create the Job object
    #
    my $job = RPS::Mechanical::UK::Job::CreateStatement::AVP->new(
        runID       => $self->{_runID},
        statementID => $statementID,
        logLevel    => $self->{_logLevel}
    );
    $job->enqueue();

    # Update the statement record with the job id, and
    # update the status to 'in queue'
    #
    $statementObj->job_id( $job->id() );
    $statementObj->status(RPS::DB::Item::McpsStatement::kStatusInQueue);
    $statementObj->save();

    return $job;
}

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

    # Create a job for each statement id, and put it in the queue.
    #
    my $clientID = Common::RSApp::GetClientID();

    my $filePath = RPS::Statement::UKMechanical::McpsPDF::StatementPathFromRunID( $self->{_runID} );
    my $jobArgs  = RPS::Mechanical::UK::Job::CreatePDFStatements->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    my $job = $jobArgs->enqueue();

}

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

    # Create a job for the run, and put it in the queue.
    #
    my $clientID = Common::RSApp::GetClientID();
    my $filePath = RPS::Statement::UKMechanical::McpsText::StatementPathFromRunID( $self->{_runID}, $clientID );
    my $jobArgs  = RPS::Mechanical::UK::Job::CreateMcpsTextStatement->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    my $job = $jobArgs->enqueue();
}

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

    # We may want to wrap a job around this later.
    #
    RPS::DB::Item::ReportQueries::UKMechRetentionRunSummary->CreateRetentionSummary( $self->{_runID} );

}

sub _getLicenseShareErrors {
    my ($self) = @_;
    return undef;
}

sub _getAllOtherCommittedRuns {
    my ($self) = @_;
    return undef;
}

1;
