package RPS::Import::INGrooves2015;

use strict;

use Date::Calc qw(Days_in_Month Add_Delta_Days Decode_Month);
use Data::Dumper;

use lib '/app/tools/common/lib';
use Common::Country;
use Common::Util qw(normalize_upc normalize_isrc);

use lib '/app/tools/rps/lib';
use RPS::DB::Item::SaleImportError;
use RPS::File::Sale;
use RPS::Import::ServiceMap;
use RPS::Import::Importer;
use base 'RPS::Import::MapFaceBase';
use base 'RPS::Import::Importer';

sub _headerIdentifier { ( upc => qr/UPC(\/EAN)?/ ) }

sub _fieldMap {
    {
        my $self = shift;

        my %fieldMap = (
            22 => {
                labelName        => 'N',
                serviceID        => 'B',
                countryCode      => 'L',
                dateBegin        => 'A',
                productType      => 'O',    # ASSET TYPE
                formatType       => 'P',    # SALES DESCRIPTION
                mediaType        => 'Q',    # SALES CATEGORY
                artistName       => 'C',
                serviceProductID => 'J',    # CATALOG
                upc              => 'E',
                albumName        => 'D',
                isrc             => 'H',
                trackName        => 'F',
                units            => 'R',
                currencyCode     => 'T1',
                price            => 'T',
            },
            23 => {
                labelName        => 'O',
                serviceID        => 'B',
                countryCode      => 'M',
                dateBegin        => 'C',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CATEGORY
                artistName       => 'D',
                serviceProductID => 'K',    # CATALOG
                upc              => 'F',
                albumName        => 'E',
                isrc             => 'I',
                trackName        => 'G',
                units            => 'S',
                currencyCode     => 'U1',
                price            => 'U',
            },
            25 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                artistName       => 'E',
                serviceProductID => 'H',    # CATALOG
                clientProductID  => 'H',    # CATALOG
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'U1',
                price            => 'U',
            },
            26 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                artistName       => 'E',
                serviceProductID => 'H',    # CATALOG
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'V1',
                price            => 'V',
            },
            27 => {
                labelName        => 'N',
                serviceID        => 'B',
                countryCode      => 'L',
                dateBegin        => 'A',
                productType      => 'O',    # ASSET TYPE
                formatType       => 'P',    # SALES DESCRIPTION
                mediaType        => 'Q',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                artistName       => 'C',
                serviceProductID => 'J',    # CATALOG
                upc              => 'E',
                albumName        => 'D',
                isrc             => 'H',
                trackName        => 'F',
                units            => 'R',
                currencyCode     => 'U1',
                price            => 'U',
            },
            28 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                artistName       => 'E',
                serviceProductID => 'H',    # CATALOG
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'V',
                price            => 'V',
            },
            29 => {
                labelName       => 'A',
                serviceID       => 'C',
                countryCode     => 'N',
                dateBegin       => 'B',
                dateEnd         => 'B',
                productType     => 'O',    # ASSET TYPE
                formatType      => 'P',    # SALES DESCRIPTION
                mediaType       => 'Q',    # SALES CLASSIFICATION
                clientProductID => 'G',
                artistName      => 'D',
                upc             => 'F',
                albumName       => 'E',
                isrc            => 'J',
                trackName       => 'H',
                units           => 'R',
                price           => 'T',
            },
            30 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                artistName       => 'E',
                clientProductID  => 'H',
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'W1',
                price            => 'W',
            },
            31 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                clientProductID  => 'H',
                artistName       => 'E',
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'V1',
                price            => 'V',
            },
            32 => {
                labelName        => 'D',
                serviceID        => 'C',
                countryCode      => 'O',
                dateBegin        => 'A',
                productType      => 'P',    # ASSET TYPE
                formatType       => 'Q',    # SALES DESCRIPTION
                mediaType        => 'R',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                clientProductID  => 'H',
                artistName       => 'E',
                upc              => 'G',
                albumName        => 'F',
                isrc             => 'K',
                trackName        => 'I',
                units            => 'S',
                currencyCode     => 'W',
                price            => 'V',
            },
            33 => {
                labelName        => 'E',
                serviceID        => 'C',
                countryCode      => 'P',
                dateBegin        => 'A',
                productType      => 'Q',    # ASSET TYPE
                formatType       => 'R',    # SALES DESCRIPTION
                mediaType        => 'S',    # SALES CLASSIFICATION (formerly SALES CATEGORY)
                clientProductID  => 'I',
                artistName       => 'F',
                upc              => 'H',
                albumName        => 'G',
                isrc             => 'L',
                trackName        => 'J',
                units            => 'T',
                currencyCode     => 'V1',
                price            => 'V',
            },
        );

        return $fieldMap{ $self->_version() };
    }
}

