--[[----------------------------------------------------------------------------

WFCatImportManifestAlbumAssetsProcessor.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2017 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

----------------------------------------------------------------------------]]--

	-- Rx
local Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.oz_client.managed_catalog
local WFCatAlbums = require 'WFCatAlbums'

	-- wf.core
local WFConfiguration = require 'WFConfiguration'

	-- WF.oz_client.managed_catalog.import
local WFCatImportManifestConstants = require 'WFCatImportManifestConstants'
local WFCatImportManifestProcessorBase = require 'WFCatImportManifestProcessorBase'
local WFCatImportManifestProcessorUtils = require 'WFCatImportManifestProcessorUtils'

--------------------------------------------------------------------------------
local fakeRevisionExistsErrorUUID = WFConfiguration.Manifest.fakeRevisionExistsErrorUUIDForAlbumAsset
--------------------------------------------------------------------------------

local WFCatImportManifestAlbumAssetsProcessor = {
	
	WFCatImportManifestProcessorBase,
}

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:__init()

	self._stageName = WFCatImportManifestConstants.stageNames.albumAssets

	self._messageCodeCompleted = WFCatImportManifestConstants.messages.albumAssetProcessingComplete.code
	self._messageCodeErrorCount = WFCatImportManifestConstants.messages.albumAssetProcessingErrorCount.code
	self._messageCodeGenericError = WFCatImportManifestConstants.messages.albumAssetProcessingError.code
