package RPS::ArtistRoyalty::Fast::TermType::Base;

use strict;

use lib '/app/tools/rps/lib';
use lib '/app/tools/common/lib';
use Common::RSMath;
use Common::Log;
use RPS::File::Sale;
use RPS::ArtistRoyalty::Fast::Static::Sales;
use RPS::ArtistRoyalty::Fast::Mapped::Data;
use RPS::ArtistRoyalty::Fast::Static::ProductTerms;
use RPS::ArtistRoyalty::Fast::Static::Terms;
use RPS::DB::Item::IncomeSource;
use RPS::DB::Item::PriceLevel;
use RPS::DB::Item::ContractRateType;
use RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem;
use RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyAlbum;
use RPS::ArtistRoyalty::Fast::Final::ArtistContractTermReserveRun;

my %gIsAlbumIncomeSource = (
    RPS::DB::Item::IncomeSource::kIncomeSourceDigitalAlbum()        => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceCD()                  => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceLP()                  => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceCassette()            => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceCDSingle()            => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceDblCD()               => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceDigitalAlbumPremium() => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceDigitalAlbumUpgrade() => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceDVD()                 => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceBluRay()              => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceDVDCDSet()            => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceCassette5()           => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceLP5()                 => 1,
    RPS::DB::Item::IncomeSource::kIncomeSourceVHS()                 => 1,
);

sub isRevenueBased       { 0 }
sub isDocumentPriceBased { 0 }

sub accumulateData {
    my ( $self, $data, $item ) = @_;

    $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kSales] += $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kSales];
    $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kReturns] +=
      $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kReturns];

    # JPK - Don't I need to apply the conversion rate here as well?
    my $convRate = Common::RSMath::round( $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kConversionRate], 4 );

    my $revenue = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kRevenue] * $convRate;

    my $distFee = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kDistributionFee];

    # !!! The _correct_ way:
    # if ($distFee != 0)
    if ($distFee) {

        # !!! We're replicating a bug here...
        # The bug is - this happens even if the fee is '0.00'.
        # Not to mention rounding at all is pointless...
        # This does shave money off.
        #
        $revenue = Common::RSMath::round( $revenue * ( ( 100 - $distFee ) / 100 ), 2 );
    }
    Log->info("TermType/Base revenue: $revenue");
    $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kRevenue] += $revenue;

    # Reserves are not always liquidated - We have some situations where we skip them.
    #
    $self->_accumulateReserves( $data, $item );

    # !!! One more thing to do here - We need to keep track of the original reserve ids, so we can build a table later.
    # !!! I think that may require a method in the $item to pull off.

    # The isDigital flag is contained in the RPS::ArtistRoyalty::Fast::Mapped::Data line.
    # Oddly, the ArtistRoyaltyIncomeItem data doesn't contain product_id.
    # I'm going to have to store this flag in the IncomeItem, so I can get at
    # it when we're calculating the reserves right before output.
    # It will (or at least should...) always be the same for given line.
    #
    $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kIsDigital] =
      $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kIsDigital];

    # This will be the same for all product ids.

    # But new reserves will be calculated at the very end.  We don't accumulate that data.
    # !!! Therefore, we don't need to reserve those two columns in the RPS::ArtistRoyalty::Fast::Mapped::Data data...
    #
    #    $item->[ArtistRoyaltyIncomeItem::kUnitsReserved += $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kUnitsReserved];
    #    $item->[ArtistRoyaltyIncomeItem::kRevenueReserved += $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kRevenueReserved];

    # Now we may need to do some logging.
    #
    $self->_logSaleOrReserves( $data, $item );

}

sub _accumulateReserves {
    my ( $self, $data, $item ) = @_;

    # Reserve liquidations come from the mapped items table.
    #
    if ( 1 == $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kReservePeriodsRemaining] ) {
        $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kUnitsLiquidated] +=
          $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kUnitsLiquidated];
        $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kRevenueLiquidated] +=
          $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kRevenueLiquidated];
    }
}

