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

NmbPathRestorer.lua

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

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

NOTICE: All information contained herein is, and remain 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 may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  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'

	-- WF.core.logger
local WFLogger = require 'WFLogger'

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local KSData = require 'KSData'
local KSTableUtils = require 'KSTableUtils'
local WFFileUtils = require 'WFFileUtils'
local WFRxBatchedBulkApplyOverlay = require 'WFRxBatchedBulkApplyOverlay'

	-- WF.oz_client.managed_catalog
local WFCatFileSpec = require 'WFCatFileSpec'
local WFCatDelete = require 'WFCatDelete'
local WFCatIndexUtils = require 'WFCatIndexUtils'

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

local check = assert( WFArguments.check )
KSData.registerArgumentChecker()

local isValidAssetDocument = assert( WFCatIndexUtils.isValidAssetDocument )
local isFileBasedAssetDocument = assert( WFCatIndexUtils.isFileBasedAssetDocument )

local logger = WFLogger 'NmbPathRestorer'

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

local KSTableUtils_derefOpt = assert( KSTableUtils.derefOpt, 
										"KSTableUtils.derefOpt undefined" )

--============================================================================--

-- @mark NmbPathRestorer namespace

local NmbPathRestorer = {}

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

local function extractProxySizeFromArchivedAsset( docValue )

	local localOnlyFiles = KSTableUtils_derefOpt( docValue, '_localOnly', 'files' )

	local proxySize = KSTableUtils_derefOpt( localOnlyFiles, 'proxy', '2560', 'archivedVolumeGuid' ) and '2560'

	if not proxySize then
		proxySize = KSTableUtils_derefOpt( localOnlyFiles, 'proxy', 'pano', 'archivedVolumeGuid' ) and 'pano'
	end

	if not proxySize then
		proxySize = KSTableUtils_derefOpt( localOnlyFiles, 'proxy', '720p', 'archivedVolumeGuid' ) and '720p'
	end
	
	return proxySize

end

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

local function emitArchivedFile( docValue, emit, binaryType, binarySize )

	local localOnlyFiles = KSTableUtils_derefOpt( docValue, '_localOnly', 'files' )

	local currentVolumeGuid = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'volumeGuid' )
	local currentRelativePath = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'relativePath' )
	local currentSha256 = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'sha256' )
	
	local archivedVolumeGuid = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'archivedVolumeGuid' )
	local archivedRelativePath = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'archivedRelativePath' )
	local archivedSha256 = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'archivedSha256' )
		
	if archivedVolumeGuid then
	
		emit( docValue._localId, binaryType, binarySize, 
				currentVolumeGuid, currentRelativePath, currentSha256,
				archivedVolumeGuid, archivedRelativePath, archivedSha256 )
			
	end

end

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

function NmbPathRestorer.generateIndexSchema( indexStoreSchema )

	check( 'WFRxIndexStoreSchema' )
	
	indexStoreSchema:addTable {

		sql = [[CREATE TABLE archivedFiles(
				localId,
				binaryType,
				binarySize,
				currentVolumeGuid,
				currentRelativePath,
				currentSha256,
				archivedVolumeGuid,
				archivedRelativePath,
				archivedSha256 )]],
				
		hasOneRowPerDocument = false,
		
		relevantTypeSubtypeTuples = {
			{ 'asset', 'image' },
			{ 'asset', 'video' },
			{ 'asset', 'deleted_image' },
			{ 'asset', 'deleted_video' }
		},
		
		map = function( docValue, emit )

			if isValidAssetDocument( docValue ) then

				emitArchivedFile( docValue, emit, 'original', 'master' )
				
				local _, proxySize = WFCatFileSpec.getProxyVolumeGuidFromDocValue( docValue )
				
				if not proxySize then
				
					proxySize = extractProxySizeFromArchivedAsset( docValue )
					
				end
				
				emitArchivedFile( docValue, emit, 'proxy', proxySize )
				
			end

		end,

	}
	
	indexStoreSchema:addIndex
		[[CREATE INDEX archivedFiles_localId
			ON archivedFiles( localId )]]

end

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

function NmbPathRestorer.archiveLocalFilePath( docStore, localId, binaryType, binarySize )

	check( 'WFRxDocStore', 'number', 'string', 'string' )

	logger:infof( "Now archiving %s:%s for local Id %d", binaryType, binarySize, localId )

	return docStore:docValue( localId )
	
		:flatMapLatest( function( docValue )
		
			local binarySizes = {}
			
			local localOnlyFiles = KSTableUtils_derefOpt( docValue, '_localOnly', 'files' )

			binarySizes[ binarySize ] = {
			
				archivedRelativePath = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'relativePath' ),
				archivedSha256 = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'sha256' ),
				archivedVolumeGuid = KSTableUtils_derefOpt( localOnlyFiles, binaryType, binarySize, 'volumeGuid' ),
				
			}
			
			local files = {}
			files[ binaryType ] = binarySizes
			
			return docStore:applyOverlay(
					{	
						_id = localId,
						_localOnly = {  
							files = files
						}
					} )
		
		end )
	