end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:_reportSkippedEntries( messageHandler, albumId, albumAssets )
	
	local errData = {}	
	
	for _, albumAsset in ipairs( albumAssets ) do
		table.insert( errData, { albumAsset.asset, albumId } )
	end
	
	self:_sendMessage( messageHandler, WFCatImportManifestConstants.messages.skippedAlbumAssets.code, errData )
	self:_updateError( messageHandler, #albumAssets )
end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:_reportErrorEntries( messageHandler, albumId, albumAssets )
	
	local errData = {}	
	
	for _, albumAsset in ipairs( albumAssets ) do
		table.insert( errData, { albumAsset.asset, albumId } )
	end
	
	self:_sendMessage( messageHandler, WFCatImportManifestConstants.messages.albumAssetImportError.code, errData )	
	self:_updateError( messageHandler, #albumAssets )
end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:_updateStatus( manifestStatusDatabase, albumAssets, success )
	
	local statusData = {}
	
	for _, albumAsset in ipairs( albumAssets ) do
		table.insert( statusData, {
			local_id = albumAsset.local_id,
			status = success and WFCatImportManifestConstants.status.success or WFCatImportManifestConstants.status.failure,
		} )
	end
	
	return manifestStatusDatabase:writeData( 'addAlbumAssetStatus', statusData )
		:ignoreElements()
	
end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:_updateStatusWithIds( manifestStatusDatabase, albumAssetLocalIdForOzAssetId, albumAssetsInfo )
	
	local statusWithOzIds = {}
	for assetId, oz_id in pairs( albumAssetsInfo ) do
		table.insert( statusWithOzIds, {
			local_id = albumAssetLocalIdForOzAssetId[ assetId ],
			status = oz_id and WFCatImportManifestConstants.status.success or WFCatImportManifestConstants.status.failure,
			oz_id = oz_id or nil,
		} )
	end
	
	return manifestStatusDatabase:writeData( 'addAlbumAssetStatus', statusWithOzIds )
		:ignoreElements()
	
end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:setup( catalog, manifestDatabase, manifestStatusDatabase, idCache, messageHandler )

	return self:WFCatImportManifestProcessorBase_setup( catalog, 
		manifestStatusDatabase, idCache, messageHandler, 'selectAlbumAssetsStatus' )

end

--------------------------------------------------------------------------------

function WFCatImportManifestAlbumAssetsProcessor:processImpObservable( catalog, profileCatalog, wfCatSynchronizer, 
			manifestDatabase, manifestStatusDatabase, idCache, messageHandler )
			
	return self:_maybeSetCount( manifestDatabase, 'countAlbumAssets' )
	
		:flatMap( function()
			
			return manifestDatabase:readData( 'selectAlbumAssets' )
		end )
	
		-- Group the info by the albums, so we can set in bulk
		:map( function( results )
			
			local albumAssetsGroups = {}
			
			local alreadyProcessedCount = 0
			
			for _, albumAsset in ipairs( results ) do
			
				if self:_isAlreadyProcessed( albumAsset.local_id ) then
					alreadyProcessedCount = alreadyProcessedCount + 1
				else
					local albumLocalId = albumAsset.album
					
					albumAssetsGroups[ albumLocalId ] = albumAssetsGroups[ albumLocalId ] or {}
					table.insert( albumAssetsGroups[ albumLocalId ], albumAsset )
				end
			end
			
			-- Report progress of rows that have already been processed
			if alreadyProcessedCount > 0 then
				self:_updateAlreadyProcessed( messageHandler, alreadyProcessedCount )
			end
			
			return albumAssetsGroups
			
		end )
		
		:flatMap( function( albumAssetGroups )
			
			return Observable.create( function( observer )
				
				for albumId, albumAssetGroup in pairs( albumAssetGroups ) do
					observer:onNext( albumId, albumAssetGroup )
				end
				
				observer:onCompleted()
				
			end )
			
		end )
		
		:flatMap( function( albumId, albumAssetGroup )
			
			local albumOzId = idCache:getOzIdForLocalId( WFCatImportManifestConstants.idCacheSets.albums, albumId )
			if not albumOzId then
				self:_reportSkippedEntries( messageHandler, albumId, albumAssetGroup )
				return self:_updateStatus( manifestStatusDatabase, albumAssetGroup, false )
			end

			local assetOzIds = {}
			local assetOzIdAlbumAssets = {}
			local inProcessAlbumAssets = {}
			local albumAssetLocalIdForOzAssetId = {}
			local skippedAlbumAssets = {}
			local obeyOrder = false
				-- Obey order if atleast one album-asset has order specified
				-- Order will be maintained as the query is already ordered
			
			for _, albumAsset in ipairs( albumAssetGroup ) do
				
				obeyOrder = obeyOrder or not not albumAsset.position
				local assetOzId = idCache:getOzIdForLocalId( WFCatImportManifestConstants.idCacheSets.assets, albumAsset.asset )
					
				if assetOzId  then
					assetOzIds[ #assetOzIds + 1 ] = assetOzId
					assetOzIdAlbumAssets[ assetOzId ] = albumAsset.ozAlbumAssetId
					albumAssetLocalIdForOzAssetId[ assetOzId ] = albumAsset.local_id
					inProcessAlbumAssets[ #inProcessAlbumAssets + 1 ] = albumAsset
				else
					skippedAlbumAssets[ #skippedAlbumAssets + 1 ] = albumAsset
					self:_reportSkippedEntries( messageHandler, albumId, { albumAsset } )
				end
			end
			
			local updateObservable
			
			if #skippedAlbumAssets > 0 then
				updateObservable = self:_updateStatus( manifestStatusDatabase, skippedAlbumAssets, false )
			else
				updateObservable = Observable.empty()
			end
			
			if fakeRevisionExistsErrorUUID then
				-- Add an already existing id to all, so that
				-- the album-asset processing fails atleast
				-- for one.
				for i, assetOzId in ipairs( assetOzIds ) do
					assetOzIdAlbumAssets[ assetOzId ] = fakeRevisionExistsErrorUUID
				end
				fakeRevisionExistsErrorUUID = nil
			end
			
			return updateObservable
				:ignoreElements()
				:defaultIfEmpty( nil )
				:flatMap( function()
				
					return WFCatAlbums.addAssetsToAlbumGivenDocIds( catalog, albumOzId, assetOzIds, assetOzIdAlbumAssets, obeyOrder )
					
						:flatMap( function( albumAssetsInfo )
							
							local numSuccess, errorEntries = 0, {}
							for asset, oz_id in pairs( albumAssetsInfo ) do
								if oz_id then
									numSuccess = numSuccess + 1
								else
									table.insert( errorEntries, {
										asset = asset,
										album = albumOzId,
									} )
								end
							end
							
							if #errorEntries > 0 then
								self:_reportErrorEntries( messageHandler, albumId, errorEntries )
							end
							
							self:_updateSuccess( messageHandler, numSuccess )
							
							return self:_updateStatusWithIds( manifestStatusDatabase, albumAssetLocalIdForOzAssetId, albumAssetsInfo )
						end )
						
						:catch( function( err )
							
							if WFCatImportManifestProcessorUtils.isDocAlreadyExistsError( err ) then
						
								self:_sendMessage( messageHandler, WFCatImportManifestConstants.messages.albumAssetProcessingRevisionExists.code, { err } )
								
								local pauseInfo = {
									pauseType = WFCatImportManifestConstants.pauseType.resumeAuto,
								}
								
								return Observable.throw( pauseInfo )
							end
							
							self:_reportErrorEntries( messageHandler, albumId, inProcessAlbumAssets )
							return self:_updateStatus( manifestStatusDatabase, inProcessAlbumAssets, false )
						end )
				end )
			
		end )
		:ignoreElements()
		:defaultIfEmpty( nil )
		:flatMap( function()
		
			local queryId = "countAlbumAssetsStatus"
			local dataType = WFCatImportManifestConstants.dataTypes.albumAssets
			return self:_sendSummary( messageHandler, manifestStatusDatabase, queryId, dataType )
			
		end )
		:ignoreElements()
			-- Adding summary step to the chain and then explicitely
			-- ignoring elements to denote that we are not interested
			-- in the return values.

end

--------------------------------------------------------------------------------

return Rx.Class 'WFCatImportManifestAlbumAssetsProcessor'( WFCatImportManifestAlbumAssetsProcessor )
