package BookPub::Sale::Report::Royalty::BerrettKoehler;

use strict;
use Template;
use Data::Dumper;

use lib '/app/tools/common/lib';
use Common::Log;
use Common::Client;
use Common::DB::ItemCollection;
use Common::DB::Item;
use Common::Util qw(formatFixedPoint);

use lib '/app/tools/bookpub/lib';
use BookPub::DB::Item::Book;
use BookPub::DB::Item::BookProduct;
use BookPub::DB::Item::ClientService;
use BookPub::DB::Item::Period;
use BookPub::DB::Item::Sale;
use BookPub::DB::Item::Service;
use BookPub::Tracker::Service;

use base 'BookPub::Sale::Report::Royalty';

use constant kDB         => Common::DB::Item::kClientDB;
use constant OUTDIR_BASE => '/app/data/sale_report/tmp/';

my %HEADER_MAIN = (
    _order        => [qw(doc_type quantity extension product_code doc_id customer_san customer_name title product_type)],
    doc_type      => 'Doc Type',
    quantity      => 'Quantity',
    extension     => 'Extension',
    product_code  => 'Product Code',
    doc_id        => 'Doc ID',
    customer_san  => 'Customer SAN',
    customer_name => 'Customer Name',
    title         => 'Title',
    product_type  => 'Product Type',
);

sub new {
    my $class = shift;

    my $self = {};
    bless $self, $class;

    $self->_init(@_);

    return $self;
}

sub fileName {
    my ($self) = @_;
    if ( !$self->{_fileName} ) {
        my $client    = Common::Client::Current();
        my $period    = BookPub::DB::Item::Period->Lookup( period_id => $self->{_periodID} );
        my $periodRef = $period->name();
        $periodRef =~ s/\s+/_/g;
        $periodRef =~ s/\W+//g;
        $periodRef =~ s/__/_/g;
        $periodRef ||= $period->period_id();

        $self->{_fileName} = "BKP_RoyaltyReport_$periodRef.tsv";
    }
    return $self->{_fileName};
}

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

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

    $self->{clientID} = $args{clientID} ? $args{clientID} : Common::RSApp::GetClientID();
    $self->{tmplObj} = Template->new( { START_TAG => '{', END_TAG => '}' } );

}

