#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package Raptor::SaleReport::BaseReport;
use strict;
use warnings;
use Data::Dumper;

use lib '/app/tools/appuser/lib';
use AppUser::User::User;

use lib '/app/tools/common/lib';
use RSApache::Response::XSLT;
use Common::Assert;
use Common::XMLObject;
use Common::Util;
use Common::Client;
use Common::Consts qw(%CODE_TO_COUNTRY);

use lib '/app/tools/data_classes/lib';
use Period::QuarterPeriod;
use Period::MonthPeriod;
use Period::YearPeriod;
use Product::ProductAlbum;
use Product::ProductTrack;
use Artist;

use lib '/app/tools/raptor/lib';
use SaleReport::ReportLib;
use Raptor::SaleReport::PeriodNav;

use base 'Common::XMLObject';

use constant MAX_RECORDS      => 200;
use constant RECORDS_PER_PAGE => 20;

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

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

    # !!! Note -
    # _Loads_ of refactoring to do here.
    # Need a consistent object model interface for Period, Pie Charts, Label, etc.
    #

    # Initialize the Period object.
    #
    my $periodType = $args{periodType};
    assert( 'm' eq $periodType || 'q' eq $periodType || 'y' eq $periodType, "Bogus period parameter $periodType" );

    my $period;
    if ( 'm' eq $periodType ) {
        $period = new Period::MonthPeriod();
    } elsif ( 'q' eq $periodType ) {
        $period = new Period::QuarterPeriod();
    } elsif ( 'y' eq $periodType ) {
        $period = new Period::YearPeriod();
    }

    # We'll need the period object later.
    # !!! some day we won't need to split the object and it's xml...
    #
    $self->{_period} = $period;

    # Finish initializing Period
    #
    my $periodParam = $args{periodParam};
    if ($periodParam) {
        $self->{_period}->Load( period_id => $periodParam );
    } else {

        #$self->{_period}->LoadCurrent();
        return $self unless ( $self->{_period}->LoadCurrent() );
    }

    $self->{Period} = $period->GetObjectXML();

    # Different subclasses will need slightly different initialization of the
    # stats object.  But many do exactly the same thing.  So I've put that code
    # in a method that they can override if they wish.
    #
    $self->{_statParams} = $self->_initStatsParams(%args);
    $self->{_stats}      = new Stats::Sales( %{ $self->{_statParams} } );

    my $navStats = Stats::Sales->new(
        period_id   => $self->{_period}->PeriodID,
        period_type => $self->{_period}->PeriodType,
        client_id   => Common::RSApp::GetClientID(),
    );

    $self->{PeriodNav} = new Raptor::SaleReport::PeriodNav();

    # we always need sub label xml, unless this user is
    # limited to their own label
    my $allowedLabels;
    my $listOfLabels = $args{commandObj}->_getUser()->getLabelIDs();
    if ( $listOfLabels && scalar @$listOfLabels > 0 ) {
        $allowedLabels = {};
        foreach my $allowedLabelID (@$listOfLabels) {
            $allowedLabels->{$allowedLabelID} = 1;
        }
    }

    my $labels = Client::Label->GetAllLabels();
    foreach my $label_id ( sort { lc( $labels->{$a}->{label_name} ) cmp lc( $labels->{$b}->{label_name} ) } keys %$labels ) {
        if ( defined $allowedLabels && !$allowedLabels->{$label_id} ) {
            next;
        }
        my $pretty_name = $labels->{$label_id}->{label_name};
        if ( length $labels->{$label_id}->{label_name} > 40 ) {
            $pretty_name = substr $pretty_name, 0, 37;
            $pretty_name .= "...";
        }
        push @{ $self->{Labels}->{Label} },
          { LabelID => $label_id, LabelName => $labels->{$label_id}->{label_name}, LabelNamePretty => $pretty_name };
    }

    #	}

    # label specific name
    my $label_param = $args{labelID};
    if ($label_param) {

        # If a user is trying to view a label that they shouldn't,
        # let's throw an error.
        #
        if ( defined $allowedLabels && !$allowedLabels->{$label_param} ) {
            die "ERROR - user is not allowed access to label id $label_param";
        }

        $self->{Label} = { LabelID => $label_param, LabelName => $labels->{$label_param}->{label_name} };
    }

    # country info
    my $geo_map = _mapCountries( $navStats->GetCountryList( all => 1 ) );
    foreach my $ccode ( sort { $geo_map->{$a} cmp $geo_map->{$b} } keys %$geo_map ) {
        push @{ $self->{Countries}->{Country} }, { Code => uc($ccode), Name => $geo_map->{ uc($ccode) } };
    }
    my $current_country = uc( $args{countryCode} );
    $self->{Country} = $CODE_TO_COUNTRY{$current_country} || $current_country;

    return $self;
}

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

    # This hash of parameters gets re-used several times.
    # !!! Why, I don't really understand.  That needs to be fixed.
    #
    my %params = (
        period_id    => $self->{_period}->PeriodID,
        period_type  => $self->{_period}->PeriodType,
        client_id    => Common::RSApp::GetClientID(),
        label_id     => $args{labelID},
        country_code => $args{countryCode},
    );

    return \%params;
}

