#!/usr/bin/perl
#---------------------------------------------------------------
# This script generates all CMRRA agent Excel statement reports.
# See Case 23 for additional information.
#
# Copyright (C) 2006-2012 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#---------------------------------------------------------------
use strict;

use Data::Dumper;
use Getopt::Std;
use Carp;

use Excel::Writer::XLSX;

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

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

use RPS::Statement::Mechanical::CA::StatementFull;
use RPS::DB::Item::CATrackLicense;
use RPS::DB::Item::CAMechanicalRun;
use RPS::DB::Item::CAMechanicalStatement;
use RPS::DB::Item::CAMechanicalStatementItem;

use RPS::DB::Item::CAPublisher;
use RPS::DB::Item::CAPublisherAccount;
use RPS::DB::Item::ProductType;

$SIG{__DIE__} = \&Carp::confess;

# This determines how much output we spew forth to STDERR.
# The user can set this with the -V command-line argument.
#
my $gVerbosityLevel = 1;

my %gReportHash = ();

# Parse the command-line, then create the CSV!
#
my %options;
parseCommandLine( \%options );

processCanadianAgent( $options{clientID}, $options{mechRunID}, $options{outputPath} );

# ----------------------------------------------------------------------------------------------

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

    assert($clientID);
    assert($runID);

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

    # Create the output path if it isn't there already.  This report is
    # intended to be run after the statements have been generated, so
    # the output path will normally already exist.
    #
    if ( '/' ne substr( $outFilePath, -1, 1 ) ) {
        $outFilePath .= "/";
    }

    if ( !-d $outFilePath ) {
        mkpath($outFilePath) or die "ERROR: Unable to create path $outFilePath: $!\n";
    }

    # Get rid of the semaphore file if any
    #
    my $semaphore = $outFilePath . "CMRRA_AGENT_REPORT_COMPLETE";
    unlink($semaphore);

    # Get the set of agents; we'll generate a report each agent
    # with statement activity.
    #
    my $agents = RPS::DB::Item::CAPublisher->GetAllAgents();

    while ( $agents->hasNext() ) {
        my $agent     = $agents->next();
        my $agentID   = $agent->ca_publisher_id;
        my $agentName = $agent->publisher_name;

        # Instantiate the Excel object.  Each agent will have their own
        # CMRRA Summary report.
        #
        my $targetFile = $outFilePath . "cmrra_agent_report_run_$runID\_agent_$agentID.xlsx";    # This is the internal filename

        my $workbook;
        my $worksheet;

        my $run = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $runID );

        if ( !$run ) {
            die("ERROR: CA runID $runID is invalid!");
        }

        my $payorID   = $run->payor_id;
        my $startDate = $run->start_date;
        my $endDate   = $run->end_date;
        my $label     = $run->label;

        my $quarterName;

        my ( $year, $month, $day ) = split( "-", $endDate );

        # Determine the name of the quarter in YYQ[1-4] format
        #
        my $q;
        $q = 1 if ( $month >= 1 and $month <= 3 );
        $q = 2 if ( $month >= 4 and $month <= 6 );
        $q = 3 if ( $month >= 7 and $month <= 8 );
        $q = 4 if ( $month >= 9 and $month <= 12 );
        $quarterName = substr( $year, -2 ) . "Q" . $q;

        # Get the collection of statements for this run.
        #
        my $statements = RPS::DB::Item::CAMechanicalStatement->GetStatementsWithActivityByMechanicalRunID($runID);
        while ( my $statement = $statements->next() ) {

            my $_stmtID      = $statement->ca_mechanical_statement_id;
            my $_publisherID = $statement->ca_publisher_id;

            my $pObj = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $_publisherID );

            if ( $pObj->is_agency ) {

                # This statement is for an agent; make sure it's for the _correct_ agent. (FB24)
                #
                next if ( $agentID != $pObj->ca_publisher_id );

                my $_name = $pObj->publisher_name;

                if ( !$workbook ) {
                    $workbook  = Excel::Writer::XLSX->new($targetFile);
                    $worksheet = $workbook->add_worksheet();
                }

                # Get the CMRRA manufacturer name/ID
                #
                my $pa = RPS::DB::Item::CAPublisherAccount->Lookup(
                    ca_publisher_id => $_publisherID,
                    payor_id        => $payorID
                );
                my $manufacturerName = $pa->manufacturer_name;
                my $manufacturerID   = $pa->manufacturer_id;

                # Get the collection of statement items.  These are not sorted, so
                # build up a report hash such that we can organize the output.
                #
                my $stmtItems = RPS::DB::Item::CAMechanicalStatementItem->GetByMechanicalStatementID($_stmtID);
                while ( my $stmtItem = $stmtItems->next() ) {

                    my $itemID     = $stmtItem->ca_mechanical_statement_item_id;
                    my $licenseID  = $stmtItem->ca_track_license_id;
                    my $netRate    = $stmtItem->net_rate;
                    my $amountPaid = $stmtItem->amount_paid;
                    my $netUnits   = $stmtItem->net_units;

                    #  Get license object and extract data
                    #
                    my $tlObj = RPS::DB::Item::CATrackLicense->Lookup( ca_track_license_id => $licenseID );

                    my $tlPublisherID   = $tlObj->ca_publisher_id;     # aka focus publisher
                    my $issuerLicenseID = $tlObj->issuer_license_id;
                    my $issuerSongID    = $tlObj->issuer_song_id;
                    my $share           = $tlObj->share;
                    my $trackID         = $tlObj->track_id;

                    my $productID   = $stmtItem->product_id;
                    my $pObj        = RPS::DB::Item::Product->Lookup( product_id => $productID );
                    my $productType = productTypeIDToName( $pObj->product_type_id );

                    # Get the composer info (need to get the track, master and song first, though)
                    #
                    my $trObj = RPS::DB::Item::Track->Lookup( track_id => $trackID );
                    my $mObj = RPS::DB::Item::Master->Lookup( master_id => $trObj->master_id );
                    my $cObj;
                    my $composer;

                    if ( $mObj->song_id ) {
                        my $sObj = RPS::DB::Item::Song->Lookup( song_id => $mObj->song_id );
                        if ( $sObj->composer_id ) {
                            $cObj = RPS::DB::Item::Composer->Lookup( composer_id => $sObj->composer_id );
                        }
                    }

                    my $trackName = $trObj->title;
                    $composer = ($cObj) ? $cObj->name : '';

                    my $aObj = RPS::DB::Item::Album->Lookup( album_id => $trObj->album_id );
                    my $catalogNumber = $aObj->catalog_number;

                    $gReportHash{$itemID}{publisher_id}      = $tlPublisherID;
                    $gReportHash{$itemID}{issuer_license_id} = $issuerLicenseID;
                    $gReportHash{$itemID}{issuer_song_id}    = $issuerSongID;
                    $gReportHash{$itemID}{share}             = $share;
                    $gReportHash{$itemID}{track_id}          = $trackID;
                    $gReportHash{$itemID}{title}             = $trackName;
                    $gReportHash{$itemID}{composer}          = $composer;
                    $gReportHash{$itemID}{net_rate}          = $netRate;
                    $gReportHash{$itemID}{net_units}         = $netUnits;
                    $gReportHash{$itemID}{amount_paid}       = $amountPaid;
                    $gReportHash{$itemID}{catalog_number}    = $catalogNumber;
                    $gReportHash{$itemID}{product_type}      = $productType;

                }    # statement item loop

                my %publisherMap;

                # For XLSX, the general syntax is write($row, $column, $token). Note that
                # row and column are zero indexed.
                #
                my $row = 0;
                my $col = 0;

                # Output header row 1
                #
                $worksheet->write( $row, 0, "CMRRA MANUFACTURER ID" );
                $worksheet->write( $row, 1, uc $manufacturerID );
                $row++;

                # Output header row 2
                #
                $worksheet->write( $row, 0, "MANUFACTURER NAME" );
                $worksheet->write( $row, 1, uc $manufacturerName );
                $row++;

                # Output header row 3
                #
                $worksheet->write( $row, $col++, "PUBLISHER NAME" );
                $worksheet->write( $row, $col++, "PUBLISHER #" );
                $worksheet->write( $row, $col++, "LICENSE # PREFIX" );
                $worksheet->write( $row, $col++, "LICENSE # SUFFIX" );
                $worksheet->write( $row, $col++, "SONG #" );
                $worksheet->write( $row, $col++, "SONG TITLE" );
                $worksheet->write( $row, $col++, "WRITER NAME(S)" );
                $worksheet->write( $row, $col++, "CATALOG #" );
                $worksheet->write( $row, $col++, "CONTRIVANCE" );
                $worksheet->write( $row, $col++, "ROYALTY RATE" );
                $worksheet->write( $row, $col++, "OWNERSHIP %" );
                $worksheet->write( $row, $col++, "UNITS" );
                $worksheet->write( $row, $col++, "ROYALTY AMOUNT OWNING \$" );
                $worksheet->write( $row, $col++, "QUARTER" );
                $worksheet->write( $row, $col++, "CMRRA MANUFACTURER ID" );
                $row++;

                # Output detail lines
                #
                foreach my $itemID ( sort _mysort keys %gReportHash ) {
                    my $publisherID     = $gReportHash{$itemID}{publisher_id};
                    my $issuerLicenseID = $gReportHash{$itemID}{issuer_license_id};
                    my $issuerSongID    = $gReportHash{$itemID}{issuer_song_id};
                    my $share           = $gReportHash{$itemID}{share};
                    my $trackID         = $gReportHash{$itemID}{track_id};
                    my $trackName       = $gReportHash{$itemID}{title};
                    my $composer        = $gReportHash{$itemID}{composer};
                    my $netRate         = $gReportHash{$itemID}{net_rate};
                    my $netUnits        = $gReportHash{$itemID}{net_units};
                    my $amountPaid      = $gReportHash{$itemID}{amount_paid};
                    my $catalogNumber   = $gReportHash{$itemID}{catalog_number};
                    my $productType     = $gReportHash{$itemID}{product_type};

                    if ( !exists $publisherMap{$publisherID} ) {
                        my $o = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $publisherID );
                        $publisherMap{$publisherID}{publisher_name}    = $o->publisher_name;
                        $publisherMap{$publisherID}{client_account_id} = $o->client_account_id;
                    }
                    my $publisherName   = $publisherMap{$publisherID}{publisher_name};
                    my $clientAccountID = $publisherMap{$publisherID}{client_account_id};

                    my $licensePrefix = $issuerLicenseID;
                    my $licenseSuffix;
                    if ( $issuerLicenseID =~ /(\d{1,6})\-?(\d+)/ ) {
                        $licensePrefix = $1;
                        $licenseSuffix = $2;
                    }

                    $col = 0;
                    $worksheet->write( $row, $col++, uc $publisherName );      # PUBLISHER NAME
                    $worksheet->write( $row, $col++, uc $clientAccountID );    # PUBLISHER #
                    $worksheet->write( $row, $col++, $licensePrefix );         # LICENSE # PREFIX
                    $worksheet->write( $row, $col++, $licenseSuffix );         # LICENSE # SUFFIX
                    $worksheet->write( $row, $col++, uc $issuerSongID );       # SONG #
                    $worksheet->write( $row, $col++, uc $trackName );          # SONG TITLE
                    $worksheet->write( $row, $col++, uc $composer );           # WRITER NAME(S)
                    $worksheet->write( $row, $col++, uc $catalogNumber );      # CATALOG #
                    $worksheet->write( $row, $col++, uc $productType );        # CONTRIVANCE
                    $worksheet->write( $row, $col++, uc $netRate );            # ROYALTY RATE
                    $worksheet->write( $row, $col++, $share );                 # OWNERSHIP %
                    $worksheet->write( $row, $col++, $netUnits );              # UNITS
                    $worksheet->write( $row, $col++, $amountPaid );            # ROYALTY AMOUNT OWNING $
                    $worksheet->write( $row, $col++, $quarterName );           # QUARTER
                    $worksheet->write( $row, $col++, uc $manufacturerID );     # CMRRA MANUFACTURER ID
                    $row++;
                }    # income item loop

                undef %gReportHash;

            }    # $pObj->is_agency

        }    # statement loop

      DONE:

        # All done, close it up.
        #
        $workbook->close() or die "Error closing file: $!" if ($workbook);

    }    # agent loop

    # Create the semaphore
    #
    open SEMAPHORE, "> $semaphore";
    print SEMAPHORE "done\n";
    close SEMAPHORE;

}    #processCanadianAgent

