package RPS::Import::BMG::v2;

use strict;
use warnings;
use POSIX;

use lib '/app/tools/rps/lib';
use RPS::File::Sale;
use RPS::Import::ServiceMap;
use Date::Calc qw(Days_in_Month Add_Delta_Days Decode_Month);

use parent 'RPS::Import::ImporterMixed';

sub physical { 2 }
sub _dateFormat { [ 'iso', 'text' ] }

my %sheetToHeaderMap = (
    'digital'      => [ 'digital',  'option1' ],
    'physical us'  => [ 'physical', 'option1' ],
    'physical row' => [ 'physical', 'option1' ],
);

my %sheetsToSkip = (
    'SURFDOGBMG'                  => 1,
    'SURFDOG-BMG'                 => 1,
    'Ex-US Cash Disc Sales Allow' => 1,
    'Expenses - USD stmt'         => 1,
    'Expenses - GBP stmt'         => 1,
    'Inventory'                   => 1,
    'Reconciliation'              => 1,
);

sub _fieldMapExtended {
    {
        digital => {
            option1 => {    # Digital
                labelName   => 'A',
                serviceID   => 'M',
                countryCode => 'P',
                dateBegin   => 'C',
                productType => 'H',
                formatType  => 'H',
                artistName  => 'E',
                upc         => 'J',
                albumName   => 'G',
                isrc        => 'L',
                trackName   => 'F',
                units       => 'U',
                price       => 'X',
                mediaType   => 'H',
            },
        },
        physical => {
            option1 => {    # "Physical US" or "Physical ROW"
                labelName      => 'A',
                countryCode    => 'P',
                dateBegin      => 'C',
                productType    => 'J',
                artistName     => 'E',
                upc            => 'K',
                albumName      => 'G',
                priceLevel     => 'I',
                wholesalePrice => 'R',
                retailPrice    => 'Q',
                sales          => 'U',
                salesRevenue   => 'AC',
                returns        => 'X',
                returnsRevenue => 'AF',
                totalRevenue   => 'AG',
                mediaType      => 'J',
            },
        },
    };

}

sub _unverifiedFieldMapExtended {
    {
        physical => {
            option1 => {    # "Physical US" and "Physical ROW"
                _netUnits => 'Y',
                _promo    => 'W',
            },
        },
    };
}

sub _headerIdentifierExtended {
    my $self = shift;

    my $hHeader = {
        digital => {
            option1 => {    # Digital
                price => 'Net Income',
            },
        },
        physical => {
            option1 => {    # "Physical US" and "Physical ROW"
                totalRevenue => 'Net Sales',
            },
        },
    };

    my $sheetName = $self->_sheetName;

    # for the structure validation
    return $hHeader unless $sheetName;

    # didn't find a sheetname
    return {} unless exists $sheetToHeaderMap{$sheetName};

    if ( exists $sheetToHeaderMap{$sheetName} ) {
        my $type   = $sheetToHeaderMap{$sheetName}[0];
        my $option = $sheetToHeaderMap{$sheetName}[1];
        return {
            $type => {
                $option => $hHeader->{$type}{$option}
            }
        };
    }
}

