#!/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::Client;
use Common::RSDB;


use lib '/app/tools/rps/lib';
use RPS::DB::Item::MechanicalRun;
use RPS::DB::Item::MechanicalStatement;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::LicenseReserve;
use RPS::DB::Item::CAMechanicalRun;
use RPS::DB::Item::CAMechanicalStatement;
use RPS::DB::Item::CAMechanicalStatementItem;
use RPS::DB::Item::CALicenseReserve;
use RPS::DB::Item::ArtistRoyaltyRun;
use RPS::DB::Item::ArtistRoyaltyStatement;
use RPS::DB::Item::ArtistRoyaltyAlbum;
use RPS::DB::Item::ArtistRoyaltyIncomeItem;
use RPS::DB::Item::ArtistContractTermReserve;
use RPS::DB::Item::ReserveLiquidation;
use RPS::DB::Item::Product;
use RPS::DB::Item::NewArtistContractTerm;

my $gVerbosityLevel = 1;



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

#my $clientID = $options{clientID};

my @clientIDs;
if ($options{clientID})
{
    push @clientIDs, $options{clientID};
}
else
{
    @clientIDs = Common::RSDB::GetAllClientIDs();
}

my @clientMissingUSMechanicalReserves;
my @clientMissingCAMechanicalReserves;
my @clientMissingArtistReserves;

