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

WFCatImportManifestPersonProcessor.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
local ReplaySubject = Rx.ReplaySubject

local WFConfiguration = require 'WFConfiguration'

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'

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

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

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

local function sendErrorMessage( self, messageHandler, errorCode, errorText, personList )
	
	local datalist = {}
	for k, p in pairs( personList ) do
		table.insert( datalist, { p.local_id, p.name, errorText } )
	end
	
	self:_sendMessage( messageHandler, errorCode, datalist )
end

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

local function addStatus( manifestStatusDatabase, persons, status )
	
	local statusData = {}
	
	for _, person in ipairs( persons ) do
		statusData[ #statusData + 1 ] = {
			local_id = person.local_id,
			oz_id = person.uuid,
			status = status,
		}
	end
	
	return manifestStatusDatabase:writeData( 'addOrReplacePersonStatus', statusData )
		:ignoreElements()
	
end

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

local function updateStatus( manifestStatusDatabase, persons, status )
	
	local statusData = {}
	
	for i, person in ipairs( persons ) do
		statusData[ i ]  = {
			local_id = person.local_id,
			status = status,
		}
	end
	
	return manifestStatusDatabase:writeData( 'updatePersonStatus', statusData )
		:ignoreElements()
end

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

local WFCatImportManifestPersonProcessor = {
	
	WFCatImportManifestProcessorBase,
}

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

function WFCatImportManifestPersonProcessor:__init()
	
	self._stageName = WFCatImportManifestConstants.stageNames.persons
	
	self._messageCodeCompleted = WFCatImportManifestConstants.messages.personsProcessingComplete.code
	self._messageCodeErrorCount = WFCatImportManifestConstants.messages.personsProcessingErrorCount.code
	self._messageCodeGenericError = WFCatImportManifestConstants.messages.personsProcessingError.code
end

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

function WFCatImportManifestPersonProcessor:setup( catalog, manifestDatabase, manifestStatusDatabase, idCache, messageHandler )
	
	return self:preProcessPending( catalog, manifestStatusDatabase, idCache, messageHandler )
		:defaultIfEmpty()
		:flatMap( function()
			
			return self:WFCatImportManifestProcessorBase_setup( catalog, 
				manifestStatusDatabase, idCache, messageHandler, 'selectPersonStatus', 
				WFCatImportManifestConstants.idCacheSets.persons )
		end )

end

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

--[[
	If there are pending records, we go through them and
	verify if they already exist. If they do, then we
	update the status of the same, so that it will get
	considered in the cache-setup phase.
]]
function WFCatImportManifestPersonProcessor:preProcessPending( catalog, manifestStatusDatabase, idCache, messageHandler )
	
	return manifestStatusDatabase:readData( "selectPersonStatusWithStatus" , { status = WFCatImportManifestConstants.status.pending } )
	
		:flatMap( function( pendingEntries )
		
			local docIds = {}
			local statusForDocId = {}
			
			for i, entry in ipairs( pendingEntries ) do
					
				docIds[ i ] = entry.oz_id
				statusForDocId[ entry.oz_id ] = entry
					
			end
		
			return catalog:docStore():bulkDocValue( docIds )
			
				:flatMap( function( docValues )
		
					local statusListToUpdate = {}
					
					for i, docValue in ipairs( docValues ) do
					
						if not docValue.empty then
							-- Document exists. 
							-- Mark the status as done.
							
							local statusInfo = statusForDocId[ docValue._id ]
							
							statusListToUpdate[ #statusListToUpdate + 1 ] = {
								local_id = statusInfo.local_id,
							}

						end
					end
					
					if #statusListToUpdate > 0 then
						return updateStatus( manifestStatusDatabase, statusListToUpdate, WFCatImportManifestConstants.status.success )
					else
						return Observable.empty()
					end
					
				end )
		end )

end

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

function WFCatImportManifestPersonProcessor:processImpObservable( catalog, profileCatalog, wfCatSynchronizer, 
		manifestDatabase, manifestStatusDatabase, idCache, messageHandler )
	
	return self:_maybeSetCount( manifestDatabase, 'countPersons' )
	
		:flatMap( function()
		
			return manifestDatabase:readData( 'selectPersons' )
			
		end )
		
		:flatMap( function( rows )
		
			local personList = {}
			local inProcessPersons = {}
			
			for i, row in ipairs( rows ) do
				-- Add to the list only if it has not been processed before
				if self:_isAlreadyProcessed( row.local_id ) then
					
					self:_updateAlreadyProcessed( messageHandler, 1 )
					
				else
					-- Use the pending id, if it exists already.
					local pendingUUID = self._processedRows[ row.local_id ] and self._processedRows[ row.local_id ].oz_id
						
					personList[ #personList + 1 ] = { 
						uuid = pendingUUID or KSIdGenerator.uuidInLowercaseHex( ),
						name = row.name,
						local_id = row.local_id,
					}
					inProcessPersons[ #inProcessPersons + 1 ] = row
				end
			end
			
			if #personList == 0 then
				return Observable.empty()
			end
			
			return addStatus( manifestStatusDatabase, personList, WFCatImportManifestConstants.status.pending )
				:defaultIfEmpty()
				:flatMap( function()
					return WFCatPerson.addPersons( catalog, personList )
				end )
			
				:flatMap( function( )
					
					local statusList = {}
					for i, person in ipairs( personList ) do
						
						idCache:setOzIdForLocalId( WFCatImportManifestConstants.idCacheSets.persons, rows[ i ].local_id, person.uuid )
						self:_updateSuccess( messageHandler, 1 )
						
						local status = {
							local_id = rows[ i ].local_id,
							oz_id = person.uuid,
						}
						statusList[ i ] = status
					end
					
					-- Update the status as well.
					return updateStatus( manifestStatusDatabase, statusList, WFCatImportManifestConstants.status.success )
					
				end )
				:catch( function( err )
					
					self:_updateError( messageHandler, #personList )
					sendErrorMessage( self, messageHandler, 
						WFCatImportManifestConstants.messages.personsProcessingAddingError.code, err, rows )
					return updateStatus( manifestStatusDatabase, inProcessPersons, WFCatImportManifestConstants.status.failure )
				end )
				
		end )
		:ignoreElements()
		:defaultIfEmpty( nil )
		:flatMap( function()
		
			local queryId = "countPersonStatus"
			local dataType = WFCatImportManifestConstants.dataTypes.persons
			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


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

function WFCatImportManifestPersonProcessor:process( ... )

	if WFConfiguration.Manifest.disablePerson then
		local retSubject = ReplaySubject( 1 )
		retSubject:onNext( true )
		retSubject:onCompleted()
		return retSubject
	end
	
	return self:WFCatImportManifestProcessorBase_process( ... )
end

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

return Rx.Class 'WFCatImportManifestPersonProcessor'( WFCatImportManifestPersonProcessor )
