#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2010 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------
package BookPub::Price::Validator;
use strict;
use warnings;

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

use lib '/app/tools/bookpub/lib';
use BookPub::Price::ProductMarketPrice;
use BookPub::Price::PriceTypeWithVariance;
use BookPub::Price::SalePriceException;
use BookPub::Price::SalePriceExceptionRule;
use BookPub::Price::Job::PriceValidation;

use BookPub::DB::Item::BookProduct;
use BookPub::DB::Item::rNativeListPrice;
use BookPub::DB::Item::Sale;
use BookPub::DB::Item::File;

use lib '/app/tools/sale_import/lib';
use base 'Sale::Price::Validator';

sub _init {
    my ( $self, %args ) = @_;

    $self->{_catalogImportID} = $args{catalogImportID};
    return $self->SUPER::_init(%args);
}

sub _endDatePadding   { 2 }
sub _startDatePadding { 0 }

sub _getRecordsToValidate {
    my $self = shift;

    return BookPub::DB::Item::Sale->GetAllWithoutPriceValidation(
        sale_id => $self->{_saleID},
        file_id => $self->{_fileID}
    );

}

sub _clearValidation {
    my $self = shift;
    my %args = @_;

    my $sales = BookPub::DB::Item::Sale->GetAllWithPriceValidation(
        sale_id           => $self->{_saleID},
        file_id           => $self->{_fileID},
        price_type_id     => $self->{_priceTypeID},
        tolerance         => $args{tolerance},
        catalog_import_id => $self->{_catalogImportID}
    );

    while ( my $sale = $sales->next ) {
        $sale->date_price_validated(0);
        $sale->save;

        my $salePriceException = BookPub::DB::Item::SalePriceException->Lookup( sale_id => $sale->sale_id );
        $salePriceException->delete() if ($salePriceException);
    }
}

# Get the allowable price tolerance for a given price type.  Since this requires
# a database lookup we cache these results and only lookup the tolerance once
# for each type.
sub _getTolerance {
    my $self   = shift;
    my $dbItem = shift;

    return unless ( $dbItem->price_type_id );

    my $cache = $self->{_tolerance} || {};

    unless ( $cache->{ $dbItem->price_type_id } ) {
        my $obj = new BookPub::Price::PriceTypeWithVariance( priceTypeID => $dbItem->price_type_id );
        $cache->{ $dbItem->price_type_id } = $obj->AllowedVariance;
        $self->{_tolerance} = $cache;
    }

    return $cache->{ $dbItem->price_type_id };
}

# Get the stored price from the database.
sub _getStoredPrice {
    my $self   = shift;
    my $dbItem = shift;

    assert($dbItem);

    my $currencyCode = $self->_getCurrencyCode($dbItem);

    # We need to make sure this has some value assigned to it.
    # Otherwise the query will ignore price_type_qualifier_id.
    my $priceTypeQualifierID = $dbItem->price_type_qualifier_id;
    if ( !$priceTypeQualifierID ) {
        $priceTypeQualifierID = 0;
    }

    my @prices = BookPub::Price::ProductMarketPrice->GetCurrentPrices(
        start_date              => $dbItem->date_begin,
        end_date                => $dbItem->date_end,
        price_type_id           => $dbItem->price_type_id,
        price_type_qualifier_id => $priceTypeQualifierID,
        currency_code           => $currencyCode,
        product_id              => $dbItem->product_id,
        region_id               => $self->_getRegionIDs( $dbItem->country_code ),
        end_date_padding        => $self->_endDatePadding(),
        start_date_padding      => $self->_startDatePadding(),
    );

    return \@prices;
}

sub _getSalePrice {
    my $self   = shift;
    my $dbItem = shift;

    # First we need to check the r_native_list_price table.
    my $nativePrice = BookPub::DB::Item::rNativeListPrice->Lookup( sale_id => $dbItem->sale_id );
    if ($nativePrice) {
        return $nativePrice->r_native_list_price;
    } else {
        return $dbItem->list_price;
    }
}