foreach my $clientID (@clientIDs)
{
    
    # Instantiate the application singleton object.
    #
    my $appSingleton = Common::RSApp->new(clientID => $clientID);
    
    _report("\n---- Client $clientID");
    
    
    _report("---- Checking US mechanicals");

    my $missingUSMechanicalReservesFlag = 0;    
    my $runs = RPS::DB::Item::MechanicalRun->GetAll();
    my @runIDs;
    while (my $run = $runs->next())
    {
        # We only care about committed or closed runs.
        # 
        if ($run->status == 2 || $run->status == 5)
        {
            #_report("\n---- checking run " . $run->mechanical_run_id);
            push(@runIDs, $run->mechanical_run_id); 
        }  
    } 
    
    my @statementIDs;
    
    foreach my $runID (@runIDs)
    {
        my $statements = RPS::DB::Item::MechanicalStatement->GetByMechanicalRunID($runID);
        
        while (my $statement = $statements->next())
        {
            push(@statementIDs, $statement->mechanical_statement_id);   
        }         
    }
    
    my $totalMissingReserves = 0;
    my %reserves = ();
    
    foreach my $statementID (@statementIDs)
    {
        my $statementItems = RPS::DB::Item::MechanicalStatementItem->GetByMechanicalStatementID($statementID);
        
        while (my $statementItem = $statementItems->next())
        {       
            # First we'll see what's been reserved and what's already liquidated.
            #        
            my $trackLicenseID = $statementItem->track_license_id;
            my $statementItemID = $statementItem->mechanical_statement_item_id;
            my $statementItemReserved = $statementItem->reserved;
            my $statementItemLiquidated = $statementItem->liquidated;
            
            if ($statementItemReserved > 0)
            {
                $reserves{$trackLicenseID} += $statementItemReserved;
                #_report("---- track license $trackLicenseID: $statementItemReserved reserves taken");
            }
            
            if ($statementItemLiquidated > 0)
            {
                $reserves{$trackLicenseID} -= $statementItemLiquidated;
                #_report("---- track license $trackLicenseID: $statementItemLiquidated reserves liquidated");
            }
    
            # And now we have to include any reserves we know about that have not been liquidated yet.
            #
            my $reserveItems = RPS::DB::Item::LicenseReserve->GetByStatementItemID($statementItemID);
            while (my $reserveItem = $reserveItems->next())
            {            
                if ($reserveItem->periods_remaining != 0)
                {
                    $reserves{$trackLicenseID} -= $reserveItem->units;
                    #_report("---- track license $trackLicenseID: " . $reserveItem->units . " reserves saved");
                }
            }              
        }                        
    }
    
    # Okay, let's look at the totals.  
    # If it's greater than zero, then we have reserves taken that will not be liquidated. (These are what we care about)
    # If it's zero, then things add up perfectly.
    # if it's negative, then we'll have to assume that because of historical reserves being liquidated.
    
    my $totalMissingReserves = 0;
    
    foreach my $trackLicenseID (keys %reserves)
    {
        my $remainingReserves = %reserves->{$trackLicenseID};
        if ($remainingReserves > 0)
        {
            $missingUSMechanicalReservesFlag = 1;
            
            $totalMissingReserves += $remainingReserves;
            
            # If we are only looking at one client, let's give more details.
            #
            if ($options{clientID})
            {
                _report("---- Track license $trackLicenseID has $remainingReserves missing reserves!");    
            }  
        }
    }
    
    if ($missingUSMechanicalReservesFlag == 1)
    {
        _report("---- Found missing $totalMissingReserves US mechanical reserves!");
        push(@clientMissingUSMechanicalReserves, $clientID);
    }
    
    
    
    _report("---- Checking CA mechanicals");

    my $missingCAMechanicalReservesFlag = 0;    
    my $runs = RPS::DB::Item::CAMechanicalRun->GetAll();
    my @runIDs;
    while (my $run = $runs->next())
    {
        # We only care about committed or closed runs.
        # 
        if ($run->status == 2 || $run->status == 5)
        {
            #_report("\n---- checking run " . $run->mechanical_run_id);
            push(@runIDs, $run->ca_mechanical_run_id); 
        }  
    } 
    
    my @statementIDs;
    
    foreach my $runID (@runIDs)
    {
        my $statements = RPS::DB::Item::CAMechanicalStatement->GetByMechanicalRunID($runID);
        
        while (my $statement = $statements->next())
        {
            push(@statementIDs, $statement->ca_mechanical_statement_id);   
        }         
    }
    
    my $totalMissingReserves = 0;
    my %reserves = ();
    
    foreach my $statementID (@statementIDs)
    {
        my $statementItems = RPS::DB::Item::CAMechanicalStatementItem->GetByMechanicalStatementID($statementID);
        
        while (my $statementItem = $statementItems->next())
        {       
            # First we'll see what's been reserved and what's already liquidated.
            #        
            my $trackLicenseID = $statementItem->ca_track_license_id;
            my $statementItemID = $statementItem->ca_mechanical_statement_item_id;
            my $statementItemReserved = $statementItem->reserved;
            my $statementItemLiquidated = $statementItem->liquidated;
            
            if ($statementItemReserved > 0)
            {
                $reserves{$trackLicenseID} += $statementItemReserved;
                #_report("---- track license $trackLicenseID: $statementItemReserved reserves taken");
            }
            
            if ($statementItemLiquidated > 0)
            {
                $reserves{$trackLicenseID} -= $statementItemLiquidated;
                #_report("---- track license $trackLicenseID: $statementItemLiquidated reserves liquidated");
            }
    
            # And now we have to include any reserves we know about that have not been liquidated yet.
            #
            my $reserveItems = RPS::DB::Item::CALicenseReserve->GetByStatementItemID($statementItemID);
            while (my $reserveItem = $reserveItems->next())
            {            
                if ($reserveItem->periods_remaining != 0)
                {
                    $reserves{$trackLicenseID} -= $reserveItem->units;
                    #_report("---- track license $trackLicenseID: " . $reserveItem->units . " reserves saved");
                }
            }              
        }                        
    }
    
    # Okay, let's look at the totals.  
    # If it's greater than zero, then we have reserves taken that will not be liquidated. (These are what we care about)
    # If it's zero, then things add up perfectly.
    # if it's negative, then we'll have to assume that because of historical reserves being liquidated.
    foreach my $trackLicenseID (keys %reserves)
    {
        my $remainingReserves = %reserves->{$trackLicenseID};
        if ($remainingReserves > 0)
        {
            $missingCAMechanicalReservesFlag = 1;
            
            # If we are only looking at one client, let's give more details.
            #
            if ($options{clientID})
            {
                _report("---- Track license $trackLicenseID has $remainingReserves missing reserves!");    
            }  
            
            # Okay, now let's create them.
            #
            
        }
    }
    
    if ($missingCAMechanicalReservesFlag == 1)
    {
        _report("---- Found missing CA mechanical reserves!");
        push(@clientMissingCAMechanicalReserves, $clientID);
    }    
    
    
    
    _report("---- Checking artist royalties");

    my $missingArtistReservesFlag = 0;    
    my $runs = RPS::DB::Item::ArtistRoyaltyRun->GetAll();
    my @runIDs;
    while (my $run = $runs->next())
    {
        # We only care about committed or closed runs.
        # 
        if ($run->status == 2 || $run->status == 5)
        {
            #_report("\n---- checking run " . $run->mechanical_run_id);
            push(@runIDs, $run->artist_royalty_run_id); 
        }  
    } 
    
    my @statementIDs;
    
    foreach my $runID (@runIDs)
    {
        my $statements = RPS::DB::Item::ArtistRoyaltyStatement->GetByArtistRoyaltyRunID($runID);
        
        while (my $statement = $statements->next())
        {
            push(@statementIDs, $statement->artist_royalty_statement_id);   
        }         
    }
   
    my $totalMissingReserves = 0;
    my %unitReserves = ();     
    my %revenueReserves = ();
    my @royaltyAlbumIDs;
    
    foreach my $statementID (@statementIDs)
    {
        my $royaltyAlbums = RPS::DB::Item::ArtistRoyaltyAlbum->GetByArtistRoyaltyStatementID($statementID);
        
        while (my $royaltyAlbum = $royaltyAlbums->next())
        {
            my $artistRoyaltyAlbumID = $royaltyAlbum->artist_royalty_album_id;  
            my $artistContractID = $royaltyAlbum->artist_contract_id;   

            my $statementItems = RPS::DB::Item::ArtistRoyaltyIncomeItem->GetByArtistRoyaltyAlbumID($artistRoyaltyAlbumID);
              
            while (my $statementItem = $statementItems->next())
            {       
                # First we'll see what's been reserved and what's already liquidated.
                #        
                my $statementItemID = $statementItem->artist_royalty_income_item_id;
                my $statementItemUnitsReserved = $statementItem->units_reserved;
                my $statementItemUnitsLiquidated = $statementItem->units_liquidated;
                
                if ($statementItemUnitsReserved > 0)
                {
                    $unitReserves{$artistContractID} += $statementItemUnitsReserved;
                }
                
                if ($statementItemUnitsLiquidated > 0)
                {
                    $unitReserves{$artistContractID} -= $statementItemUnitsLiquidated;
                }
                
                my $statementItemRevenueReserved = $statementItem->revenue_reserved;
                my $statementItemRevenueLiquidated = $statementItem->revenue_liquidated;
                
                if ($statementItemRevenueReserved > 0)
                {
                    $revenueReserves{$artistContractID} += $statementItemRevenueReserved;
                }
                
                if ($statementItemRevenueLiquidated > 0)
                {
                    $revenueReserves{$artistContractID} -= $statementItemRevenueLiquidated;
                }                
        
                # And now we have to include any reserves we know about that have not been liquidated yet.
                #
                my $reserveItems = RPS::DB::Item::ArtistContractTermReserve->GetByStatementItemID($statementItemID);
                while (my $reserveItem = $reserveItems->next())
                {            
                    if ($reserveItem->periods_remaining != 0)
                    {
                        $unitReserves{$artistContractID} -= $reserveItem->units;
                        $revenueReserves{$artistContractID} -= $reserveItem->revenue;
                    }
                }                  
                           
            }                        
        }    
    }
    
    
    # Okay, let's look at the totals.  
    # If it's greater than zero, then we have reserves taken that will not be liquidated. (These are what we care about)
    # If it's zero, then things add up perfectly.
    # if it's negative, then we'll have to assume that because of historical reserves being liquidated.
    foreach my $artistContractID (keys %unitReserves)
    {
        my $remainingReserves = %unitReserves->{$artistContractID};
        if ($remainingReserves > 0)
        {
            $missingArtistReservesFlag = 1;   
            
            # If we are only looking at one client, let's give more details.
            #
            if ($options{clientID})
            {
                _report("---- Artist contract $artistContractID has $remainingReserves missing unit reserves!");    
            }   
            
            # Okay, let's create them.
            #               

        }
    }
    foreach my $artistContractID (keys %revenueReserves)
    {
        my $remainingReserves = %revenueReserves->{$artistContractID};
        if ($remainingReserves > 0)
        {
            $missingArtistReservesFlag = 1;   
            
            # If we are only looking at one client, let's give more details.
            #
            if ($options{clientID})
            {
                _report("---- Artist contract $artistContractID has $remainingReserves missing revenue reserves!");    
            }                    
        }
    }        
    
    if ($missingArtistReservesFlag == 1)
    {
        _report("---- Found missing artist reserves!");
        push(@clientMissingArtistReserves, $clientID);
    }    
    
}