sub _unverifiedFieldMap {
    {
        my $self = shift;

        my %fieldMap = (
            25 => {
                stmtCountryISO   => 'N',
                altCurrencyCode  => 'T1',
                altCurrencyCode2 => 'V',
            },
            26 => {
                stmtCountryISO  => 'N',
                altCurrencyCode => 'T1',
            },
            28 => {
                stmtCountryISO  => 'N',
                altCurrencyCode => 'V1',
            },
            30 => {
                stmtCountryISO  => 'N',
                altCurrencyCode => 'T1',
            },
            31 => {
                stmtCountryISO  => 'N',
                altCurrencyCode => 'T1',
            },
            32 => {
                stmtCountryISO  => 'N',
                altCurrencyCode => 'T1',
            },
            33 => {
                stmtCountryISO      => 'O',
                altCurrencyCode     => 'V1',
                netDollarsAfterFees => 'V',
            },
        );
        return $fieldMap{ $self->_version() };
    }
}

my %gProductFormatMediaTypeMappings = (
    'album' => {
        'cma download (album)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (album)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (disc on demand)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (rental)' => {
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (track)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'album completion' => {
        'cma download (album)' => {
            'download' => {
                check_isrc  => 1,
                format_type => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type  => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (a/v)' => {
            'download' => {
                check_isrc   => 1,
                product_type => RPS::File::Sale::TYPE_ALBUM,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOADUPGRADE,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'ringtone' => {
        'ringtone' => {
            'mobile (ringtones/ringback)' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_RINGTONE,
                ,
                media_type => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'ringtone (conditional)' => {
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'ringback' => {
        'ringback (conditional)' => {
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'ringback' => {
        'ringback' => {
            'mobile (ringtones/ringback)' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'track' => {
        'cma return download (track)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (disc on demand)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (track conditional)' => {
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'download (track)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'not provided' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
            'non-interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'return cma download (track)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'ringback' => {
            'mobile (ringtones/ringback)' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'ringtone' => {
            'mobile (ringtones/ringback)' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_RINGTONE,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'stream (ad-supported)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (ad-supported)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (jukebox)' => {
            'non-interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
            'non-interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
            'tethered download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_TETHERED,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (cloud)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (free trial)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (locker)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (radio)' => {
            'non-interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
        'streaming (subscription)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            },
        },
    },
    'video' => {
        'download' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
        'download (video disc on demand)' => {
            'download' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
        'download (video)' => {
            'download' => {
                check_isrc   => 1,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
        'streaming' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
        'streaming (video)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
        'video streaming (ad-supported)' => {
            'interactive streaming' => {
                product_type => RPS::File::Sale::TYPE_TRACK,
                format_type  => RPS::File::Sale::FORMAT_STREAM,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeVideo,
            },
        },
    },
    'album or track' => {
        'download' => {
            'download' => {
                check_isrc   => 1,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            }
        }
    },
    '_blank_' => {
        'download' => {
            'download' => {
                check_isrc   => 1,
                format_type  => RPS::File::Sale::FORMAT_DOWNLOAD,
                media_type   => RPS::DB::Item::MediaType::kMediaTypeAudio,
            }
        }
    }
);

sub _getMappings {
    my $self = shift;

    my $assetType        = $self->_getByFieldName('productType') || '_blank_';
    my $salesDescription = $self->_getByFieldName('formatType')  || '';
    my $salesCategory    = $self->_getByFieldName('mediaType')   || '';

    my $productAttributes = $gProductFormatMediaTypeMappings{ lc($assetType) }{ lc($salesDescription) }{ lc($salesCategory) };

    my $product_type = $productAttributes->{product_type};
    my $format_type  = $productAttributes->{format_type};
    my $media_type   = $productAttributes->{media_type};

    # !!! Note that product type is now based on whether or not we have a track name,
    # !!! so none of the stuff that happens here will actually affect the product type.
    #
    if ( defined( $productAttributes->{check_isrc} ) && $self->_version != 29 ) {
        if ( $self->_getByFieldName('isrc') ) {
            $product_type = RPS::File::Sale::TYPE_TRACK;
        } else {
            $product_type = RPS::File::Sale::TYPE_ALBUM;
        }
    }

    # !!! We're going to override some of the mappins now for certain services.

    my $retailer = $self->_getByFieldName('serviceID');

    if ( $retailer =~ /vevo/i && !$salesDescription && !$salesCategory ) {
        $format_type = RPS::File::Sale::FORMAT_STREAM;
        $media_type  = RPS::DB::Item::MediaType::kMediaTypeVideo;
    } elsif ( $retailer =~ /soundexchange/i ) {
        $format_type = RPS::File::Sale::FORMAT_PERFORMANCE;
        $media_type  = RPS::DB::Item::MediaType::kMediaTypeAudio;
    } elsif ( $retailer =~ /pandora/i && $salesCategory =~ /non-interactive streaming/i ) {
        $format_type = RPS::File::Sale::FORMAT_PERFORMANCE;
    }

    return ( $format_type, $product_type, $media_type );
}

sub upc {
    return normalize_upc( shift->_getByFieldName('upc') );
}

sub productType {
    my $self = shift;

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

    my ( $formatType, $productType, $mediaType ) = $self->_getMappings();

    return $productType if ($productType);

    # If we can't find the "real" product type,
    # let's pass along whatever is in the file.
    return $_productType;
}

sub formatType {
    my $self      = shift;
    my $assetType = $self->_getByFieldName('formatType') || '';

    return $self->handleError(
        "Error: Licensing income cannot be processed with this sales file. "
          . "Remove the sales line and enter separately through the Licensing Income interface.",
        RPS::DB::Item::SaleImportError::kErrorNonqualifying
    ) if ( $assetType =~ /licensing/i );

    my ( $formatType, $productType, $mediaType ) = $self->_getMappings();

    return $formatType if ($formatType);

    # If we can't find the "real" format type,
    # let's pass along whatever is in the file.
    return $assetType;
}

sub mediaType {
    my $self      = shift;
    my $salesType = $self->_getByFieldName('mediaType') || '';

    return $self->handleError(
        "Error: Licensing income cannot be processed with this sales file. "
          . "Remove the sales line and enter separately through the Licensing Income interface.",
        RPS::DB::Item::SaleImportError::kErrorNonqualifying
    ) if ( $salesType =~ /licensing/i );

    my ( $formatType, $productType, $mediaType ) = $self->_getMappings();

    return $mediaType if ($mediaType);

    # If we can't find the "real" media type,
    # let's pass along whatever is in the file.
    return $salesType;
}

sub serviceID {
    my $self     = shift;
    my $retailer = $self->_getByFieldName('serviceID');

    my $service_id = $self->getServiceID( lc($retailer) ) || $RPS::Import::ServiceMap::SERVICE_ALIASES{ lc($retailer) };

    return $self->handleError( "Unknown service $retailer", RPS::DB::Item::SaleImportError::kErrorService ) if ( !$service_id );

    return $service_id;
}

sub countryCode {
    my $self = shift;

    if ( $self->_version == 29 ) {
        return 'US' unless $self->_getByFieldName('countryCode');
    }

    my $name = $self->SUPER::countryCode();

    if ($name) {
        $name =~ s/,.*//;

        $name = $self->_getAlternateCountryName($name) || $name;

        my $country = new Common::Country( search => $name );
        if ($country) {
            return $country->alpha2();
        } else {
            return $name;
        }
    } else {

        # If territory is blank, try alternate location (FB12234)
        #
        if ( defined $self->_unverifiedFieldMap && exists $self->_unverifiedFieldMap->{'stmtCountryISO'} ) {
            my $alt = $self->_getByFieldName('stmtCountryISO');
            return ($alt) ? $alt : 'US';    # default is US if nothing in template (FB18630)
        } else {
            return 'US';                    # default is US if nothing in template (FB18630)
        }
    }
}

my %countries = (
    'US Virgin Islands'                    => 'Virgin Islands, U.S.',
    'British Virgin Islands'               => 'Virgin Islands, British',
    'European Economic and Monetary Union' => 'European Union',
    'Kyrgyz Republic'                      => 'Kyrgyzstan',
    'Micronesia,Federated States of'       => 'Micronesia, Federated States of',
);

sub _getAlternateCountryName {
    my ( $self, $name ) = @_;
    return ( exists $countries{$name} ) ? $countries{$name} : undef;
}

sub free {
    my $self = shift;

    my $assetType          = $self->_getByFieldName('productType');
    my $salesDescription   = $self->_getByFieldName('formatType'); # Q
    my $salesCategory      = $self->_getByFieldName('mediaType');  # R
    my $price              = $self->_getByFieldName('price') || 0;

    # must be at the very beginning
    my $free = 0;
    if ( $self->_version =~ /^25|31$/ ) {
        $free = 1
            if ( $salesDescription =~ /^(?:video )?streaming \(free trial\)$/i
                 && $salesCategory =~ /^interactive streaming$/i
                 && $price == 0
            );
        return $free;
    } elsif ( $self->_version == 33 ) {
        my $netDollarsAfterFee = $self->_getByFieldName('netDollarsAfterFees') || 0;
        if ( $assetType =~ /^Track$/i
            && $salesDescription =~ /^Streaming \(Free Trial\)$/i
            && $salesCategory =~ /^Interactive Streaming$/i
            && !$netDollarsAfterFee
        ) {
            $free = 1;
        }
        return $free;
    }

    # order is important
    $free = 1
        if ( $assetType =~ /^track$/i
             && $salesDescription =~ /^streaming \(free trial\)$/i
             && $salesCategory =~ /^interactive streaming$/i
        );

    if ( $self->_version =~ /^30|32$/ && $self->_getByFieldName('price') ) {
        return 0;
    }

    return $free;
}

sub price {
    my $self = shift;

    my $price = $self->_getByFieldName('price') || 0;

    return sprintf "%0.8f", $price;
}

sub unitPrice { abs shift->SUPER::unitPrice }
sub units {
    my $self = shift;

    my $units = $self->_getByFieldName('units') || 0;
    my $price = $self->_getByFieldName('price') || 0;

    # Due to custom Excel formatting, the units may have ".0" appended to them.
    #
    $units =~ s/\.0+$//;

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

    return $units;
}

sub currencyCode {
    my $self     = shift;

    return 'USD' if $self->_version == 29;

    my $currency = $self->_getByFieldName('currencyCode') || '';
    return $currency if $currency =~ /^\w{3}$/;

    if ( $currency =~ /^US\$/ || $currency =~ /Dollars/i || $currency =~ /^Net US/ ) {
        return "USD";
    }

    if ( defined $self->_unverifiedFieldMap && exists $self->_unverifiedFieldMap->{'altCurrencyCode'} ) {
        $currency = $self->_getByFieldName('altCurrencyCode') || '';
        if ( $currency =~ /^US\$/ || $currency =~ /Dollars/i || $currency =~ /^Net Amount/  ) {
            return "USD";
        }
    }

    if ( defined $self->_unverifiedFieldMap && exists $self->_unverifiedFieldMap->{'altCurrencyCode2'} ) {
        $currency = $self->_getByFieldName('altCurrencyCode2');
        if ( $currency =~ /^[a-z]{3}$/i ) {
            return $currency;
        }
    }

    return $self->handleError( "Cannot determine currency code from $currency", RPS::DB::Item::SaleImportError::kErrorNonqualifying );

    return $currency;
}

sub _getDateBegin {
    my $self   = shift;
    my $period = $self->SUPER::_getDateBegin;

    if ( $period =~ /^P(\d\d) (\d\d)/i )    # PMM YY (Jan 13)
    {
        my ( $month, $year ) = ( $1, $2 );
        return '20' . $year . '-' . $month . '-01';
    }

    if ($period) {
        return $period;
    }

    return $self->handleError( "Date not set", RPS::DB::Item::SaleImportError::kErrorNonqualifying );
}

sub _getDateEnd {
    my $self = shift;

    return $self->_getDateBegin;
}

sub _isValidSaleRecord {
    my $self      = shift;
    my $trackName = $self->_getByFieldName('trackName');
    my $albumName = $self->_getByFieldName('albumName');
    my $service   = $self->_getByFieldName('serviceID');

    my $artistName = $self->_getByFieldName('artistName');
    my $upc        = $self->_getByFieldName('upc');
    my $isrc       = $self->_getByFieldName('upc');

    my $units = $self->_getByFieldName('units');
    my $price = $self->_getByFieldName('price');

    my $dateBegin = $self->_getByFieldName('dateBegin') || '';
    return if $dateBegin =~ /total/i;

    # Skip line if there are zero units with zero revenue.
    # See https://royaltyshare.fogbugz.com/default.asp?W21 for more information.
    #
    return 0 if ( !$units && !$price && (!$upc || !$isrc) );

    my $formatType = $self->_getByFieldName('formatType');
    my $mediaType  = $self->_getByFieldName('mediaType');
    if ( !$formatType && !$mediaType ) {
        $self->handleError(
            "Format type and media type are empty.",
            RPS::DB::Item::SaleImportError::kErrorNonqualifying
        ) if $self->_version =~ /^(?:26|30)$/;
    }

    # Error out if line has a service name, but is missing anything that could be used for matching
    #
    if ($service) {

        # If we don't have a track name, album name, artist name, UPC and ISRC, then we have an error.
        # Otherwise, if we have at least a UPC or ISRC then let it through.
        #
        if (   ( !$trackName || '' eq $trackName )
            && ( !$albumName  || '' eq $albumName )
            && ( !$artistName || '' eq $artistName )
            && ( !$upc        || '' eq $upc )
            && ( !$isrc       || '' eq $isrc ) ) {

            # Since this is being called from within _isValidSaleRecord, we aren't going to return the result of
            # handleError.  Instead, we'll just log the error and return 1.
            $self->handleError( "Record missing catalog data", RPS::DB::Item::SaleImportError::kErrorNonqualifying );
            return 1;
        } elsif ( ( $isrc && '' ne $isrc )
            || ( $upc && '' ne $upc ) ) {
            return 1;
        }
    }

    return $self->SUPER::_isValidSaleRecord();
}

## MapFace Stuff

# If we want to check for mappings that were created by other compatible versions,
# that must be specified here.
sub _mapFaceCompatibleVersions {
    return ( 22, 23, 25, 26, 27, 28, 29, 30, 32, 33 );
}

sub _mapFaceServiceID {
    return ('serviceID');
}

sub _mapFaceCountryCode {
    return ('countryCode');
}

# !! This is now based on whether or not we have a track name.
sub _mapFaceProductType {
    return ( 'mediaType', 'formatType', 'productType');
}

sub _mapFaceFormatType {
    return ( 'mediaType', 'formatType', 'productType');
}

sub _mapFaceMediaType {
    return ( 'mediaType', 'formatType', 'productType');
}

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