#---------------------------------------------------------------
# ____                   _ _         ____  _                    
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___ 
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/                            
#
# Copyright (C) 2010 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------
package RPS::MapFace::Mapper;
use strict;
use warnings;
use Data::Dumper;

use lib '/app/tools/raptor/lib';
use Raptor::DB::Item::File;
use Raptor::DB::Item::Sale;

use lib '/app/tools/rps/lib';
use RPS::DB::Item::MapFaceTempMapping;
use RPS::DB::Item::MapFaceTempMappingHistory;
use RPS::DB::Item::SaleImportError;
use RPS::File::File;
use RPS::File::Sale;
use RPS::Import::SaleRec;
use RPS::Import::Service;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::Util;
use Common::Email;
use Common::Assert;
use Common::DB::Item::MapFaceMapping;
use Common::DB::Item::MapFaceMappingCriteria;

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

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

sub new {
    my $class = shift;
    my $self  = bless {}, $class;

    $self->_init( @_ );

    return $self;
}

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

    $self->{_fileID} = $args{fileID};
    $self->{_clientID} = $args{clientID};
    
    return $self;
}

sub run {
    my $self = shift;
    my $clientID;
    
    if ($self->{_clientID}) {
    	$clientID = $self->{_clientID};
    } else {
    	$clientID = Common::RSApp::GetClientID();
    }
    
    if ($self->{_fileID}) {
    	print STDERR "mapface: processing file " . $self->{_fileID} . "\n";
    	$self->_processFile($self->{_fileID}, $clientID);
    } else {
    	# Let's do all of the on-hold files for this client.
    	print STDERR "mapface: processing all on-hold files for client " . $clientID . "\n";
    	my $onHoldFiles = Raptor::DB::Item::File->GetAllOnHold();
    	while ($onHoldFiles->hasNext()) {
    		my $onHoldFile = $onHoldFiles->next();
    		$self->_processFile($onHoldFile->file_id, $clientID);
    	}
    }
}

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

    my $file = RPS::File::File->new(file_id => $fileID, client_id => $clientID);
    my $fileStatus = $file->FileStatus();
    my $collection = $self->_getRecordsToMap($fileID);

    my %constructionArgs = (
        service_id => $file->ServiceID(),
        version_num => $file->VersionNum(),
    );
        
	my $importer = RPS::Import::Service->new(%constructionArgs);    
    
    while ( my $row = $collection->next ) {
        $self->_checkForMapping( $row, $file, $importer, $clientID );
    }
    
    # We need to check the file's status now. 
    my $status = Common::DB::Item::MapFaceFile->GetStatusByClientAndFileID($clientID, $fileID);

    if (!$status || $status != Common::DB::Item::MapFaceFile::STATUS_ERROR) {
    
	    # If we've mapped all of the errors for a group, we need to set the status to approved.
	    # !!! But only if it has not already been manually approved.
		my $mappings = RPS::DB::Item::MapFaceTempMapping->GetUnapprovedByFileID($fileID);
		while ($mappings->hasNext() ) {	    
	    	my $mapping = $mappings->next();
	    	my $unmappedErrorCount = RPS::DB::Item::SaleImportError->GetUnmappedCountByMappingID($mapping->mapface_temp_mapping_id);	
	    	if ($unmappedErrorCount == 0){
				$mapping->status(RPS::DB::Item::MapFaceTempMapping::STATUS_AUTO_APPROVED);	
				$mapping->save();	
				
				# And add the event to this history.
				my $newHistoryAction = RPS::DB::Item::MapFaceTempMappingHistory->Create(
					mapface_temp_mapping_id => $mapping->mapface_temp_mapping_id,
					action  => RPS::DB::Item::MapFaceTempMappingHistory::kActionAutoApproved,
				);			
				$newHistoryAction->save();			
			}    			
	    }
	    
		# Now we need to update the remaining_errors field in the mapface_file table in RSCOMMON.
		my $remainingCount = RPS::DB::Item::MapFaceTempMapping->GetRemainingErrorCountByFileID($fileID);
		
		# We also want to update the initial error count, in case we auto-approved anything.
		my $initialErrors = RPS::DB::Item::MapFaceTempMapping->GetNonAutoApprovedCountByFileID($fileID);
		my $approvedCount = RPS::DB::Item::MapFaceTempMapping->GetApprovedCountByFileID($fileID);
		
		my $mapFaceFiles = Common::DB::Item::MapFaceFile->GetByClientAndFileID($clientID,$fileID);
		while ($mapFaceFiles->hasNext() ) {	
		    my $mapFaceFile = $mapFaceFiles->next();
		    $mapFaceFile->initial_errors($initialErrors);
			$mapFaceFile->remaining_errors($remainingCount);
			$mapFaceFile->approved_mappings($approvedCount);
			
			# Note that we may end up changing this status below.
			# But maybe not.  So let's just go with what we know so far.
			if ($remainingCount == 0) {
				$mapFaceFile->status(Common::DB::Item::MapFaceFile::STATUS_REVIEW);
			}
			
			$mapFaceFile->save();	    
		}   
		
	    # After we've checked all of the rows, we need to see if we've resolved everything for this file.
	    # If so, we'll need to update its status so that it moves to the open files section.
	    my $unmappedErrorCount = RPS::DB::Item::SaleImportError->GetUnmappedCountByFileID($fileID);
	    
	    if ($unmappedErrorCount == 0) {   	
	    	$file->UpdateSummary(no_save => 1);
	        $file->FileStatus(File::File::STATUS_OPEN);
	        $file->MD5Sum(Common::Util::md5sum($file->FileDir . '/' . $file->FileName)) unless $file->MD5Sum;    	
			$file->Save();    
			
			# If this file was previously on hold, let's send out an email to the user who uploaded it 
			# and let them know it is in the open files section now.
			if ($fileStatus == File::File::STATUS_ON_HOLD){
				_fileProcessedNotification($file);
			}	
			
			# Also update the mapface_file entry in RSCOMMON
			my $mapFaceFiles = Common::DB::Item::MapFaceFile->GetByClientAndFileID($clientID,$fileID);
			while ($mapFaceFiles->hasNext() ) {	
		    	my $mapFaceFile = $mapFaceFiles->next();
				$mapFaceFile->status(Common::DB::Item::MapFaceFile::STATUS_COMPLETED);
				$mapFaceFile->date_completed(Common::Util::today_and_now());
				$mapFaceFile->save();		
			}									
	   	}  
	}
}