# If we checked all the clients on the server, let's print out a summary of the results.
#
if (! $options{clientID})
{
    _report("\n---- RESULTS");
    @clientMissingUSMechanicalReserves = sort { $a <=> $b }  @clientMissingUSMechanicalReserves;
    my $usMechanicalClients = join(", ",@clientMissingUSMechanicalReserves);
    _report("\n---- Clients missing US mechanical reserves: " . $usMechanicalClients); 
    @clientMissingCAMechanicalReserves = sort { $a <=> $b }  @clientMissingCAMechanicalReserves;
    my $caMechanicalClients = join(", ",@clientMissingCAMechanicalReserves);     
    _report("\n---- Clients missing CA mechanical reserves: " . $caMechanicalClients); 
    @clientMissingArtistReserves = sort { $a <=> $b }  @clientMissingArtistReserves; 
    my $artistClients = join(", ",@clientMissingArtistReserves);       
    _report("\n---- Clients missing artist reserves: " . $artistClients); 
    
}

#_report(%reserves);



sub _createArtistUnitReserve
{
    my ($incomeItem) = @_;

    _report("_createArtistUnitReserve");
    
    my $productID;
    my $products = RPS::DB::Item::Product->GetByUPC($incomeItem->upc_ean);
    
    while ($products->hasNext())
    {
        my $product = $products->next();        
        $productID = $product->product_id;
    }

    my $term = RPS::DB::Item::NewArtistContractTerm->Lookup(artist_contract_term_id => $incomeItem->artist_contract_term_id);
    my $artistContractID = $term->artist_contract_id;
    my $units = $incomeItem->units_reserved;

    my %scheduleTable;
    
    _report("looking up liquidation schedule for artist contract $artistContractID");
    
    my $schedule = RPS::DB::Item::ReserveLiquidation->GetByIDType($artistContractID, RPS::DB::Item::ReserveLiquidation::kTypeArtistContract);
    
    if (! $schedule)
    {
        die "no schedule!";   
    }
    
    print STDERR Dumper $schedule;

    while ($schedule->hasNext())
    {
        my $scheduleEntry = $schedule->next();
        $scheduleTable{$scheduleEntry->period} = $scheduleEntry->percent;
    }

    my $albumID = $incomeItem->album_id;
    my $trackID = $incomeItem->track_id;
    $trackID = 0 unless $trackID;

    my $createdReserve = 0;
    my $initialUnits = $units;
    for (my $i = 1; $units > 0 && $i <= 8; $i++)
    {
        my $p = $scheduleTable{$i};
        next unless $p;

        my $periodUnits = ceil($initialUnits * ($p / 100));
        $periodUnits = $units unless $periodUnits < $units;
        $units -= $periodUnits;
        
        

        my $newReserve = RPS::DB::Item::ArtistContractTermReserve->Create
        (
            artist_contract_term_id     => $incomeItem->artist_contract_term_id,
            original_statement_item_id  => $incomeItem->artist_royalty_income_item_id,
            income_source_id            => $incomeItem->income_source_id,
            region_id                   => $incomeItem->region_id,
            channel_id                  => $incomeItem->channel_id,
            price_level_id              => $incomeItem->price_level_id,
            product_id                  => $productID,
            units                       => $periodUnits,
            revenue_based               => 0,
            effective_rate              => $incomeItem->rate,  
            price                       => $incomeItem->price,
            periods_remaining           => $i + 1,
            album_id                    => $albumID,
            track_id                    => $trackID,
        );

        $newReserve->save();
        $createdReserve = 1;
        _report("created reserve for $periodUnits units");
    }
    
    # If for some reason we didn't create a reserve (like say the liquidation schedule is blank),
    # that's bad news so we need to bail.
    #
    if ($createdReserve == 0)
    {
        die "ERROR - reserve was not created successfully for product id: $productID income item: " . Dumper($incomeItem) . "\n";
    }
   
}


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";
    }
}


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

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

    $settings->{clientID} = $opt{c} if $opt{c};

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