#!/usr/bin/perl -w

###
#   We have added a new field to the sale table, r_cogs which is the inverse of
#   discount rate.
#
#   This script will correctly popultate the calculated discount rate based on
#   logic defined in case 14352.
#
#   For services listed in the invert discount list we will remove the r_discount
#   value and populate r_cogs with it's inverse.
###
use strict;

DiscountFix->start();

package DiscountFix;

use Data::Dumper;

use lib '/app/tools/bookpub/lib';
use base 'BookPub::Script';

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

use lib '/app/tools/bookpub/lib';
use BookPub::Tracker::Service;
use BookPub::DB::Item::File;
use BookPub::DB::Item::Sale;

sub _invertedDiscountServices {
    ( BookPub::Tracker::Service::KOBO, BookPub::Tracker::Service::LSI, BookPub::Tracker::Service::SCROLL_MOTION );
}

sub _options {
    {
        service_id => {
            short       => 's',
            description => 'Limit correction to file service id',
            parameter   => 'i'
        },
        sale_id => {
            description => 'Limit correction to sale_id',
            parameter   => 'i'
        },
        commit => {
            description => 'Unless this option is specified, no changes will be committed to the database',
        },
    };
}

sub _process {
    my $self = shift;

    my $files = BookPub::DB::Item::File->GetAll();

    while ( my $file = $files->next() ) {
        $self->_updateSales($file);
    }
}

sub _updateSales {
    my $self   = shift;
    my $file   = shift || die "File required";
    my $saleID = $self->param('sale_id');

    my $updateCount = 0;

    # If we have specified a service id on the command line then only continue if they match.
    return if ( $self->param('service_id') && $self->param('service_id') != $file->service_id );

    #return if( $file->service_id == 12 );
    return if ( $file->service_id == 24 && ( $file->version_num < 4 ) );

    # if we have specified a sale id check the file now
    if ($saleID) {
        my $s = BookPub::DB::Item::Sale->Lookup( sale_id => $saleID );
        return if ( !$s || $s->file_id != $file->file_id );
    }

    my $invertedDiscount = $self->_invertDiscount( $file->service_id );

    Log->info( "Correcting discount, File ID: "
          . $file->file_id
          . " Service ID: "
          . $file->service_id
          . " Invert discount: "
          . sprintf( "%s", $invertedDiscount ? "Y" : "N" ) );

    my $sales = BookPub::DB::Item::Sale->GetAllByFileID( $file->file_id );

    while ( my $sale = $sales->next() ) {
        next if ( $saleID && $saleID != $sale->sale_id );

        if ( $invertedDiscount && $sale->r_discount ) {
            $sale->r_cogs( $self->_calculateInvertedDiscount($sale) );
            $sale->r_discount(undef);
        }

        $self->_updateDiscount($sale);

        $updateCount++ if ( $sale->isDirty() );

        # We don't want to save, just use this script for reporting.
        assert( !$self->param('commit'), "Commit disabled" );

        #$sale->save if( $self->param('commit') );
    }

    Log->info( sprintf( "$updateCount records found %s", $self->param('commit') ? 'and updated' : 'that require updated' ) );
}

sub _invertDiscount {
    my $self = shift;
    my $serviceID = shift || die "Service ID required";

    for my $invertedServiceID ( $self->_invertedDiscountServices ) {
        return 1 if ( $serviceID == $invertedServiceID );
    }

    return;
}

sub _calculateInvertedDiscount {
    my $self = shift;
    my $sale = shift || die "Sale Required";

    my $unitPrice = $sale->unit_price();
    my $listPrice = $sale->r_list_price();

    assert( $unitPrice && $listPrice );

    my $discount         = $sale->r_discount();
    my $invertedDiscount = 1 - $sale->r_discount();

    my $calculatedDiscount = ( 1 - $unitPrice / $listPrice ) if ( $unitPrice && $listPrice && $listPrice > 0 );

    if ( $listPrice > 0 && abs( $discount - $calculatedDiscount ) < 0.15 ) {
        return $invertedDiscount;
    }

    elsif ( $listPrice > 0 && abs( $invertedDiscount - $calculatedDiscount ) < 0.15 ) {
        return $discount;
    }

    else {
        return $discount > .5 ? $discount : $invertedDiscount;
    }
}

sub _updateDiscount {
    my $self = shift;
    my $sale = shift || die "Sale required";

    my $discount          = $sale->r_discount();
    my $cogs              = $sale->r_cogs();
    my $unitPrice         = $sale->unit_price();
    my $listPrice         = $sale->r_list_price();
    my $listPriceCurrency = $sale->r_list_price_currency();
    my $unitPriceCurrency = $sale->currency_code();

    if ( defined($discount) ) {
        $sale->discount($discount);
    }

    elsif ( defined($cogs) ) {
        $sale->discount( 1 - $cogs );
    }

    else {
        # Noop unless cogs or discount are set
        return;
    }

### Our normal discount calculation includes the below code, but for this fix
    #   we only need to examine sales with r_discount or r_cogs set.
###

    #    elsif( defined($unitPrice) &&
    #           defined($listPrice) &&
    #           ( ! $listPriceCurrency || $listPriceCurrency eq $unitPriceCurrency ) ) {
    #        Log->info( "U: $unitPrice / $listPrice" );
    #        assert( $listPrice, 'Divide by 0' );
    #        $sale->discount(1 - $unitPrice / $listPrice);
    #    }

    #    elsif( defined($unitPrice) && defined($listPrice) ) {
    #        die "Can not determine discount with inconsistent currencies: " . Dumper $sale;
    #    }
    #
    #    else {
    #        print Dumper $sale;
    #        die " No discount. " . $sale->sale_id();
    #        # No discount stored if we don't have a list price.
    #        $self->discount(undef);
    #    }

    # Before we move on lets see if we can validate the discount within 10
    # We can only do this validation if we have a list price, unit price and they are in the same currency.
    if (   defined($unitPrice)
        && defined($listPrice)
        && $listPrice > 0
        && $unitPrice > 0
        && ( !$listPriceCurrency || $listPriceCurrency eq $unitPriceCurrency ) ) {
        my $calculated = 1 - $unitPrice / $listPrice;
        my $difference = abs( $calculated - $sale->discount() );
        Log->debug( "I: " . $sale->sale_id . " U: $unitPrice L: $listPrice Calculated: $calculated Discount: " . $sale->discount() );
        warn "Incorrect discount rate ($difference off) for sale id " . $sale->sale_id . " file id " . $sale->file_id . "\n"
          if ( $difference > 0.15 );
    }

}
