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

ADOBE CONFIDENTIAL
------------------
Copyright 2019 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.

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

local KSTableUtils 	= require 'KSTableUtils'
local WFCatOzErrors = require 'WFCatOzErrors'
local WFSQLiteUtils = require 'WFSQLiteUtils'

local LrAssetUtils = require 'LrAssetUtils'

local LrmAssetUtils = require 'LrmAssetUtils'

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

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

local LrmCatSyncStatus = {}

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

-- Assets

-- While this list of sync states might look like a subset of the
-- asst sync states in WFCatSyncStatus, there are some differences.

	-- regular states
local kAssetNeedsMetadataUpload 			= 'needsMetadataUpload'
local kAssetNeedsMasterUpload 				= 'needsMasterUpload'
local kAssetNeedsDevelopXmpUpload			= 'needsDevelopXmpUpload'
local kAssetNeedsProxyOrMasterDownload		= 'needsProxyOrMaster'
local kAssetNeedsDevelopXmpDownload			= 'needsDevelopXmp'
local kAssetComplete 						= 'complete'
local kAssetCompleteNoMaster 				= 'completeNoMaster'
local kAssetCompleteNoNegatives 			= 'completeNoNegatives'
local kAssetStackComplete 					= 'stackComplete'

	-- error states
local kAssetStorageLimitExceeded			= 'storageLimitExceeded'
local kAssetTrialOrSubscriptionExpired		= 'trialOrSubscriptionExpired'
local kAssetMultipartCompleteError			= 'multipartCompleteError'
local kAssetInsufficientPermissionError		= 'insufficientPermissionError'

local kAssetSyncStatusById = {
	kAssetNeedsMetadataUpload, 				-- 1
	kAssetNeedsMasterUpload, 				-- 2
	kAssetNeedsDevelopXmpUpload,			-- 3
	kAssetNeedsProxyOrMasterDownload,		-- 4
	kAssetNeedsDevelopXmpDownload,			-- 5
	kAssetComplete, 						-- 6
	kAssetCompleteNoMaster, 				-- 7
	kAssetStackComplete, 					-- 8
	kAssetStorageLimitExceeded,				-- 9
	kAssetTrialOrSubscriptionExpired,		-- 10
	kAssetMultipartCompleteError,			-- 11
	kAssetCompleteNoNegatives,				-- 12
	kAssetInsufficientPermissionError,		-- 13
}

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

-- Albums

local kAssetSyncStatus = KSTableUtils.invert( kAssetSyncStatusById )

local kAlbumSyncing = 'syncing'
local kAlbumComplete = 'synced'
local kAlbumSyncingForOfflineUse = 'syncingForOfflineUse'
local kAlbumCompleteForOfflineUse = 'syncedForOfflineUse'

local kAlbumSyncStatusById = {
	kAlbumSyncing,									-- 1
	kAlbumComplete,									-- 2
	kAlbumSyncingForOfflineUse, 					-- 3
	kAlbumCompleteForOfflineUse,					-- 4
}

local kAlbumSyncStatus = KSTableUtils.invert( kAlbumSyncStatusById )

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

local function getSyncingStatusIdsForAssets( allSyncStatuses )
	
	-- SPAM [jandrew 2021-06-08]: on mobile the kAssetNeedsProxyOrMasterDownload and
	-- kAssetNeedsDevelopXmpDownload sync statuses don't mean sync of proxy/master/xmp
	-- is imminent so don't count them as syncing.

	local syncingStates = {
		kAssetNeedsMetadataUpload,
		kAssetNeedsMasterUpload,
		kAssetNeedsDevelopXmpUpload
	}

	-- Copy status ids into a table that can be unpacked.
	
	local syncingStatusIds = {}

	for _, syncingState in pairs( syncingStates ) do

		if allSyncStatuses[ syncingState ] then
			table.insert( syncingStatusIds, allSyncStatuses[ syncingState ] )
		end

	end

	return syncingStatusIds

end

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

local function getErrorStatusIdsForAssets( allSyncStatuses )
	
	local errorSyncStates = {
		kAssetStorageLimitExceeded,
		kAssetTrialOrSubscriptionExpired,
		kAssetMultipartCompleteError,
		kAssetInsufficientPermissionError,
	}
	
	local errorStatusIds = {}
	
	for _, errorState in ipairs( errorSyncStates ) do
		
		if allSyncStatuses[ errorState ] then
			table.insert( errorStatusIds, allSyncStatuses[ errorState ] )
		end
		
	end
	
	return errorStatusIds
	