sub create {
    my $self = shift;
    my %tmplVars;

    # We'll keep a separate tally of units and revenue and
    # return those at the end for validation.
    my $totalUnits;
    my $totalRevenueUnrounded;

    my %serviceNames;

    my $periodID   = $self->periodID();
    my $period     = BookPub::DB::Item::Period->Lookup( period_id => $periodID );
    my $periodName = $period->name;
    my $docID;

    if ( $periodName =~ /(\d+\/\d+\/\d+)/ ) {
        $docID = $1;
    }

    my $sales = BookPub::DB::Item::Sale->GetFinalSalesByPeriod( $self->periodID() );

    my $outFilePath = $self->filePath();

    unless ( open( OUT, '>:utf8', $outFilePath ) ) {
        die "can't open output file ($outFilePath) - $!\n";
    }

    # print the main header
    my @headers = @HEADER_MAIN{ @{ $HEADER_MAIN{_order} } };
    print OUT join( "\t", @headers ), "\n";

    my %rows;

    while ( my $sale = $sales->next() ) {
        my $units        = abs( $sale->units );
        my $revenue      = $sale->revenue;
        my $currencyCode = $sale->currency_code;

        if ( $currencyCode ne 'USD' ) {
            my $conversionRate = $sale->conversion_rate;
            if ($conversionRate) {
                $revenue *= $conversionRate;
            }
        }

        $totalUnits += $sale->units;
        $totalRevenueUnrounded += $revenue;

        $revenue = abs($revenue);

        my $productID   = $sale->product_id;
        my $productType = $sale->product_type;

        my $bookProduct = BookPub::DB::Item::BookProduct->Lookup( product_id => $productID );
        my $book  = BookPub::DB::Item::Book->Lookup( book_id => $bookProduct->book_id );
        my $title = $book->title;
        my $isbn  = $bookProduct->isbn13;
        if ( !$isbn ) {
            $isbn = $bookProduct->isbn10;
        }

        my $docType;
        if ( $sale->units > 0 ) {
            $docType = 'Invoice';
        } elsif ( $sale->units < 0 ) {
            $docType = 'CM';
        } else {
            next;
        }

        my $price = formatFixedPoint( $revenue / $units, 2 );

        my $serviceID   = $sale->service_id;
        my $service     = BookPub::DB::Item::Service->Lookup( service_id => $serviceID );
        my $serviceName = $service->service_name;

        my ( $customerSAN, %sanCriteria ) = $self->_getSAN($sale);

        #  If we still don't have one, time to error out.
        if ( !$customerSAN ) {
            my $error = "No match found in client service table for sale id " . $sale->sale_id;
            $self->_sendErrorEmail($error);
            die $error . "\n";
        }

        $rows{$serviceName}{$customerSAN}{$title}{$productType}{$isbn}{$price}{$docType}{units}   += $units;
        $rows{$serviceName}{$customerSAN}{$title}{$productType}{$isbn}{$price}{$docType}{revenue} += $revenue;

    }

    foreach my $serviceName ( sort keys %rows ) {
        my $serviceSales = $rows{$serviceName};

        foreach my $customerSAN ( sort keys %$serviceSales ) {
            my $customerSANsales = $serviceSales->{$customerSAN};

            foreach my $title ( sort keys %$customerSANsales ) {
                my $titleSales = $customerSANsales->{$title};

                foreach my $productType ( sort keys %$titleSales ) {
                    my $productTypeSales = $titleSales->{$productType};

                    foreach my $isbn ( sort keys %$productTypeSales ) {
                        my $isbnSales = $productTypeSales->{$isbn};

                        foreach my $price ( sort keys %$isbnSales ) {
                            my $priceSales = $isbnSales->{$price};

                            foreach my $rowType ( sort keys %$priceSales ) {
                                my $row = $priceSales->{$rowType};

                                $tmplVars{doc_type}      = $rowType;
                                $tmplVars{quantity}      = $row->{units};
                                $tmplVars{extension}     = formatFixedPoint( $row->{revenue}, 2 );
                                $tmplVars{product_code}  = $isbn;
                                $tmplVars{doc_id}        = $docID . $customerSAN;
                                $tmplVars{customer_san}  = $customerSAN;
                                $tmplVars{customer_name} = $serviceName;
                                $tmplVars{title}         = $title;
                                $tmplVars{product_type}  = $productType;

                                my @detailRow = @tmplVars{ @{ $HEADER_MAIN{_order} } };
                                print OUT join( "\t", @detailRow ), "\n";
                            }
                        }
                    }
                }
            }
        }
    }

    close(OUT);

    # Going to send everything back in a hash so that's it's
    # easy to add more stuff in later.
    #
    my %returnValues;
    $returnValues{totalUnits}            = $totalUnits;
    $returnValues{totalRevenueUnrounded} = $totalRevenueUnrounded;

    return (%returnValues);

}

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

    my $clientService;
    my $san;

    my $serviceID    = $sale->service_id;
    my $currencyCode = $sale->currency_code;
    my $productType  = $sale->product_type;

    # We need to send a back the list of fields and their values that we used to look for the SAN for a service.
    # Service ID is always a criteria, so you don't need to add that in here _unless_ it is the only criteria.
    my %sanCriteria;

    # ebooks.com (service ID 10) has two different client service IDs, one for each feed.
    # So, we'll have to look it up a little differently.
    if ( $serviceID == BookPub::Tracker::Service::EBOOK() ) {
        my $file     = BookPub::DB::Item::File->Lookup( file_id => $sale->file_id );
        my $fileName = $file->orig_file_name;
        my $feedID   = lc( substr( $fileName, 0, 3 ) );

        # We're going to default the feed id to 'ebc', because older versions of the sales
        # files did not include the feed id prefix.
        if ( $feedID ne 'ebc' && $feedID ne 'ebl' ) {
            $feedID = 'ebc';
        }

        $sanCriteria{'Feed ID'}      = $feedID;
        $sanCriteria{'Product Type'} = $productType;

        $clientService =
          BookPub::DB::Item::ClientService->Lookup( service_id => $serviceID, feed_id => $feedID, product_type => $productType );
    } elsif ( $serviceID == BookPub::Tracker::Service::INGRAM_DIGITAL() ) {
        my $file     = BookPub::DB::Item::File->Lookup( file_id => $sale->file_id );
        my $fileName = lc( $file->orig_file_name );
        my ($feedID) = $fileName =~ /\.\w\w\w\d\d\.(\w+)\./i;

        $clientService =
          BookPub::DB::Item::ClientService->Lookup( service_id => $serviceID, feed_id => $feedID, product_type => $productType );
    } elsif ( $serviceID == BookPub::Tracker::Service::VITAL_SOURCE() ) {
        my $file     = BookPub::DB::Item::File->Lookup( file_id => $sale->file_id );
        my $fileName = lc( $file->orig_file_name );
        my ($feedID) = $fileName =~ /\.\w\w\w-\d\d\.VST(?:\w+)?_(\w+)\./i;

        # if the fileName isn't associated with "Vital Source B & N Edu", we use the data for the combined format
        $feedID ||= 'ANY';

        $clientService =
          BookPub::DB::Item::ClientService->Lookup( service_id => $serviceID, feed_id => $feedID, product_type => $productType );
    } else {

        # First, try to find a client service match using the service id, product type, and currency code.
        $clientService = BookPub::DB::Item::ClientService->Lookup(
            service_id    => $serviceID,
            currency_code => $currencyCode,
            product_type  => $productType
        );

        $sanCriteria{'Currency Code'} = $currencyCode;
        $sanCriteria{'Product Type'}  = $productType;

        # If that didn't work, try just using the service id and product type.
        if ( !$clientService ) {
            $clientService =
              BookPub::DB::Item::ClientService->Lookup( service_id => $serviceID, currency_code => "ANY", product_type => $productType );
        }
    }

    if ($clientService) {
        $san = $clientService->client_service_id;
    }

    return ( $san, %sanCriteria );
}

sub validateFile {
    my ( $self, $fileID ) = @_;

    # This is the hash we'll send back all of the data in.
    # We'll need to know exactly which fields are missing.
    my %validation;

    # If everything is good, this is all we'll send back.
    $validation{isValid} = 1;

    my $i = 1;
    my %seen;

    # We need to check each sale for a SAN.
    my $sales = BookPub::DB::Item::Sale->GetAllByFileID($fileID);

    while ( my $sale = $sales->next() ) {
        my ( $customerSAN, %sanCriteria ) = $self->_getSAN($sale);

        if ( !$customerSAN ) {
            $validation{isValid} = undef;

            # We need to trim this down to the unique sets of SAN criteria.
            my $seenString = $sale->service_id . "-";
            foreach my $criteria ( sort keys %sanCriteria ) {
                $seenString .= $criteria . "-" . $sanCriteria{$criteria} . "-";
            }

            if ( !$seen{$seenString} ) {
                foreach my $criteria ( sort keys %sanCriteria ) {
                    $validation{"MultipleSANs"}{$i}{$criteria} = $sanCriteria{$criteria};
                }

                $i++;
                $seen{$seenString} = 1;
            }
        }
    }

    return %validation;
}

1;