sub createJob {
	my $self = shift;
	my %args = @_;
	
	Common::Log::Debug( "Creating new mapper job" );
	my $job = $self->_mapperJobObject( %args );
	
	$job->enqueue();
}

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

	return RPS::MapFace::Job::Mapper->new (
		clientID      => $args{clientID},
	)
}

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

    return RPS::DB::Item::SaleImportError->GetUnmappedByFileID($fileID);

}

sub _checkForMapping {
	my ($self, $error, $file, $importer, $clientID) = @_;  

	my $serviceID = $file->ServiceID();    
	$importer->{_file} = $file;
	my @compatibleVersions = $importer->_mapFaceCompatibleVersions();  
	my $versions = join(',',@compatibleVersions);
	
	my $errorType = $error->error_type;
	my @mappingCriteria;
	if ($errorType == RPS::DB::Item::SaleImportError::kErrorService) {
		@mappingCriteria = $importer->_mapFaceServiceID();
	} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorCountry) {
		@mappingCriteria = $importer->_mapFaceCountryCode();
	} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorProductType) {
		@mappingCriteria = $importer->_mapFaceProductType();	
	} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorFormatType) {
		@mappingCriteria = $importer->_mapFaceFormatType();				
	} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorMediaType) {
		@mappingCriteria = $importer->_mapFaceMediaType();				
	} else {
		die "Unknown error type";	
	}
	
	my %saleData;
    my $fieldCount = @mappingCriteria;
    
    foreach my $saleField (@mappingCriteria) {
    	my $valueToMap = RPS::DB::Item::SaleImportErrorRawValue->GetStoredValue($error->sale_id, $saleField);
    	#if (!$valueToMap) {
    	#	die "Unable to find value to map for $saleField";
    	#}
    	$saleData{$saleField} = $valueToMap;
    }
    
   	my $mappings = Common::DB::Item::MapFaceMapping->GetByServiceAndVersion($serviceID, $versions);
   	
   	# If we find a match, we need to blow away the value for that in the temp mapping.
   	# We don't need it anymore (since we've found a permanent mapping) and it could cause a conflict later.
   	my $tempMapping = RPS::DB::Item::MapFaceTempMapping->Lookup(mapface_temp_mapping_id => $error->mapface_temp_mapping_id);
   	
	while ($mappings->hasNext()) {
   		my $mapping = $mappings->next();
   		my $fieldsMatched = 0;
   		
   		foreach my $saleField (keys %saleData) {
   			my $mappingCriteria = Common::DB::Item::MapFaceMappingCriteria->GetByMappingAndField($mapping->mapface_mapping_id, $saleField);  		
   				
   			if (!$saleData{$saleField} && !$mappingCriteria->hasNext()) {
	   			$fieldsMatched++;
	   			next();
   			}
   			
			if ($mappingCriteria->hasNext()) {
		   		my $criteria = $mappingCriteria->next();   
		   		if ($criteria->value eq $saleData{$saleField}) {
		   			$fieldsMatched++;
		   			next();
		   		}
		   	}
		}
		
		if ($fieldsMatched == $fieldCount) {
			# Found a match!
			# Now we need to grab the right value, plug it into the sale, and set the
			# sale_import_error record as mapped.
			my $sale = Raptor::DB::Item::Sale->Lookup(sale_id => $error->sale_id);
			
			# Now we just have to make sure we're putting the right value in the right place. 
			# And let's make sure there is a value...
			if ($errorType == RPS::DB::Item::SaleImportError::kErrorService && $mapping->mapped_service_id) {
				$sale->service_id($mapping->mapped_service_id);
				$tempMapping->service_id(undef);
				
                # We also need to make sure that this service is in the client's service table.
                # We'll just try to add it, and it's smart enough to skip it if it's already there.				
                Client::Service::AddService(
                    client_id => $clientID,
                    service_id => $mapping->mapped_service_id
                );				
				
			} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorCountry && $mapping->mapped_country_code) {
				$sale->country_code($mapping->mapped_country_code);
				$tempMapping->country_code(undef);
			} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorProductType && $mapping->mapped_product_type) {
				$sale->product_type($mapping->mapped_product_type);	
				$tempMapping->product_type(undef);
			} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorFormatType && $mapping->mapped_format_type) {
				$sale->format_type($mapping->mapped_format_type);	
				$tempMapping->format_type(undef);
			} elsif ($errorType == RPS::DB::Item::SaleImportError::kErrorMediaType && $mapping->mapped_media_type) {
				$sale->media_type($mapping->mapped_media_type);		
				$tempMapping->media_type(undef);		
			} else {
				next();
			}

			$sale->save();
			$tempMapping->save();	
			
			$error->mapped('Y');
			$error->save();	
			
		    my $unmappedErrorCount = RPS::DB::Item::SaleImportError->GetUnmappedCountBySaleID($sale->sale_id);
		    if ($unmappedErrorCount == 0) {
		    	# If there are no unmapped import errors left for this sale, 
		    	# let's run the matcher on it.
		    	my $saleObject = RPS::File::Sale->new(sale_id => $sale->sale_id);
		    	
		    	# But first we need to circle back and sanitize the format type..
		    	# And for that, we need a saleRec.
		    	my $saleRec = $self->_createSaleRec($saleObject);
		    	$importer->_sanitizeFormatType($saleRec);
		    	$saleObject->FormatType($saleRec->formatType);
		    	$importer->{_rawValuesStored} = 1;
		    	
		    	# And one last check just to make sure nothing sneaks by.	
				$importer->_validateSaleRec($saleRec);			
				$importer->_fillInSaleID($sale->sale_id);

				# Let's refresh the error count.  Hopefully it's still 0.
			    $unmappedErrorCount = RPS::DB::Item::SaleImportError->GetUnmappedCountBySaleID($sale->sale_id);
			    
			    if ($unmappedErrorCount == 0) {		    	
			    	$saleObject->_findMatch(file => $file);
			    	$saleObject->Save();
			    } else {
					# If we went back to having errors after resolving them all, something is wrong.
					my $mapFaceFiles = Common::DB::Item::MapFaceFile->GetByClientAndFileID($clientID,$file->FileID());
					while ($mapFaceFiles->hasNext() ) {	
				    	my $mapFaceFile = $mapFaceFiles->next();
						$mapFaceFile->status(Common::DB::Item::MapFaceFile::STATUS_ERROR);
						$mapFaceFile->save();		
					}					    		
			    }
		   	} 	
					
			last();
		}		
	}	
}