end

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

function LrmCatSyncStatus.getAssetSyncStatus( docValue )

	local syncStatusId = kAssetNeedsMetadataUpload

	if LrmAssetUtils.isStack( docValue ) then

		if not LrAssetUtils.isLatestMetadataUploaded( docValue ) then
			syncStatusId = kAssetNeedsMetadataUpload
		else
			syncStatusId = kAssetStackComplete
		end

	else

		if not LrAssetUtils.isLatestMetadataUploaded( docValue ) then

			if WFCatOzErrors.hasStorageExceededError( docValue ) then
				syncStatusId = kAssetStorageLimitExceeded
			elseif WFCatOzErrors.hasTrialOrSubscriptionExpiredError( docValue ) then
				syncStatusId = kAssetTrialOrSubscriptionExpired
			elseif WFCatOzErrors.hasMultipartCompleteError( docValue ) then
				syncStatusId = kAssetMultipartCompleteError
			elseif WFCatOzErrors.hasInsufficientPermissionError( docValue ) then
				syncStatusId = kAssetInsufficientPermissionError
			else
				syncStatusId = kAssetNeedsMetadataUpload
			end

		elseif LrmAssetUtils.doesAssetNeedMasterUploaded( docValue ) then

			if WFCatOzErrors.hasStorageExceededError( docValue ) then
				syncStatusId = kAssetStorageLimitExceeded
			elseif WFCatOzErrors.hasTrialOrSubscriptionExpiredError( docValue ) then
				syncStatusId = kAssetTrialOrSubscriptionExpired
			elseif WFCatOzErrors.hasMultipartCompleteError( docValue ) then
				syncStatusId = kAssetMultipartCompleteError
			elseif WFCatOzErrors.hasInsufficientPermissionError( docValue ) then
				syncStatusId = kAssetInsufficientPermissionError
			else
				syncStatusId = kAssetNeedsMasterUpload
			end

		elseif LrmAssetUtils.doesAssetNeedDevelopXmpUploaded( docValue ) then

			if WFCatOzErrors.hasStorageExceededError( docValue ) then
				syncStatusId = kAssetStorageLimitExceeded
			elseif WFCatOzErrors.hasTrialOrSubscriptionExpiredError( docValue ) then
				syncStatusId = kAssetTrialOrSubscriptionExpired
			elseif WFCatOzErrors.hasMultipartCompleteError( docValue ) then
				syncStatusId = kAssetMultipartCompleteError
			else
				syncStatusId = kAssetNeedsDevelopXmpUpload
			end

		elseif LrmAssetUtils.doesAssetNeedProxyOrMasterDownloaded( docValue ) then

			syncStatusId = kAssetNeedsProxyOrMasterDownload

		elseif LrmAssetUtils.doesAssetNeedDevelopXmpDownloaded( docValue ) then

			syncStatusId = kAssetNeedsDevelopXmpDownload

		else

			if LrmAssetUtils.hasMasterLocally( docValue ) then

				syncStatusId = kAssetComplete

			elseif LrmAssetUtils.hasProxyLocally( docValue ) then
			
				if docValue._spaceId and docValue.subtype == 'video' then
				
					-- This addresses bug https://jira.corp.adobe.com/browse/WF-1611
					-- The kAssetCompleteNoMaster sync status maps to "Synced from Lr Classic"
					-- in client UI. This is not true for shared-with-you video assets.
					-- Shared-with-you video assets will not have an accessible original
					-- or a true proxy. Videos, in general, use the 720p as the 'proxy'. This
					-- 720p rendition is available for all shared-with-you album members.
					--
					-- In order to prevent shared-with-you videos from showing up as 
					-- "Synced from Lr Classic", we do not treat the presence of the 720p 
					-- rendition as if the asset has a proxy for shared videos.
					--
					-- Note: The bug only applies to shared-with-you viewers/contributors
					-- and not editors because editors have access to the original.
					
					syncStatusId = kAssetCompleteNoNegatives
					
				else
				
					syncStatusId = kAssetCompleteNoMaster
					
				end
				
			else
			
				syncStatusId = kAssetCompleteNoNegatives

			end

		end

	end

	return kAssetSyncStatus[ syncStatusId ]

end