my %sheetRules = (

    # digital
    'digital' => {
        productType => sub {
            return RPS::File::Sale::TYPE_TRACK if $_[1] =~ /^(?:EMDTrack|EMDTrackStreaming)$/i;
            return RPS::File::Sale::TYPE_ALBUM if $_[1] =~ /^EMDAlbum$/i;

            # Additional rules, order is important
            my $upc  = $_[0]->_getByFieldName('upc')  || '';
            my $isrc = $_[0]->_getByFieldName('isrc') || '';

            if ( $_[1] =~ /^(?:Emdsinglestreaming|EVD)$/i ) {
                if ( $upc && !$isrc ) {
                    return RPS::File::Sale::TYPE_ALBUM;
                } else {
                    return RPS::File::Sale::TYPE_TRACK;
                }
            }

            if ( $_[1] =~ /^(?:eMastertone|EVDStreaming)$/i ) {
                if ( !$upc && $isrc ) {
                    return RPS::File::Sale::TYPE_TRACK;
                } else {
                    return RPS::File::Sale::TYPE_ALBUM;
                }
            }
        },
        formatType => sub {
            return RPS::File::Sale::FORMAT_DOWNLOAD if $_[1] =~ /^(?:EMDTrack|EMDAlbum|EVD)$/i;
            return RPS::File::Sale::FORMAT_STREAM   if $_[1] =~ /^(?:Emdsinglestreaming|EMDTrackStreaming|EVDStreaming)$/i;
            return RPS::File::Sale::FORMAT_RINGTONE if $_[1] =~ /^eMastertone$/i;
        },
        isrc => sub {
            return $_[0]->_getByFieldName('isrc') if $_[0]->productType && $_[0]->productType eq RPS::File::Sale::TYPE_TRACK;
        },
        trackName => sub {
            return $_[0]->_getByFieldName('trackName') if $_[0]->productType && $_[0]->productType eq RPS::File::Sale::TYPE_TRACK;
        },
        mediaType => sub {
            return RPS::DB::Item::MediaType::kMediaTypeAudio
              if $_[1] =~ /^(?:EMDTrack|EMDAlbum|eMastertone|Emdsinglestreaming|EMDTrackStreaming)$/i;
            return RPS::DB::Item::MediaType::kMediaTypeVideo if $_[1] =~ /^(?:EVD|EVDStreaming)$/i;
        },
    },

    # physical
    'physical us' => {
        serviceID   => Client::Service::DSP_BMG,
        productType => sub {
            return RPS::File::Sale::TYPE_BLURAY if $_[1] =~ qr/^Blu-ray/i;
            return RPS::File::Sale::TYPE_BLURAY if $_[1] =~ qr/^Blu-ray with CD/i;
            return RPS::File::Sale::TYPE_CD     if $_[1] =~ qr/^CD$/i;
            return RPS::File::Sale::TYPE_CD     if $_[1] =~ qr/^CD 5-inch$/i;
            return RPS::File::Sale::TYPE_LP     if $_[1] =~ qr/^Vinyl$/i;
            return RPS::File::Sale::TYPE_LP     if $_[1] =~ qr/^Vinyl 12-inch, 45 rpm$/i;
            return RPS::File::Sale::TYPE_LP5    if $_[1] =~ qr/^Vinyl 7-inch, 45 rpm$/i;
            return RPS::File::Sale::TYPE_DVD    if $_[1] =~ qr/^DVD-Video$/i;
        },
        isrc => sub {
            return '';
        },
        mediaType => sub {
            return RPS::DB::Item::MediaType::kMediaTypeVideo if $_[1] =~ qr/^(?:DVD|Blu)/i;
            return RPS::DB::Item::MediaType::kMediaTypeAudio;
        },
    },
    'physical row' => {    # Same as 'physical us'
        serviceID   => Client::Service::DSP_BMG,
        productType => sub {
            return RPS::File::Sale::TYPE_BLURAY if $_[1] =~ qr/^Blu-ray/i;
            return RPS::File::Sale::TYPE_BLURAY if $_[1] =~ qr/^Blu-ray with CD/i;
            return RPS::File::Sale::TYPE_CD     if $_[1] =~ qr/^CD$/i;
            return RPS::File::Sale::TYPE_CD     if $_[1] =~ qr/^CD 5-inch$/i;
            return RPS::File::Sale::TYPE_LP     if $_[1] =~ qr/^Vinyl$/i;
            return RPS::File::Sale::TYPE_LP     if $_[1] =~ qr/^Vinyl 12-inch, 45 rpm$/i;
            return RPS::File::Sale::TYPE_LP5    if $_[1] =~ qr/^Vinyl 7-inch, 45 rpm$/i;
            return RPS::File::Sale::TYPE_DVD    if $_[1] =~ qr/^DVD-Video$/i;
        },
        isrc => sub {
            return '';
        },
        mediaType => sub {
            return RPS::DB::Item::MediaType::kMediaTypeVideo if $_[1] =~ qr/^(?:DVD|Blu)/i;
            return RPS::DB::Item::MediaType::kMediaTypeAudio;
        },
    }
);