#---------------------------------------
# helper functions
#---------------------------------------

sub _getPageOffset {
    my ( $self, $page_num ) = @_;

    $page_num = 1 unless $page_num;

    my $max_pages = MAX_RECORDS / RECORDS_PER_PAGE;

    $page_num = 1          if ( $page_num < 1 );
    $page_num = $max_pages if ( $page_num > $max_pages );

    return ( $page_num - 1 ) * RECORDS_PER_PAGE;
}

sub _getPageCount {
    my ( $record_count, $offset ) = @_;
    my $pages = MAX_RECORDS / RECORDS_PER_PAGE;

    if ( $record_count + $offset < MAX_RECORDS ) {
        $pages = int( ( $record_count + $offset ) / RECORDS_PER_PAGE );
        $pages++ if ( ( $record_count + $offset ) % RECORDS_PER_PAGE );
    }

    return $pages;
}

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

    my $stats_obj   = $args{stats_obj};
    my $prod_type   = $args{prod_type};
    my @metric_list = map { $_->ID } @{ $args{metric_list} };

    my ( $products, $count ) = $stats_obj->GetTopRanking(
        category     => 'product',
        product_type => $prod_type,
        limit        => $args{limit}
    );

    my $productsPrev = $stats_obj->GetTopRanking(
        category     => 'product',
        product_type => $prod_type,
        prev_rank => 1
    );

    my $table_data = [];
    foreach my $prodRef ( @$products ) {
        my $prod_id  = $prodRef->ID;
        my $row_data = {};

        $row_data->{Rank}     = $prodRef->Rank;
        $row_data->{Revenue}  = Common::Client::Current()->Locale()->formatMoney( $prodRef->Revenue );
        $row_data->{Units}    = Common::Client::Current()->Locale()->formatNumber( $prodRef->Units );
        $row_data->{PrevRank} = $productsPrev->{$prod_id} || '';

        if ( $prod_type eq 'A' ) {
            my $album_info = new Product::ProductAlbum( product_id => $prod_id );
            $row_data->{ProductInfo}->{AlbumName}   = $album_info->ProductTitle;
            $row_data->{ProductInfo}->{AlbumName}   = "(" . $album_info->CatalogID . ")" unless ( $album_info->AlbumName );
            $row_data->{ProductInfo}->{ArtistName}  = $album_info->ArtistName;
            $row_data->{ProductInfo}->{ProductID}   = $album_info->ProductID;
            $row_data->{ProductInfo}->{ProductType} = $album_info->ProductType;
            $row_data->{ProductInfo}->{CatalogID}   = $album_info->CatalogID;
            $row_data->{ProductInfo}->{ArtistID}    = $album_info->ArtistID;
        } elsif ( $prod_type eq 'T' ) {
            my $track_info = new Product::ProductTrack( product_id => $prod_id );
            $row_data->{ProductInfo}->{TrackName}   = $track_info->TrackName;
            $row_data->{ProductInfo}->{TrackName}   = "(Track " . $track_info->TrackNumber . ")" unless ( $track_info->TrackName );
            $row_data->{ProductInfo}->{ArtistName}  = $track_info->ArtistName;
            $row_data->{ProductInfo}->{ProductID}   = $track_info->ProductID;
            $row_data->{ProductInfo}->{ProductType} = $track_info->ProductType;
            $row_data->{ProductInfo}->{CatalogID}   = $track_info->CatalogID;
            $row_data->{ProductInfo}->{ArtistID}    = $track_info->ArtistID;
        }

        if ( @metric_list ) {
            my $summary_metric = $stats_obj->GetSummaryMetric(
                category    => $args{metric_name},
                metric_list => \@metric_list,
                product_id  => $prod_id
            );

            $row_data->{Data} = [];
            foreach my $metric_id ( @metric_list ) {
                # !!! Have to localize the result set.
                # It would be more efficient to do that in the Stats object, but that would almost
                # certainly break something else..
                push @{ $row_data->{Data} }, Common::Client::Current()->Locale()->formatMoney( $summary_metric->{$metric_id} );
            }
        }

        push @$table_data, $row_data;
    }

    if ( @$table_data ) {
        my @data_labels = @metric_list ? map { $args{metric_hash}->{$_} } @metric_list : undef;
        my $table_href = CreateTableXML( data => $table_data, labels => \@data_labels );

        if ($table_href) {
            if ( $prod_type eq 'A' ) {
                $table_href->{TableName} = 'top_albums';
            } elsif ( $prod_type eq 'T' ) {
                $table_href->{TableName} = 'top_tracks';
            }
            push @{ $args{tables} }, $table_href;
        }
    }

    return $count;
}