sub _createException {
    my $self     = shift;
    my %args     = @_;
    my $saleID   = $args{saleID};
    my $variance = $args{variance};
    my $price    = $args{price};
    my $approved = $args{approved};

    my ( $priceID, $catalogPrice, $currency, $start, $end );

    if ($price) {
        $priceID      = $price->ProductMarketPriceID;
        $catalogPrice = $price->Price;
        $currency     = $price->CurrencyCode;
        $start        = $price->StartDate;
        $end          = $price->EndDate;
    }

    my $obj = new BookPub::Price::SalePriceException(
        saleID               => $saleID,
        variance             => $variance,
        approved             => $approved,
        productMarketPriceID => $priceID,
        price                => $catalogPrice,
        currencyCode         => $currency,
        startDate            => $start,
        endDate              => $end,
    );

    $obj->save();
}

sub _isFree {
    my $self   = shift;
    my $dbItem = shift;

    return $dbItem->is_free && uc( $dbItem->is_free ) eq 'Y' ? 1 : undef;
}

sub _getUnits {
    my $self   = shift;
    my $dbItem = shift;

    return $dbItem->units;
}

sub _checkForAutoException {
    my $self   = shift;
    my $dbItem = shift;
    my $bookProduct = BookPub::DB::Item::BookProduct->Lookup( product_id => $dbItem->product_id);
    my $publishingStatus = $bookProduct->onix_code_publishing_status;

    # We want 'out of print' products to create a price exception every time,
    # even if they have been approved in the past.
    return $publishingStatus eq BookPub::DB::Item::BookProduct::kStatusOutOfPrint ? 1 : undef;
}

sub _autoMatchRule {
    my $self   = shift;
    my $dbItem = shift;

    my $currencyCode = $self->_getCurrencyCode($dbItem);
    my $listPrice    = $self->_getSalePrice($dbItem);

    my $obj = BookPub::Price::SalePriceExceptionRule->new(
        serviceID            => $dbItem->service_id,
        version              => $dbItem->{version_num},
        listPrice            => $listPrice,
        currencyCode         => $currencyCode,
        priceTypeID          => $dbItem->price_type_id,
        priceTypeQualifierID => $dbItem->price_type_qualifier_id,
        productID            => $dbItem->product_id,
        countryCode          => $dbItem->country_code,
    );

    return $obj->match();
}

sub _validationJobObject {
    my $self = shift;
    my %args = @_;

    return BookPub::Price::Job::PriceValidation->new(
        fileID          => $self->{_fileID},
        saleID          => $self->{_saleID},
        priceTypeID     => $self->{_priceTypeID},
        catalogImportID => $self->{_catalogImportID},
        tolerance       => $args{tolerance},
    );
}

sub _updateValidationCounts {
    my $self   = shift;
    my $fileID = $self->{_fileID};

    if ( $self->{_saleID} ) {
        my $sale = BookPub::DB::Item::Sale->Lookup( sale_id => $self->{_saleID} );
        $fileID = $sale->file_id() if ($sale);

        unless ( $sale || $fileID ) {
            Common::Log::Debug("WARNING: Sale does not exists or has no file id");
            return;
        }
    }

    BookPub::DB::Item::File->updatePriceValidationCounts( file_id => $fileID );
}

sub _getRegionIDs {
    my $self        = shift;
    my $countryCode = shift || die;
    my $cache       = $self->{_regionCache} || {};

    unless ( $cache->{$countryCode} ) {
        my @regions;
        Common::Log::Debug(" ++ Cache region not found, looking up for '$countryCode'");

        my $regions = BookPub::DB::Item::Region->GetAllRegions( country_code => $countryCode );

        assert( $regions->hasNext(), "Could not find region for country code '$countryCode'" );

        while ( my $region = $regions->next ) {
            push( @regions, $region->region_id );
        }
        $cache->{$countryCode} = \@regions, $self->{_regionCache} = $cache;
    }

    return $cache->{$countryCode};
}

sub _getCurrencyCode {
    my $self   = shift;
    my $dbItem = shift;

    my $currencyCode;

    # First we need to check the r_native_list_price table.
    my $nativePrice = BookPub::DB::Item::rNativeListPrice->Lookup( sale_id => $dbItem->sale_id );
    if ($nativePrice) {
        $currencyCode = $nativePrice->r_native_list_price_currency;
    } else {
        $currencyCode = $dbItem->r_list_price_currency ? $dbItem->r_list_price_currency : $dbItem->currency_code;
    }

    return $currencyCode;
}

###
1;    #
###