function LrmCatSyncStatus.getAlbumSyncStatus( docValue )

	local syncStatusId = kAlbumComplete

	local isMarkedForOffline = false
	if KSTableUtils.derefOpt( docValue, '_localOnly', 'useOffline' ) then
		isMarkedForOffline = true
	end

	--  LrAssetUtils.isLatestMetadataUploaded() is applicable to albums as well
	if LrAssetUtils.isLatestMetadataUploaded( docValue ) then

		if isMarkedForOffline then
			syncStatusId = kAlbumCompleteForOfflineUse
		else
			syncStatusId = kAlbumComplete
		end

	else

		if isMarkedForOffline then
			syncStatusId = kAlbumSyncingForOfflineUse
		else
			syncStatusId = kAlbumSyncing
		end

	end

	return kAlbumSyncStatus[ syncStatusId ]

end

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

function LrmCatSyncStatus.isAssetCompleted( statusOrId )

	if type( statusOrId ) ~= 'string' then
		statusOrId = kAssetSyncStatusById[ statusOrId ]
	end

	return statusOrId == kAssetComplete
		or statusOrId == kAssetCompleteNoMaster
		or statusOrId == kAssetStackComplete
		or statusOrId == kAssetCompleteNoNegatives

end

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

function LrmCatSyncStatus.isAlbumCompleted( statusOrId )

	if type( statusOrId ) ~= 'string' then
		statusOrId = kAlbumSyncStatusById[ statusOrId ]
	end

	return statusOrId == kAlbumComplete
		or statusOrId == kAlbumCompleteForOfflineUse

end

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

function LrmCatSyncStatus.getAssetSyncStatusList()

	return kAssetSyncStatus

end

function LrmCatSyncStatus.getAlbumSyncStatusList()

	return kAlbumSyncStatus

end

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

-- Returns a map of albumIds and their isCompleted syncstatus of the parent collections of the albumIds passed.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfParentCollectionOfAlbums()
function LrmCatSyncStatus.getSyncStatusOfParentCollectionOfAlbums( wfCatalog, albumIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT a.docId, a.syncStatus
						FROM albums as a
						INNER JOIN albums as b ON b.parentId == a.docId
						WHERE b.docId IN CARRAY(?,?,'char*')]],

			calm = 2,
			albumIds,
			#albumIds
		}

		:map( function( albumStatuses )

			local syncedAlbumsById = {}

			for _, albumStatus in ipairs( albumStatuses ) do

				local albumId = albumStatus[ 1 ]
				local statusId = albumStatus[ 2 ]

				syncedAlbumsById[ albumId ] = LrmCatSyncStatus.isAlbumCompleted( statusId )

			end

			return syncedAlbumsById

		end )

end

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

-- Returns a map of albumsIds and their isCompleted syncstatus of the albums asscociated
-- with passed albumAssetIds.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfAlbumsOfAlbumAssets()
function LrmCatSyncStatus.getSyncStatusOfAlbumsOfAlbumAssets( wfCatalog, albumAssetIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT albums.docId, albums.syncStatus
						FROM albums
						INNER JOIN album_asset ON album_asset.albumId == albums.docId
						WHERE album_asset.docId IN CARRAY(?, ?, 'char*') ]],
	
			calm = 2,
			albumAssetIds,
			#albumAssetIds
		}

		:map( function( albumStatuses )

			local syncedAlbumsById = {}

			for _, albumStatus in ipairs( albumStatuses ) do

				local albumId = albumStatus[ 1 ]
				local statusId = albumStatus[ 2 ]

				syncedAlbumsById[ albumId ] = LrmCatSyncStatus.isAlbumCompleted( statusId )

			end

			return syncedAlbumsById

		end )

end

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

local function parseAssetStatuses( assetStatuses )

	local syncedAssetsById = {}

	for _, assetStatus in ipairs( assetStatuses ) do

		local assetId = assetStatus[ 1 ]
		local statusId = assetStatus[ 2 ]

		syncedAssetsById[ assetId ] = LrmCatSyncStatus.isAssetCompleted( statusId )

	end

	return syncedAssetsById

end

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

-- Returns a map of assetIds and their isCompleted syncstatus of the assets asscociated
-- with passed albumAssetIds.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfAssetsOfAlbumAssets()
function LrmCatSyncStatus.getSyncStatusOfAssetsOfAlbumAssets( wfCatalog, albumAssetIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT coreInfo.docId, coreInfo.syncStatus
						FROM coreInfo
						INNER JOIN album_asset ON album_asset.assetId == coreInfo.docId
						WHERE album_asset.docId IN CARRAY(?, ?, 'char*')
						
					UNION
					SELECT trashAssets.docId, trashAssets.syncStatus
						FROM trashAssets
						INNER JOIN album_asset ON album_asset.assetId == trashAssets.docId
						WHERE album_asset.docId IN CARRAY(?, ?, 'char*')
						
					]],
			calm = 2,
			albumAssetIds,
			#albumAssetIds,
			albumAssetIds,
			#albumAssetIds
		}

		:map( parseAssetStatuses )

