package BookPub::Catalog::BookProductListBatch;

use strict;
use warnings;

use lib '/app/tools/common/lib';
use lib '/app/tools/bookpub/lib';

use Common::Assert;
use Common::CurrencyFormat;
use Common::Client;
use Common::Util;
use BookPub::DB::Item::BookProduct;
use BookPub::DB::Item::ProductMarketPrice;
use BookPub::Price::DefaultPrice::PrimaryMarket;
use BookPub::Price::DefaultPrice::SecondaryMarket;

use base 'Common::XMLObject';

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

    $self->SUPER::_init(%args);

    my $bookID = $args{bookID};
    assert($bookID);

    $self->{BookProduct} = [];
    my $aProductRows = BookPub::DB::Item::BookProduct->GetRowsForShowBookByBookID($bookID);
    return $self unless @$aProductRows;

    my ( $hProductByID, $aProductIDs ) = $self->_buildProductRows($aProductRows);
    my $aPriceRows = BookPub::DB::Item::ProductMarketPrice->GetRowsForShowBookByProductIDs( $aProductIDs );

    $self->_attachPricingData( $hProductByID, $aPriceRows );
    $self->_attachDefaultMarkets( $hProductByID, $aProductIDs, $aPriceRows );

    foreach my $productID ( @$aProductIDs ) {
        push @{ $self->{BookProduct} }, $hProductByID->{$productID};
    }

    return $self;
}

sub _buildProductRows {
    my ( $self, $aRows ) = @_;

    my %productByID;
    my @productIDs;

    foreach my $hRow ( @$aRows ) {
        my $productID = $hRow->{product_id};

        my $hProduct = {
            ProductID                => $productID,
            OnixCodePublishingStatus => $hRow->{onix_code_publishing_status},
            ISBN10                   => $hRow->{isbn10},
            ISBN13                   => $hRow->{isbn13},
            Edition                  => $hRow->{edition},
            ImprintID                => $hRow->{imprint_id},
            ReleaseDate              => $self->_formatDate($hRow->{release_date}),
            ProductFormat            => {
                BookFormatType => {
                    Code        => $hRow->{format_code},
                    Description => $hRow->{format_description},
                },
            },
            Pricing => [],
        };

        if ( defined $hRow->{subtype_description} && $hRow->{subtype_description} ne '' ) {
            $hProduct->{ProductFormat}{BookFormatSubtype} = {
                Code        => $hRow->{subtype_code},
                Description => $hRow->{subtype_description},
            };
        }

        $productByID{$productID} = $hProduct;
        push @productIDs, $productID;
    }

    return ( \%productByID, \@productIDs );
}

sub _attachPricingData {
    my ( $self, $hProductByID, $aPriceRows ) = @_;

    my %marketByProduct;
    my %currencyByMarket;
    my %currencyFormatCache;

    foreach my $hRow (@$aPriceRows) {
        my $productID = $hRow->{product_id};
        my $product   = $hProductByID->{$productID};
        next unless $product;

        my $productMarketID = $hRow->{product_market_id};

        my $hMarket = $marketByProduct{$productID}{$productMarketID};
        if ( !$hMarket ) {
            $hMarket = {
                ProductMarketID => $productMarketID,
                MarketID        => $hRow->{market_id},
                MarketRegionID  => $hRow->{market_region_id},
                Currency        => [],
            };
            $marketByProduct{$productID}{$productMarketID} = $hMarket;
        }

        my $currencyCode = $hRow->{currency_code};
        next unless $currencyCode;

        my $hCurrency = $currencyByMarket{$productID}{$productMarketID}{$currencyCode};
        if ( !$hCurrency ) {
            my $currencyFormat = $currencyFormatCache{$currencyCode};
            if ( !$currencyFormat ) {
                $currencyFormat = Common::CurrencyFormat->new( currencyCode => $currencyCode );
                $currencyFormatCache{$currencyCode} = $currencyFormat;
            }
            $hCurrency = {
                CurrencyCode     => $currencyCode,
                CurrencyCodeName => $currencyFormat ? $currencyFormat->description() : undef,
                CurrencySymbol   => Common::CurrencyFormat::Symbol($currencyCode),
                Prices           => { ProductMarketPrice => [] },
            };
            push @{ $hMarket->{Currency} }, $hCurrency;
            $currencyByMarket{$productID}{$productMarketID}{$currencyCode} = $hCurrency;
        }

        my $status = $self->_getPriceStatus( $hRow->{start_date}, $hRow->{end_date} );

        push @{ $hCurrency->{Prices}{ProductMarketPrice} }, {
            ProductMarketPriceID => $hRow->{product_market_price_id},
            ProductMarketID      => $productMarketID,
            MarketID             => $hRow->{market_id},
            CurrencyCode         => $currencyCode,
            PriceTypeID          => $hRow->{price_type_id},
            PriceTypeQualifierID => $hRow->{price_type_qualifier_id},
            Price                => $self->_formatPriceWithSymbol( $hRow->{price}, $hCurrency->{CurrencySymbol} ),
            Status               => $status,
            RegionID             => $hRow->{price_region_id},
            StartDate            => $hRow->{start_date},
            EndDate              => $hRow->{end_date},
        };
    }

    foreach my $productID ( keys %marketByProduct ) {
        my @markets = map { $marketByProduct{$productID}{$_} }
                      sort { $a <=> $b } keys %{ $marketByProduct{$productID} };
        $hProductByID->{$productID}{Pricing} = [ map { { Market => $_ } } @markets ];
    }
}