sub _getTopArtists {
    my $self        = shift;
    my %args        = @_;
    my $stats_obj   = $args{stats_obj};
    my @metric_list = map { $_->ID } @{ $args{metric_list} };

    my $table_data = [];
    my ( $artists, $count ) = $stats_obj->GetTopRanking( category => 'artist', limit => $args{limit} );
    my $artistsPrev = $stats_obj->GetTopRanking( category => 'artist', prev_rank => 1 );

    foreach my $artistRef (@$artists) {
        my $artist_id = $artistRef->ID;
        my $row_data  = {};

        $row_data->{Rank}     = $artistRef->Rank;
        $row_data->{Revenue}  = Common::Client::Current()->Locale()->formatMoney( $artistRef->Revenue );
        $row_data->{Units}    = Common::Client::Current()->Locale()->formatNumber( $artistRef->Units );
        $row_data->{PrevRank} = $artistsPrev->{$artist_id} || '';

        my $artist_info = new Artist( artist_id => $artist_id );
        $row_data->{ArtistInfo}->{ArtistName} = $artist_info->ArtistName;
        $row_data->{ArtistInfo}->{ArtistID}   = $artist_id;

        if (@metric_list) {
            $row_data->{Data} = [];

            my $summary_metric =
              $stats_obj->GetSummaryMetric( category => $args{metric_name}, metric_list => \@metric_list, artist_id => $artist_id );
            foreach my $metric_id (@metric_list) {
                push @{ $row_data->{Data} }, Common::Client::Current()->Locale()->formatMoney( $summary_metric->{$metric_id} );

                #                push @{ $row_data->{Data} }, $summary_metric->{$metric_id} || 0;
            }
        }

        push @$table_data, $row_data;
    }

    if ( $table_data && ( scalar @$table_data >= 1 ) ) {
        my @data_labels = @metric_list ? map { $args{metric_hash}->{$_} } @metric_list : undef;
        my $table_href = CreateTableXML( data => $table_data, labels => \@data_labels );

        if ($table_href) {
            $table_href->{TableName} = 'top_artists';
            push @{ $args{tables} }, $table_href;
        }
    }

    return $count;
}

sub _sumRevenue {
    my $metric = shift;
    my $sum    = 0;
    map { $sum += $_->Revenue } @$metric;

    return $sum;
}

sub _mapCountries {
    my $country_list = shift;
    my %country_map  = ();

    map { $country_map{$_} = $CODE_TO_COUNTRY{$_} || $_; } @$country_list;

    return \%country_map;
}

sub _getMonthMap {
    return {
        1  => 'Jan',
        2  => 'Feb',
        3  => 'Mar',
        4  => 'Apr',
        5  => 'May',
        6  => 'Jun',
        7  => 'Jul',
        8  => 'Aug',
        9  => 'Sep',
        10 => 'Oct',
        11 => 'Nov',
        12 => 'Dec',
    };
}

1;