sub _logSaleOrReserves {
    my ( $self, $data, $item ) = @_;

    # In theory this should identify the mapped data item as a reserve entry.
    # Log them all, even the ones we don't actually process this period because
    # they have not 'expired' yet.
    #
    # !!! So, this obviously doesn't work, since it only logs reserves that are liquidating.  That is not what we need.
    #
    if ( RPS::ArtistRoyalty::Fast::Mapped::Data::kTypeReserve == $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kType]
        && $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kReservePeriodsRemaining] > 0 )

      #    if ($data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kUnitsLiquidated] > 0
      #    || $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kRevenueLiquidated] > 0)
    {
        $self->_createReserveLog( $data, $item );
    } else {
        $self->_createSaleRunLog( $data, $item );
    }
}

sub _createReserveLog {
    my ( $self, $data ) = @_;

    # It's ok that this immediately goes out of scope -that just causes it to write to the file immediately.
    #
    # !!! Yes, this is a hack - the reserve id is in the sale is slot for reserve liquidations.
    #
    my $reserveID = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kSaleID];
    if ( 0 != $reserveID ) {
        my $newReserveMap = RPS::ArtistRoyalty::Fast::Final::ArtistContractTermReserveRun->new($reserveID);
    }
}

sub _createSaleRunLog {
    my ( $self, $data, $item ) = @_;

    # It's ok that this immediately goes out of scope -that just causes it to write to the file immediately.
    #
    # !!! Yes, this is a hack - the reserve id is in the sale is slot for reserve liquidations.
    #
    my $saleID = $data->[RPS::ArtistRoyalty::Fast::Mapped::Data::kSaleID];
    if ( 0 != $saleID ) {
        my $newSaleMap = RPS::ArtistRoyalty::Fast::Final::SaleRunMap->new( $saleID,
            $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kArtistRoyaltyIncomeItemID] );
    }
}

sub getRateData {
    my ( $self, $sale, $productTerm, $productPrices, $incomeSourceID ) = @_;

    my ( $priceLevel, $price, $rate, $prorate );    # Added prorate in FB113

    # Looking at the old code, we get the price and the price level as a
    # single action for Retail,wholesale, and ppd.

    # So we can model that here.
    # We'll normalize the incoming price level

    # Note also that priceLevel is meaningless for Net Revenue terms, so we want to set
    # that to 0 so we hash everything together correctly.
    #

    $priceLevel = $self->_getPriceLevel( $sale, $productTerm );
    Log->info("_getPriceLevel returned priceLevel: $priceLevel");

    # JPK - There may not BE a product_price, which is usually a problem.
    #
    $price = $self->_getPrice( $sale, $priceLevel, $productPrices );
    return ( undef, undef, undef, undef ) unless defined $price;

    ( $rate, $prorate ) = $self->_getRate( $sale, $productTerm, $incomeSourceID );

    #    # !!! Rounding to 4 places here is probably not necessary, and is introducing errors.
    #    return ($priceLevel, Common::RSMath::round($price, 4), Common::RSMath::round($rate, 4));
    return ( $priceLevel, $price, $rate, $prorate );
}

sub _getPriceLevel {
    my ( $self, $sale, $productTerm ) = @_;

    my $priceLevel    = $sale->[ RPS::ArtistRoyalty::Fast::Static::Sales::kPriceLevel() ];
    my $productTypeID = $productTerm->[ RPS::ArtistRoyalty::Fast::Static::ProductTerms::kProductTypeID() ];

    if ( RPS::DB::Item::Product::kProductTypeDigitalTrack == $productTypeID ) {
        $priceLevel = RPS::DB::Item::PriceLevel::kPriceLevelTrackDownload;
    } elsif ( RPS::DB::Item::Product::kProductTypeDigital == $productTypeID ) {
        $priceLevel = RPS::DB::Item::PriceLevel::kPriceLevelAlbumDownload;
    }

    if ( 0 == $priceLevel ) {
        $priceLevel = $productTerm->[RPS::ArtistRoyalty::Fast::Static::ProductTerms::kDefaultPriceLevelID];
    }

    return $priceLevel;
}