end

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

function NmbPathRestorer.restoreArchivedPaths( docStore, indexStore, deletionBacklog, progressReporter )

	check( 'WFRxDocStore', 'WFRxIndexStore', 'NmbDeletionBacklog', '?WFProgressReporter' )

	return indexStore:queryOnce {
			sql = [[SELECT localId, binaryType, binarySize, 
						currentVolumeGuid, currentRelativePath, currentSha256, 
						archivedVolumeGuid, archivedRelativePath, archivedSha256
					FROM archivedFiles ]],
			quiesceUntilUpToDate = true,
		}

		:flatMap( function( rows )
		
			local overlays = {}
			local localFilePaths = {}
			
			for _, row in ipairs( rows ) do
			
				local localId = row[ 1 ]
				local binaryType = row[ 2 ]
				local binarySize = row[ 3 ]
				local currentVolumeGuid = row[ 4 ]
				local currentRelativePath = row[ 5 ]
				local currentSha256 = row[ 6 ]
				local archivedVolumeGuid = row[ 7 ]
				local archivedRelativePath = row[ 8 ]
				local archivedSha256 = row[ 9 ]
				
				logger:infof( "Processing asset %s:%s for local Id %d", binaryType, binarySize, localId )
				
				if WFFileUtils.isVolumeGuidOnline( archivedVolumeGuid ) then
				
					if currentSha256 
						and currentSha256 ~= archivedSha256 then
				
						-- Archived file is out of date and should be deleted.
						
						logger:infof( "Archived asset %s:%s for local Id %d is stale. Deleting it!", binaryType, binarySize, localId )
						
						
						local binarySizes = {}
				
						binarySizes[ binarySize ] = {
						
							archivedVolumeGuid = {_delete = true },
							archivedRelativePath = { _delete = true },
							archivedSha256 = { _delete = true }
							
						}
						
						local files = {}
						files[ binaryType ] = binarySizes
						
						local overlay = {
							_id = localId,
							_localOnly = {  
								files = files
							}
						}
						
						table.insert( overlays, overlay )
					
						table.insert( localFilePaths, {
							volumeGuid = archivedVolumeGuid,
							relativePath = archivedRelativePath
						} )
					
					else
				
						-- If the archived file is on a connected volume then restore it and
						-- delete the file from disk that it's replacing.
						
						logger:infof( "Now restoring asset %s:%s for local Id %d", binaryType, binarySize, localId )
						
						local binarySizes = {}
				
						binarySizes[ binarySize ] = {
						
							volumeGuid = archivedVolumeGuid,
							relativePath = archivedRelativePath,
							sha256 = archivedSha256,
							archivedVolumeGuid = {_delete = true },
							archivedRelativePath = { _delete = true },
							archivedSha256 = { _delete = true }

						}
						
						local files = {}
						files[ binaryType ] = binarySizes
						
						local overlay = {
							_id = localId,
							_localOnly = {  
								files = files
							}
						}
						
						table.insert( overlays, overlay )
					
						table.insert( localFilePaths, {
							volumeGuid = currentVolumeGuid,
							relativePath = currentRelativePath
						} )
					
					end

				end

			end
			
			if #overlays == 0 then
			
				return Rx.Observable.just( true )
			
			end
			
			if progressReporter then
				progressReporter:setTotal( #overlays, true )
			end
			
			logger:infof( "Caching %d file deletions in the backlog.", #localFilePaths )
			
			return deletionBacklog:bulkAddToDeletionBacklog( localFilePaths )
			
				:map( function()
				
					logger:info( "Ensuring deletion backlog is up to date." )
					
					return deletionBacklog:bringDeletionBacklogUpToDate()
					
				end )
			
				:flatMapLatest( function()
				
					logger:infof( "Deleting %d archived annotations from the doc store.", #overlays )
				
					return WFRxBatchedBulkApplyOverlay( "restore archived paths", docStore, overlays )
					
				end )
				
				:map( function()
		
					if progressReporter then
						progressReporter:reportBulkSuccess( #overlays, nil, true )
					end
					
					-- Delete the stale archived files.
					
					for _, fileParts in ipairs( localFilePaths ) do	
					
						local volumeGuid = fileParts.volumeGuid
						local relativePath = fileParts.relativePath
						local deleteSuccessful
						
						logger:infof( "Deleting file: %s", relativePath )
						
						if volumeGuid and relativePath then
							deleteSuccessful = WFCatDelete.deleteAssetAndMaybeParentFolders( volumeGuid, relativePath )
						end
						
						logger:infof( "Deleting %s file from the deletionBacklog", relativePath )
						
						if deleteSuccessful then
							deletionBacklog:removeFromDeletionBacklog( volumeGuid, relativePath )
						end
						
					end
				
					return true
				
				end )
				
		end )

end

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

return NmbPathRestorer