sub _sheetName {
    my $self = shift;

    my $name = $self->sheetname() || '';
    $name =~ s/^\s+|\s+$//g;
    $name =~ s/[^0-9a-zA-Z\s]//g;
    $name =~ s/\s+/ /g;
    $name =~ s/(.*)/\L$1/;

    return $name;
}

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

    my $sheet = $self->_sheetName();
    my ($method) = ( caller(1) )[3] =~ /.*::(.+)$/;
    return unless exists $sheetRules{$sheet} && exists $sheetRules{$sheet}{$method};

    my $rule = $sheetRules{$sheet}{$method};
    return ref $rule eq 'CODE' ? &$rule( $self, @args ) : $rule;
}

sub channel      { RPS::File::Sale::CHANNEL_RETAIL }
sub currencyCode { shift->{_currencyCode} }
sub free         { 0 }

sub _processSheet {
    my ( $self, $sheet, $sheetnum ) = @_;

    if ( $self->_sheetName =~ /^surfdog\-?bmg$/i ) {
        foreach my $aLine ( @$sheet[ 0 .. 10 ] ) {
            next unless ref $aLine eq 'ARRAY';

            my $dateHeader     = $aLine->[0] || '';
            my $currencyHeader = $aLine->[8] || '';

            # determine currency code
            if ( $currencyHeader =~ / (\w{3})$/ ) {
                $self->{_currencyCode} = $1 unless $self->{_currencyCode};
            }

            # determine statement date
            if ( $dateHeader =~ /^(\w+ \d{4}) Statement$/ ) {
                $self->{_dateBegin} = $1 unless $self->{_dateBegin};
            }
        }
        return;
    } elsif ( exists $sheetToHeaderMap{ lc( $self->_sheetName ) } ) {
        my $headerFound;
        $headerFound = $self->SUPER::_processSheet( $sheet, $sheetnum );
        if ( !$headerFound ) {
            $self->fail( "Unable to find header for '" . $self->{sheetname} . "' sheet" );
        }
        return;
    }

    # Error out if we don't recognize the sheetname
    # We'll probably want to add some exceptions to this, so that
    # we can ignore specific sheets without throwing an error.
    if ( !$sheetsToSkip{ $self->{sheetname} } ) {
        $self->fail( "Encountered unrecognized sheet '" . $self->{sheetname} . "'" );
    }

    return;
}

sub _getDateBegin {
    my $self = shift;

    my $date = $self->_getByFieldName('dateBegin');

    # If the date is missing on the "Digital" tab, use the date from the summary page.
    if ( $self->sheetname() =~ /^digital$/i ) {
        if ( !$date || $date eq '-' ) {
            $date = $self->{_dateBegin};
        }
    }

    if ( $date =~ /^\d+$/ ) {
        $date = Spreadsheet::ParseExcel::Utility::ExcelFmt( "yyyy-mm-dd", $date );
    }

    return $date;
}

sub serviceID {
    my $self = shift;

    my $result = $self->_ruleException();
    return $result if $result;

    my $service = lc( $self->_getByFieldName('serviceID') || '' );

    my $serviceID = $self->getServiceID($service) || $RPS::Import::ServiceMap::SERVICE_ALIASES{$service};
    return $serviceID if $serviceID;

    return Client::Service::DSP_RDIO        if $service =~ /^rdio/i;
    return Client::Service::DSP_SLACKERINC  if $service =~ /^slacker/i;
    return Client::Service::DSP_IHEARTRADIO if $service =~ /^iheartradio/i;
    return Client::Service::DSP_IHEARTMEDIA if $service =~ /^iheartmedia/i;
    return Client::Service::DSP_PANDORA     if $service =~ /^pandora/i;
    return Client::Service::DSP_ITUNES      if $service =~ /^apple iradio/i;

    return $self->fail("Unable to determine service from: '$service' ");
}