sub _getRate {
    my ( $self, $sale, $productTerm, $incomeSourceID ) = @_;

    # The rate is pretty straightforward.
    #

    # !!! Am I a 'Terms' or a 'ProductTerms' ?
    # !!! I am a Terms ref, blessed into this class.

    my $rate = $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kRate];

    my $prorate = $productTerm->[RPS::ArtistRoyalty::Fast::Static::ProductTerms::kProrateTrackCount];

    my $_prorate;    # Set only if we prorate the rate (FB113)
                     #
                     # Prorate will be set if this rate came to us via a track_contract.

    if ($prorate) {
        if ( RPS::File::Sale::TYPE_ALBUM eq $sale->[RPS::ArtistRoyalty::Fast::Static::Sales::kProductType]
            || $gIsAlbumIncomeSource{$incomeSourceID} ) {
            $rate /= $prorate;

            $_prorate = $prorate;
        }
    }

    return ( $rate, $_prorate );
}

sub calculateFinalRateData {
    my ( $self, $incomeItem, $isDigital ) = @_;

    my $netUnits = $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kSales];

    # Apply the percentage of sales deduction first.
    #
    if ( $netUnits > 0 && $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kPercentageOfSales] > 0 ) {
        $netUnits = Common::RSMath::round( $netUnits * ( $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kPercentageOfSales] / 100 ), 0 );
    }

    # Take reserves before deductions.
    #
    my $unitsReserved;

    # !!! WHere is the reservePercentage?
    # in the term
    #
    if ( !$isDigital ) {
        my $reservePercentage = $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kReserveRate];
        if ( $netUnits > 0 && $reservePercentage ) {
            $unitsReserved = Common::RSMath::round( $netUnits * ( $reservePercentage / 100 ), 0 );
            if ( $unitsReserved > $netUnits ) {
                $unitsReserved = $netUnits;
            }
            $netUnits -= $unitsReserved;
        }
    }

    # Add in liquidated units
    #
    $netUnits += $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kUnitsLiquidated];

    # Now the free goods deduction.
    #
    if ( $netUnits > 0 && $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kFreeGoodsDeduction] ) {
        $netUnits =
          Common::RSMath::round( $netUnits * ( ( 100 - $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kFreeGoodsDeduction] ) / 100 ), 0 );
    }

    # Subtract the returns now.
    #
    $netUnits -= $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kReturns];

    # Apply rate deductions
    #
    my $netRate = $self->_calcNetRate($incomeItem);

    if ( $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kRateReduction] ) {
        $netRate = $netRate * ( $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kRateReduction] / 100 );
    }

    if ( $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kPackagingDeduction] ) {
        $netRate = $netRate * ( ( 100 - $self->[RPS::ArtistRoyalty::Fast::Static::Terms::kPackagingDeduction] ) / 100 );
    }

    my $total = Common::RSMath::round( ( $netRate * $netUnits ), 2 );

    # Just write the data straight into the record.
    #
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kNetUnits]      = $netUnits;
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kNetRevenue]    = 0;
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kRevenue]       = 0;
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kNetRate]       = $netRate;
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kTotal]         = $total;
    $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kUnitsReserved] = $unitsReserved;
}

# Most terms are unit-based.
#
sub updateAlbumSummary {
    my ( $self, $item, $album ) = @_;

    $album->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyAlbum::kUnitLevelIncome] +=
      $item->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kTotal];

}

# Override this for the fixed rate type.
#
sub _calcNetRate {
    my ( $self, $incomeItem ) = @_;
    my $netRate = $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kPrice];

    return ( $netRate * ( $incomeItem->[RPS::ArtistRoyalty::Fast::Final::ArtistRoyaltyIncomeItem::kRate] / 100 ) );
}

1;
