package RPS::Import::ADA::ADABase;

use strict;

use lib '/app/tools/common/lib';
use Common::Util qw(normalize_date normalize_upc);
use Date::Calc qw(Days_in_Month);
use Data::Dumper;
use Common::Country;

use lib '/app/tools/data_classes/lib';
use Client::Service;

use lib '/app/tools/rps/lib';
use RPS::File::Sale;
use RPS::Import::ServiceMap;

use base 'RPS::Import::Importer';

use constant kDataNotFound => -1;

my %mediaCodeMap = (
    'emdalbum' => {
        product_type => RPS::File::Sale::TYPE_ALBUM,
        format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'emdalbumstreaming' => {
        product_type => RPS::File::Sale::TYPE_ALBUM,
        format_type  => RPS::File::Sale::FORMAT_STREAM,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },

    'emdtrack' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'emdtrackstreaming' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_STREAM,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'evd' => {
        product_type => RPS::File::Sale::TYPE_TRACK,                 # default; may be overriden if UPC is set
        format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,            # was FORMAT_VIDEO (FB1324)
        media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'evdstreaming' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_STREAM,              # was FORMAT_VIDEOSTREAM (FB1324)
        media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },

    # Ringtones
    'emastertone' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_RINGTONE,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'eringback' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_RINGTONE,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'usergenmastertone' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_RINGTONE,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },

    'usergenmastertonestreaming' => {
        product_type => RPS::File::Sale::TYPE_TRACK,
        format_type  => RPS::File::Sale::FORMAT_STREAM,
        media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
);

#
# Private methods
#

sub _importLines {
    my $self     = shift;
    my %args     = @_;
    my $fileObj  = $args{file};
    my $version  = $args{file}->VersionNum();
    my $fileName = $args{file}->OrigFileName();

    my $offsets = $self->_fieldMap;

    my $sheet_count = 0;
    my ( $linenum, $serviceID );

    return undef unless ( defined $args{sheets} && defined $version );

    my $sheet_names = $args{sheet_names};

    # Check if the sales file has a global currency code, distribution fee or dates.
    #
    my ( $currencyCode, $err ) = $self->_getGlobalCurrencyCode( \%args );

    if ( !$currencyCode && $err == kDataNotFound ) {
        $self->errstr("Unable to find currency code");
        return undef;
    }

    my ( $distFee, $err ) = $self->_getGlobalDistributionFee( \%args );
    if ( !$distFee && $err == kDataNotFound ) {
        $self->errstr("Unable to find distribution fee");
        return undef;
    }

    my ( $gDateBegin, $gDateEnd, $err ) = $self->_getGlobalDates( \%args );
    if ( !$gDateBegin && $err == kDataNotFound ) {
        $self->errstr("Unable to find dates");
        return undef;
    }

    foreach my $sheet ( @{ $args{sheets} } ) {

        my $sheetName = $sheet_names->[$sheet_count];

        my $sheetIndex = $self->_normalizeSheetName( lc $sheetName );

        if ( !exists $offsets->{$sheetIndex} ) {
            print STDERR "ADABase: Illegal sheet '$sheetName'\n";
            $self->errstr("Sheet '$sheetName' is not valid");
            return undef;
        }

        my $data = $offsets->{$sheetIndex};

        # If sheet has been flagged as not having any sales detail,
        # then no need to process it.
        #
        if ( exists $data->{no_detail} ) {
            $sheet_count++;
            next;
        }

        my $dateBegin;
        my $dateEnd;
        my $dateRow;
        my $dateCol;

        if ( $gDateBegin && $gDateEnd ) {
            $dateBegin = $gDateBegin;
            $dateEnd   = $gDateEnd;
        } else {
            #
            # Get the date information from the top of each sheet
            #
            $dateRow = $data->{date_row};
            $dateCol = $data->{date_col};

            ( $dateBegin, $dateEnd ) = $self->_getDates( $sheet->[$dateRow][$dateCol] );

            if ( !$dateBegin ) {

                # Look for a row that looks like 'through MMMM YYYY' or 'MMMM YYYY'
                # We scan the first 5 lines since some files have 'extra' lines
                # in the header which makes the date_row inconsistent.
                #
                for ( my $row = 0 ; $row < 5 ; $row++ ) {
                    my $rowdata = $sheet->[$row][$dateCol];
                    if (   $rowdata =~ /through (\w*) (\d{4})/
                        || $rowdata =~ /^(\w*) (\d{4})$/ ) {
                        my $_date = "$1 $2";
                        ( $dateBegin, $dateEnd ) = $self->_getDates($_date);
                        last;
                    }
                }
            }

            if ( !$dateBegin ) {

                # this is sketchy, but if the current date field looks like a date but doesn't
                # have the year, try to look for the year in the following line (FB13338)
                my $_date   = $sheet->[$dateRow][$dateCol];
                my $nextRow = $sheet->[ $dateRow + 1 ][$dateCol];
                my $year;
                if ( $nextRow =~ /Reported (\w*) (\d{4})/ ) {
                    $year = $2;
                    $_date .= " $year";
                    ( $dateBegin, $dateEnd ) = $self->_getDates($_date);
                }
            }
        }

        unless ( $dateBegin && $dateEnd ) {
            $self->errstr( "Sheet $sheetIndex: Couldn't get dates from: " . $sheet->[$dateRow][$dateCol] );
            print STDERR "Sheet $sheetIndex: Couldn't get dates from: " . $sheet->[$dateRow][$dateCol] . "\n";
            return undef;
        }

        # Check for any column shifting; this can happen if one of the other worksheets
        # (aside from the one used for file identification) has been updated such that
        # the columns no longer match what was initially defined in the fieldMap.
        #
        $linenum = 1;
        foreach my $line ( @{$sheet} ) {
            my $sn       = $sheet_count + 1;             # sheet number (1-based)
            my $lineInfo = "Sheet $sn, line $linenum";

            my $label = $line->[ $data->{label} ] if ( defined $data->{label} );
            if ( $label && $sheetName =~ /^wmi$/i )      # header row on WMI sheet...
            {
                $data->{clientProductID} = _getColumn( 'Product Identifier',   $line );
                $data->{productTypeCode} = _getColumn( 'Product ID Type Code', $line );
                $data->{units}           = _getColumn( 'Monthly Units',        $line );
                $data->{price}           = _getColumn( 'Revenue due Label',    $line );

                last;
            }
        }

        $linenum = 1;
        foreach my $line ( @{$sheet} ) {

            my $sn       = $sheet_count + 1;             # sheet number (1-based)
            my $lineInfo = "Sheet $sn, line $linenum";

            my $serviceName = $line->[ $data->{serviceName} ];

            if (   !$serviceName
                || '' eq $serviceName
                || $serviceName =~ /^(vendor|dsp_name|dsp name)$/i ) {
                $linenum++;                              # skip header or blank lines
                next;
            }

            my $artist       = $line->[ $data->{artistName} ]   if ( defined $data->{artistName} );
            my $product_code = $line->[ $data->{product_code} ] if ( defined $data->{product_code} );

            my $title = $line->[ $data->{title} ];

            my $units = $line->[ $data->{units} ];
            $units =~ s/,//g;

            # Case 15612 - we need to strip the comma out of the number.
            # It seems really odd that this is suddenly a problem...

            my $price = $line->[ $data->{price} ];
            my $label = $line->[ $data->{label} ] if ( defined $data->{label} );

            my $upc  = ( defined $data->{upc} )  ? $line->[ $data->{upc} ]  : undef;
            my $isrc = ( defined $data->{isrc} ) ? $line->[ $data->{isrc} ] : undef;

            if ( $units && $units < 0 && $price && $price > 0 ) {
                $self->errstr("$lineInfo: Negative units with revenue");

                #print STDERR "$lineInfo: Negative units with revenue, u($units) p($price)\n";
                return undef;
            }

            if ( !$units && $price && $price < 0 ) {

                #$self->errstr("$lineInfo: Negative revenue, no units");
                #print STDERR "$lineInfo: Negative revenue, no units, u($units) p($price)\n";
                #return undef;

                # We're just going to skip this line if it has negative revenue and no units.
                $linenum++;
                next;
            }

            if ( $units != 0 ) {
                $price /= ( $units * -1 ) if ( $units < 0 );
                $price /= $units if ( $units > 0 );
                if ( $price < 0 ) {
                    if ( $units > 0 ) {
                        $units *= -1;
                    }
                    $price *= -1;
                }
                $price -= ( $price * $distFee ) if ($distFee);
            }

            my $productType;
            my $formatType;
            my $mediaType;

            my $mediaCode = $line->[ $data->{format_type} ];

            if ($mediaCode) {
                $formatType  = $mediaCodeMap{ lc $mediaCode }->{format_type};
                $productType = $mediaCodeMap{ lc $mediaCode }->{product_type};
                $mediaType   = $mediaCodeMap{ lc $mediaCode }->{media_type};

                if ( !$formatType && !$productType && !$mediaType ) {
                    $self->errstr("$lineInfo: Unknown media code '$mediaCode'");

                    #print STDERR "$lineInfo: Unknown media code '$mediaCode'\n";
                    return undef;
                }
            } else {
                $self->errstr("$lineInfo: Media Code not found");

                #print STDERR "$lineInfo: Media Code not found\n";
                return undef;
            }

            #$serviceName =~ s/ DIGITAL SERVICES INC//i;
            #$serviceName =~ s/\.com//i;

            if ( defined $data->{serviceName} ) {
                $serviceName = $self->_normalizeService($serviceName);

                $serviceID = $self->getServiceID( lc($serviceName) ) || $RPS::Import::ServiceMap::SERVICE_ALIASES{ lc $serviceName };

                if ( !$serviceID ) {

                    # Trying to get the Korean names to match up...
                    my $serviceNameCopy = $serviceName;
                    $serviceNameCopy =~ s/(.|\n)/sprintf("%02lx", ord $1)/eg;
                    $serviceID = $RPS::Import::ServiceMap::SERVICE_ALIASES{ lc $serviceNameCopy };
                }
            }

            if ( defined $data->{productTypeCode} ) {
                my $_productTypeCode = $line->[ $data->{productTypeCode} ];

                if ( $_productTypeCode =~ /upc/i ) {
                    $upc = $line->[ $data->{clientProductID} ];
                    $productType = RPS::File::Sale::TYPE_ALBUM if ( $mediaCode =~ /^evd$/i );    # FB16678
                } elsif ( $_productTypeCode =~ /isrc/i ) {
                    $isrc = $line->[ $data->{clientProductID} ];
                } elsif ($_productTypeCode) {
                    $self->errstr("$lineInfo: Unknown product type code '$_productTypeCode'");
                    return undef;
                }
            }

            if ( $productType && $artist && $artist ne "Artist" && $title && $title ne "Title" ) {
                if ( defined $data->{serviceName} ) {
                    unless ($serviceID) {

                        if ( $serviceName =~ /[[:^ascii:]]/ ) {

                            # Case 15436 - defaulting service names with incompatible characters to ADA
                            $serviceID = Client::Service::DSP_ADA;
                        } else {
                            $self->errstr( "$lineInfo: Couldn't find service ID for: " . $serviceName );
                            return undef;
                        }
                    }
                }

                my $sale = RPS::Import::SaleRec->new();

                $sale->productType($productType);
                $sale->formatType($formatType);
                $sale->dateBegin($dateBegin);
                $sale->dateEnd($dateEnd);
                $sale->labelName($label) if ( defined $data->{label} && $label ne "" );

                if ( $productType eq RPS::File::Sale::TYPE_ALBUM ) {
                    $sale->albumName($title);
                } else {
                    $sale->trackName($title);
                    $sale->isrc( Common::Util::normalize_isrc($isrc) ) if ($isrc);
                }

                $sale->upc( Common::Util::normalize_upc($upc) ) if ($upc);

                $sale->serviceID($serviceID) if ($serviceName);
                $sale->artistName($artist);
                $sale->units($units);
                $sale->price($price);

                my $countryCode;
                if ( defined $data->{country_code} && "" ne $data->{country_code} ) {
                    my $countryName = $line->[ $data->{country_code} ];
                    my $country     = Common::Country->Get($countryName);
                    if ($country) {
                        $countryCode = $country->alpha2();
                    } else {
                        $countryCode = $countryName;
                    }
                } else {
                    $countryCode = 'US';
                }
                $sale->countryCode($countryCode);
                $sale->currencyCode($currencyCode) if ($currencyCode);

                $sale->mediaType($mediaType);
                $sale->lineNum($linenum);

                my $free = $price == 0 ? 1 : 0;
                $sale->free($free);

                $self->_insertSale( sale => $sale );
            } else {
                print STDERR "skip s: $sheetIndex l:" . $linenum . " a:" . $artist . " t:" . $title . " u:" . $units . " p:" . $price
                  . "\n";
            }
            $linenum++;
        }
        $sheet_count++;
    }

    return $linenum;
}

sub _getColumn {
    my ( $colName, $line ) = @_;

    #for(my $col=0; $col < 20; $col++)
    for ( my $col = 0 ; $col <= @$line ; $col++ ) {
        return $col if ( $line->[$col] =~ /^$colName$/i );
    }
}

sub _normalizeSheetName {
    my $self  = shift;
    my $sname = shift;
    my $name;

    $name = $sname;
    if ( $sname =~ /^d-downloads$/i ) {
        $name = "digital";
    } elsif ( $sname =~ /^d-streams$/i ) {
        $name = "streams";
    } elsif ( $sname =~ /^d-wireless$/i ) {
        $name = "wireless";
    } elsif ( $sname =~ /^d-intl$/i ) {
        $name = "wmi";
    }
    return $name;
}

sub _getGlobalCurrencyCode {
    my $self = shift;

    # override if you need to get a global currency code from a specific sheet

    return ( undef, undef );
}

sub _getGlobalDistributionFee {
    my $self = shift;

    # override if you need to get a global distribution fee from a specific sheet

    return ( undef, undef );
}

sub _getGlobalDates {
    my $self = shift;

    # override if you need to get global dates from a specific sheet

    return ( undef, undef );
}

sub _getDates {
    my $self = shift;
    my $date = shift;
    my ( $month, $year, $dateBegin, $dateEnd );
    if ( $date =~ /through (\w*) (\d{4})/ ) {
        $year = $2;
    } elsif ( $date =~ /(\w*) (\d{4})/ ) {
        $year = $2;
    }
    $month = "01" if ( $date =~ /jan/i );
    $month = "02" if ( $date =~ /feb/i );
    $month = "03" if ( $date =~ /mar/i );
    $month = "04" if ( $date =~ /apr/i );
    $month = "05" if ( $date =~ /may/i );
    $month = "06" if ( $date =~ /jun/i );
    $month = "07" if ( $date =~ /jul/i );
    $month = "08" if ( $date =~ /aug/i );
    $month = "09" if ( $date =~ /sep/i );
    $month = "10" if ( $date =~ /oct/i );
    $month = "11" if ( $date =~ /nov/i );
    $month = "12" if ( $date =~ /dec/i );

    if ( $month ne "" && $year ne "" ) {
        $dateBegin = $year . "-" . $month . "-01";
        $dateEnd = $year . "-" . $month . "-" . Days_in_Month( $year, $month );
        return ( $dateBegin, $dateEnd );
    }
    return undef;
}

sub _normalizeService {
    my $self = shift;
    my $name = shift;

    return $1            if ( $name =~ /^(apple family monthly trial)/i );
    return $1            if ( $name =~ /^(apple music) /i );
    return $1            if ( $name =~ /^(deezer)/i );
    return "Apple Music" if ( $name =~ /^(individual|family) mont(?:h)?ly /i );
    return $1            if ( $name =~ /^(spotify se) /i );
    return $1            if ( $name =~ /^(tencent technology) /i );

    return "KKBOX HONG KONG LIMITED" if ( $name =~ /^kkbox /i );
    return $name;
}

###
1;    # Play nicely.
###