sub _createSaleRec {
	my ($self, $sale) = @_; 
    my $saleRec = RPS::Import::SaleRec->new();

    $saleRec->price(			$sale->Price );
    $saleRec->dateBegin(		$sale->DateBegin );
    $saleRec->dateEnd(			$sale->DateEnd );
	$saleRec->mediaType(       	$sale->MediaType );
    $saleRec->productType(		$sale->ProductType );
    $saleRec->formatType(		$sale->FormatType );
    $saleRec->albumName(    	$sale->AlbumName );
    $saleRec->artistName(   	$sale->ArtistName );
    $saleRec->averagePrice(     $sale->AveragePrice );
    $saleRec->channel(          $sale->Channel );
    $saleRec->clientProductID(  $sale->ClientProductID );
    $saleRec->configuration(    $sale->Configuration );
    $saleRec->conversionRate(   $sale->ConversionRate );
    $saleRec->countryCode(      $sale->CountryCode );
    $saleRec->currencyCode(	 	$sale->CurrencyCode);
    $saleRec->free(             $sale->Free );
    $saleRec->importStatus(     $sale->ImportStatus );
    $saleRec->isrc(             $sale->ISRC );
    $saleRec->labelName(        $sale->LabelName );
    $saleRec->outlet(           $sale->Outlet );
    $saleRec->payoutType(       $sale->PayoutType );
    $saleRec->wholesalePrice(   $sale->WholesalePrice );
    $saleRec->retailPrice(      $sale->RetailPrice );
    $saleRec->priceLevel(       $sale->PriceLevel );
    $saleRec->priceType(        $sale->PriceType );
    $saleRec->retail(           $sale->Retail );
    $saleRec->returns(          $sale->Returns );
    $saleRec->returnsRevenue(   $sale->ReturnsRevenue );
    $saleRec->sales(            $sale->Sales );
    $saleRec->salesRevenue(     $sale->SalesRevenue );
    $saleRec->serviceID(        $sale->ServiceID );
    $saleRec->serviceProductID( $sale->ServiceProductID );
    $saleRec->totalRevenue(     $sale->TotalRevenue );
    $saleRec->trackName(        $sale->TrackName );
    $saleRec->trackNum(         $sale->TrackNum );
    $saleRec->units(            $sale->Units );
    $saleRec->upc(              $sale->UPC );
    $saleRec->wholesaleRate(    $sale->WholesaleRate );
    $saleRec->mcps_adjustment(  $sale->McpsAdjustment );
    $saleRec->comments(         $sale->Comments );
	$saleRec->lineNum( 			$sale->LineNum );
	
	return $saleRec;	
}	

sub _fileProcessedNotification {
    my $file = shift;

    my $user = AppUser::User::User->new( userID => $file->UserID );

    if ($user && $user->Email) {
    	my $to = $user->Email;
    	my $from = 'donotreply@royaltyshare.com';
    	my $subject = "File Successfully Processed";
    	my $url  = "https://" . Common::RSApp::GetClientVHost() . ".royaltyshare.com/app/tracker?Highlight=" . $file->FileID . "#file" . $file->FileID;
    	my $fileName = $file->OrigFileName;
	    my $body = 
"The file $fileName that previously encountered an error has now been successfully processed.

Click here to go to the Revenue tab on your RoyaltyShare website to view the file:  $url

Thank you,

RoyaltyShare Support";
	
	    Common::Email->Send( to => $to,
	                         from => $from,
	                         subject => $subject,
	                         body => $body ); 
	}	                   
}


###
1;#
###