# This subroutine controls how the output is sorted; for now,
# we'll sort by publisherName and trackID
#
sub _mysort {
    if ( $gReportHash{$a}{publisher_id} != $gReportHash{$b}{publisher_id} ) {
        my $p_a = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $gReportHash{$a}{publisher_id} );
        my $p_b = RPS::DB::Item::CAPublisher->Lookup( ca_publisher_id => $gReportHash{$b}{publisher_id} );

        return $p_a->publisher_name cmp $p_b->publisher_name;
    }

    return $gReportHash{$a}{title} cmp $gReportHash{$b}{title};
}

# These functions map ids to their text representations.
# Basically, the first time we ask for a particular id->string mapping, we
# will query the correct database, and build a hash.  Subsequent calls just
# hit the hash.
#
# Because these tables are so similar, I abstracted the guts into the _genericMapAccessor
# method (to save myself some typing).
#
my %idMaps;

sub _genericMapAccessor {
    my ( $collectionAccessor, $idName, $id ) = @_;

    if ( !defined $idMaps{$collectionAccessor} ) {
        $idMaps{$collectionAccessor} = {};

        # This is a very naughty thing to do, but it works great in this context.
        #
        #        no strict 'refs';
        #        my $c = &$collectionAccessor();
        #        use strict 'refs';
        my $c = $collectionAccessor->GetAll();

        while ( my $item = $c->next() ) {
            if ( $idName ne 'product_type_id' ) {
                $idMaps{$collectionAccessor}{ $item->$idName() } = $item->name;
            } else {
                $idMaps{$collectionAccessor}{ $item->$idName() } =
                  $item->description;
            }
        }
    }

    return $idMaps{$collectionAccessor}{$id};
}

sub productTypeIDToName {
    my ($id) = @_;

    if ( !$id ) {
        return "All Products";
    } elsif ( $id == 254 ) {
        return "All Digital";
    } elsif ( $id == 255 ) {
        return "All Physical";
    }

    return _genericMapAccessor( 'RPS::DB::Item::ProductType', 'product_type_id', $id );
}

#
# Boring script stuff below...
#

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

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

    if ( !$opt{r} || !$opt{c} ) {
        usage();
        exit(1);
    }
    $settings->{mechRunID}  = $opt{r};
    $settings->{clientID}   = $opt{c};
    $settings->{outputPath} = $opt{p};

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

sub usage {
    print STDERR "\nusage: $0 -c <client_id> -r <ca_mech_run_id> [-p <output path>]\n";
    print STDERR "\n";
    print STDERR "Arguments:\n";
    print STDERR "\t-c <client_id>\t\t\tThe client_id of the client to process.\n";
    print STDERR "\t-r <ca_mech_run_run_id>\tThe id of the CA mechanical run to generate an agent report for.\n";
    print STDERR "\t-p <output path>\tThe output path. Optional. If not provided, use current directory.\n";
}

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

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

