#!/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::ReserveLiquidation;
use RPS::DB::Item::Product;

my $gVerbosityLevel = 1;

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

my $clientID = $options{clientID};

my $testing = $options{testing};

if ($testing) {
    _report("\n Test Mode Activated -- No reserves will be created!");
}

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

_report("\n Client $clientID");

_report("---- Checking US mechanicals");

my $runs = RPS::DB::Item::MechanicalRun->GetAll();
my @runIDs;

##  We created reserves differently in the past, so we will only be able to go back so far.
##
my $cutOffDate = "2008-06-30 01:01:01";

while ( my $run = $runs->next() ) {

    # We only care about committed or closed runs.
    #
    if ( ( $run->status == 2 || $run->status == 5 ) && $run->end_time ge $cutOffDate ) {
        push( @runIDs, $run->mechanical_run_id );
    }
}

my @statementIDs;

foreach my $runID (@runIDs) {
    _report( "-- checking run " . $runID );
    my $statements = RPS::DB::Item::MechanicalStatement->GetByMechanicalRunID($runID);

    while ( my $statement = $statements->next() ) {
        push( @statementIDs, $statement->mechanical_statement_id );
    }
}

my $missingReserveGrandTotal = 0;
my $createdReserveGrandTotal = 0;

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 (if anything).
        #
        my $statementItemReserved = $statementItem->reserved;

        if ( $statementItemReserved > 0 ) {
            my $trackLicenseID  = $statementItem->track_license_id;
            my $statementItemID = $statementItem->mechanical_statement_item_id;

            # Now we need to check for an entry in the license_reserve table
            #
            my $reserveItems = RPS::DB::Item::LicenseReserve->GetByStatementItemID($statementItemID);
            my $reserveTotal = 0;
            while ( my $reserveItem = $reserveItems->next() ) {
                $reserveTotal += $reserveItem->units;
            }

            # We need to play this really safe because of historical reserves,
            # so we're only going to create reserves if they are completely missing.
            # The original issue was reserves taken without a liquidation schedule,
            # so this should be okay.
            #if ($statementItemReserved != $reserveTotal)

            if ( $reserveTotal == 0 ) {
                _report( "---- statementItemID $statementItemID: " . $statementItemReserved . " reserves" );
                _report( "---- license_reserve table: " . $reserveTotal . " reserves" );
                _report("---- $statementItemReserved != $reserveTotal");

                if ( $statementItemReserved < $reserveTotal ) {

                    # I don't think this will happen, but I want to know if it does.
                    die "ERROR - we have more reserves saved than we took... this is pretty messed up\n";
                } else {
                    my $missingReserves = $statementItemReserved - $reserveTotal;
                    $missingReserveGrandTotal += $missingReserves;

                    # Okay, let's do this.
                    #
                    my %scheduleTable;
                    my $schedule = RPS::DB::Item::ReserveLiquidation->GetByIDType( $trackLicenseID, 1 );
                    while ( my $scheduleEntry = $schedule->next() ) {
                        $scheduleTable{ $scheduleEntry->period() } = $scheduleEntry->percent;
                    }

                    my $createdReserve = 0;

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

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

                        $missingReserves -= $periodUnits;

                        #my $periodsRemaining = $i + 1;
                        my $periodsRemaining = $i;

                        if ( !$testing ) {
                            my $newReserve = _createLicenseReserve( $statementItem, $periodUnits, $periodsRemaining );
                            $newReserve->save();
                        }

                        $createdReserve = 1;
                        $createdReserveGrandTotal += $periodUnits;
                        _report("---- created reserve with $periodUnits units and $periodsRemaining periods remaining");

                    }

                    # 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 statement item: " . Dumper($statementItem) . "\n";
                    }
                }
            }
        }
    }
}

_report("---- Missing reserve grand total is $missingReserveGrandTotal");
_report("---- Created reserve grand total is $createdReserveGrandTotal");

sub _createLicenseReserve {
    my ( $statementItem, $units, $periodsRemaining ) = @_;

    my $newReserve = RPS::DB::Item::LicenseReserve->Create(
        track_license_id           => $statementItem->track_license_id,
        original_statement_item_id => $statementItem->mechanical_statement_item_id,
        product_id                 => $statementItem->product_id,
        sale_stat_rate_id          => $statementItem->sale_stat_rate_id,
        issue_stat_rate_id         => $statementItem->issue_stat_rate_id,
        units                      => $units,
        effective_rate             => $statementItem->net_rate,
        periods_remaining          => $periodsRemaining,
    );
    return $newReserve;
}

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:t', \%opt );

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

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