sub _attachDefaultMarkets {
    my ( $self, $hProductByID, $aProductIDs, $aPriceRows ) = @_;

    my $primaryCountry = Common::Client::Current()->Locale()->CountryCode();
    my $secondaryCountry = Common::Client::Current()->Locale()->SecondaryMarketCountryCode();

    my $primaryCurrencyCode = $primaryCountry
        ? Common::CurrencyFormat->new( countryCode => $primaryCountry )->currencyCode()
        : '';
    my $secondaryCurrencyCode = $secondaryCountry
        ? Common::CurrencyFormat->new( countryCode => $secondaryCountry )->currencyCode()
        : '';

    my %priceRowsByProduct;
    foreach my $hRow ( @$aPriceRows ) {
        push @{ $priceRowsByProduct{ $hRow->{product_id} } }, $hRow;
    }

    foreach my $productID ( @$aProductIDs ) {
        my $product = $hProductByID->{$productID};
        my $aRows   = $priceRowsByProduct{$productID} || [];

        my ( $primaryMarketID, $primaryCurrency ) = $self->_pickDefaultFromRows( $aRows, $primaryCurrencyCode );
        my ( $secondaryMarketID, $secondaryCurrency ) = $self->_pickDefaultFromRows( $aRows, $secondaryCurrencyCode );

        if ( !$primaryMarketID ) {
            my $oPrimary = BookPub::Price::DefaultPrice::PrimaryMarket->new( productID => $productID );
            $primaryMarketID = $oPrimary->{MarketID};
            $primaryCurrency = $oPrimary->{Currency} ? $oPrimary->{Currency}->currencyCode() : '';
        }

        if ( $secondaryCountry && !$secondaryMarketID ) {
            my $oSecondary = BookPub::Price::DefaultPrice::SecondaryMarket->new( productID => $productID );
            $secondaryMarketID = $oSecondary->{MarketID};
            $secondaryCurrency = $oSecondary->{Currency} ? $oSecondary->{Currency}->currencyCode() : '';
        }

        $product->{DefaultPrimary} = {
            MarketID => $primaryMarketID,
            Currency => {
                CurrencyCode => $primaryCurrency || '',
            },
        };

        $product->{DefaultSecondary} = {
            MarketID => $secondaryMarketID,
            Currency => {
                CurrencyCode => $secondaryCurrency || '',
            },
        };
    }
}

sub _pickDefaultFromRows {
    my ( $self, $aRows, $targetCurrencyCode ) = @_;

    return ( undef, '' ) unless $aRows && @$aRows;

    my $matchCurrency;
    my $first;

    foreach my $hRow (@$aRows) {
        $first ||= $hRow;

        my $currencyMatch = 0;
        if ( $targetCurrencyCode && $hRow->{currency_code} ) {
            $currencyMatch = 1 if $hRow->{currency_code} eq $targetCurrencyCode;
        }
        $matchCurrency ||= $hRow if $currencyMatch;
    }

    my $picked = $matchCurrency || $first;
    return ( $picked->{market_id}, ( $picked->{currency_code} || '' ) ) if $picked;

    return ( undef, '' );
}

sub _formatPriceWithSymbol {
    my ( $self, $price, $symbol ) = @_;

    return $price if !$price;
    return $price if !$symbol;
    return $price if $price =~ /^\Q$symbol\E/;

    return $symbol . $price;
}

sub _getPriceStatus {
    my ( $self, $startDateTime, $endDateTime ) = @_;

    my $startDate;
    my $now = time();

    if ($startDateTime) {
        ($startDate) = split( / /, $startDateTime );
        my $year = substr( $startDate, 0, 4 );
        $startDate = '0000-00-00' if $year < 1970;
    } else {
        $startDate = '0000-00-00';
    }

    my $start = $startDate eq '0000-00-00' ? 0 : Common::Util::dateTimeToSeconds("$startDate 00:00:00");

    return 'future'  if $now < $start;
    return 'current' if !$endDateTime;

    my ($endDate) = split( / /, $endDateTime );
    my $end = Common::Util::dateTimeToSeconds("$endDate 23:23:59") if $endDate ne '0000-00-00';

    return 'current' unless $end;

    return $end < $now ? 'historical' : 'current';
}

sub _formatDate {
    my ( $self, $dateStr ) = @_;
    return '' unless $dateStr;

    if ( my $formatted = Common::Client::Current()->Locale()->formatDate($dateStr) ) {
        return $formatted;
    }

    return $dateStr;
}


1;