sub productType {
    my $self = shift;

    my $type = $self->_getByFieldName('productType') || '';

    my $result = $self->_ruleException($type);
    return $result if $result;

    return $self->fail("Unable to determine product_type from: '$type'.");
}

sub formatType {
    my $self = shift;

    my $type = $self->_getByFieldName('formatType') || '';

    my $result = $self->_ruleException($type);
    return $result if $result;

    return $self->fail("Unable to determine format_type from: '$type'.");
}

sub upc {
    my $self = shift;

    my $result = $self->_ruleException();
    return $result if defined $result;

    return $self->_getByFieldName('upc') || '';
}

sub isrc {
    my $self = shift;

    my $result = $self->_ruleException();
    return $result if defined $result;

    return $self->_getByFieldName('isrc') || '';
}

sub trackName {
    my $self = shift;

    my $result = $self->_ruleException();
    return $result if defined $result;

    return $self->_getByFieldName('trackName') || '';
}

sub albumName {
    my $self = shift;

    my $result = $self->_ruleException();
    return $result if defined $result;

    return $self->_getByFieldName('albumName') || '';
}

sub mediaType {
    my $self = shift;

    my $type = $self->_getByFieldName('mediaType') || '';
    my $result = $self->_ruleException($type);
    return $result if $result;

    return $self->fail("Unable to determine media_type from: '$type' ");
}

# digital
sub price     { abs( shift->_getByFieldName('price') || 0 ) }
sub unitPrice { abs( shift->SUPER::unitPrice ) }

sub units {
    my $self = shift;

    my $units = $self->_getByFieldName('units') || 0;
    $units    = $units =~ /^-/ ? floor($units) : ceil($units);
    my $price = $self->_getByFieldName('price') || 0;

    if ( $price > 0 ) {
        $units = $units ? abs( $units ) : 1;
    } elsif ( $price < 0 ) {
        $units = $units ? -1 * abs( $units ) : -1;
    }

    return $units;
}

# physical
sub netUnits {
    my $self = shift;

    my $netUnits = $self->_getByFieldName('_netUnits') || 0;
    return $self->totalRevenue >= 0 ? abs $netUnits : abs($netUnits) * -1;
}

sub priceLevel {
    my $self = shift;

    my $priceLevel = $self->_getByFieldName('priceLevel') || '';
    my $promo      = $self->_getByFieldName('_promo') || '';

    return RPS::File::Sale::PLEVEL_PROMO if $promo;
    return  $priceLevel =~ qr/^Full price$/i ? RPS::File::Sale::PLEVEL_FULL : RPS::File::Sale::PLEVEL_UNKNOWN;
}

sub totalRevenue   { $_[0]->_getByFieldName('totalRevenue') || 0 }
sub sales          { $_[0]->netUnits > 0     ? ceil( $_[0]->netUnits )         : 0 }
sub salesRevenue   { $_[0]->totalRevenue > 0 ? $_[0]->totalRevenue             : 0 }
sub returns        { $_[0]->netUnits < 0     ? abs( floor( $_[0]->netUnits ) ) : 0 }
sub returnsRevenue { $_[0]->totalRevenue < 0 ? abs $_[0]->totalRevenue         : 0 }

sub _isValidSaleRecord {
    my $self = shift;

    return unless ( $self->_getByFieldName('trackName') || $self->_getByFieldName('albumName') || $self->_getByFieldName('artistName') );

    unless ( $self->isDigitalProduct( $self->productType ) ) {
        $self->fail("Defined units but with no revenue.") if ($self->netUnits && !$self->totalRevenue) && $self->priceLevel != RPS::File::Sale::PLEVEL_PROMO;
        return ( $self->totalRevenue || $self->netUnits );
    }

    return ( $self->price || $self->units );
}


1;