end

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

-- Returns a map of assetIds and their isCompleted syncstatus of the assets asscociated
-- with passed versionIds.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfAssetsOfVersions()
function LrmCatSyncStatus.getSyncStatusOfAssetsOfVersions( wfCatalog, versionIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT coreInfo.docId, coreInfo.syncStatus
						FROM coreInfo
						INNER JOIN versions ON versions.assetId == coreInfo.docId
						WHERE versions.docId IN CARRAY(?,?, 'char*')
						
					UNION
					SELECT trashAssets.docId, trashAssets.syncStatus
						FROM trashAssets
						INNER JOIN versions ON versions.assetId == trashAssets.docId
						WHERE versions.docId IN CARRAY(?,?, 'char*')
						
					]],
			calm = 2,
			versionIds,
			#versionIds,
			versionIds,
			#versionIds
		}

		:map( parseAssetStatuses )

end

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

-- Returns a map of assetIds and their isCompleted syncstatus of the assets asscociated
-- with passed favoriteIds.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfAssetsOfFavorites()
function LrmCatSyncStatus.getSyncStatusOfAssetsOfFavorites( wfCatalog, favoriteIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT coreInfo.docId, coreInfo.syncStatus
						FROM coreInfo
						INNER JOIN spacefavorite ON spacefavorite.assetId == coreInfo.docId
						WHERE spacefavorite.docId IN CARRAY(?,?, 'char*')
						
					UNION
					SELECT trashAssets.docId, trashAssets.syncStatus
						FROM trashAssets
						INNER JOIN spacefavorite ON spacefavorite.assetId == trashAssets.docId
						WHERE spacefavorite.docId IN CARRAY(?,?, 'char*')
					]],
			calm = 2,
			favoriteIds,
			#favoriteIds,
			favoriteIds,
			#favoriteIds
		}

		:map( parseAssetStatuses )

end

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

-- Returns a map of assetIds and their isCompleted syncstatus of the assets asscociated
-- with passed commentIds.
-- This method is the Lrm variant of WFCatSyncStatus.getSyncStatusOfAssetsOfComments()
function LrmCatSyncStatus.getSyncStatusOfAssetsOfComments( wfCatalog, commentIds )

	return wfCatalog:indexStore():query {
			sql = [[SELECT coreInfo.docId, coreInfo.syncStatus
						FROM coreInfo
						INNER JOIN comment ON comment.assetId == coreInfo.docId
						WHERE comment.docId IN CARRAY(?,?, 'char*')
						
					UNION
					SELECT trashAssets.docId, trashAssets.syncStatus
						FROM trashAssets
						INNER JOIN comment ON comment.assetId == trashAssets.docId
						WHERE comment.docId IN CARRAY(?,?, 'char*')
					]],
			calm = 2,
			commentIds,
			#commentIds,
			commentIds,
			#commentIds
		}

		:map( parseAssetStatuses )

end

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

function LrmCatSyncStatus.getDocIdsAndStatusWithSyncPendingOrError( wfCatalog )

	local syncingStatusIds = getSyncingStatusIdsForAssets( kAssetSyncStatus )
	
	local errorStatusIds = getErrorStatusIdsForAssets( kAssetSyncStatus )
	
	local syncingOrErrorStatusIds = KSTableUtils.appendArray( syncingStatusIds, errorStatusIds )
	
	local assetsWithSyncPendingOrErrorSQL = string.format(
		[[SELECT docId, syncStatus
			FROM coreInfo 
			WHERE coreInfo.syncStatus IN ( %s )]],
		WFSQLiteUtils.createVariableArgList( #syncingOrErrorStatusIds ) )
		
	return wfCatalog:indexStore():query {
			sql = assetsWithSyncPendingOrErrorSQL,
			unpack( syncingOrErrorStatusIds )
		}
		:map( function( rows )
			local results = {}
			for _, row in ipairs( rows ) do
				table.insert( results, { assetId = row[ 1 ], status = row[ 2 ], statusString = kAssetSyncStatusById[ row[ 2 ] ] } )
			end
			return results
		end )
end

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

return LrmCatSyncStatus
