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

WFCatDeleteTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 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 WFPlatformInfo = require "WFPlatformInfo"
if WFPlatformInfo.isAndroid or WFPlatformInfo.isiOS then
	-- iOS and Android don't use WFCatDelete so disable this test
	return {}
end

	-- RxLua
local Rx = require 'Rx'

	-- WF.core.self_test
local WFRxTestCase = require 'WFRxTestCase'

	-- WF.core.os
local KSData = require 'KSData'
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'

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

	-- WF.oz_client.managed_catalog
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatDelete = require 'WFCatDelete'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatTrashTestUtils = require 'WFCatTrashTestUtils'

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

local createFakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog
local child = KSPathUtils.child
local KSTableUtils_derefOpt = assert( KSTableUtils.derefOpt )

local getTrashAssetForDeletedAsset = assert( WFCatTrashTestUtils.getTrashAssetForDeletedAsset )

local coveragePath

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

local WFCatDeleteTest = {}

local kAsset1 = '7206c3ae9a094d69861ee8983e95085d'
local kAsset2 = '56d0ebf68fe24ac0a5f85389376429a8'
local kAlbumId = '2b32f6e475bc443da815b9ce353b9cce'
local kAlbumAssetIds = { '6e9d228508da45fe997f37547c6b3700', 'd5e3db57203444ada50b8a2119e84e45' }

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

local function pack( ... )

	return { ... }

end

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

function WFCatDeleteTest.rxTestBasic( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	for i, assetId in ipairs{ kAsset1, kAsset2 } do
		rxTest:expectSingleValue(

			docStore:setDocValue {
					_id = assetId,
					type = 'asset',
					subtype = 'image',
					captureDate = '2001-01-01T01:01:01',
					importSource = {
						filePath = '/path/',
						originalHeight = 150,
						originalWidth = 100,
					}
				},
	
			':ignore:' )

		rxTest:expectSingleValue(
	
			docStore:setDocValue {
					_id = kAlbumAssetIds[ i ],
					type = 'album_asset',
					album = {
						id = kAlbumId,
					},
					asset = {
						id = assetId,
					},
				},
	
			':ignore:' )
	end

	rxTest:expectSingleValue(
		fakeWFCatalog:docStore():docValue( kAlbumAssetIds[ 1 ] )
			:pluck( 'album', 'id' ),
		kAlbumId
	)

	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, { kAsset1 } )
	)

	if coveragePath then
		require 'SetupCoverage'.dumpCoverage( coveragePath )
	end

end

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

function WFCatDeleteTest.rxTestDeleteRemovesFiles( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local dstPath = child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), 'WFCatDeleteTest' )
	WFFileUtils.mkdir( dstPath )
	
	local originalFilePath = child( dstPath, 'originalFilePath' )
	local originalRelativePath = WFFileUtils.removeVolumeFromPath( originalFilePath )
	local originalVolumeGuid = WFFileUtils.getVolumeGuidForPath( originalFilePath )
	
	local xmpSidecarFilePath = child( dstPath, 'xmpSidecarFilePath' )
	

	local mockImage = KSData.make( assetId )

	KSData.saveToFile( mockImage, originalFilePath )
	KSData.saveToFile( mockImage, xmpSidecarFilePath )
	
	rxTest:finally( function()
		WFFileUtils.delete( originalFilePath )
		WFFileUtils.delete( xmpSidecarFilePath )
	end )
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( originalFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( xmpSidecarFilePath ) )
	
	local fileArray = {
		[ 'original' ] = { --original/proxies
			[ 'master' ] = {
				relativePath = originalRelativePath,
				volumeGuid = originalVolumeGuid
			},
			[ 'xmp' ] = {
				path = xmpSidecarFilePath,
			}
		},
	}

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = assetId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2001-01-01T01:01:01',
				_localOnly = {
					files = fileArray,
					originalHeight = 150,
					originalWidth = 100,
				}
			},

		':ignore:' )
			
	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, { assetId } )
	)

	rxTest:expectSingleValue(
		fakeWFCatalog:docStore():docValue( assetId )
			:pluck( '_empty' ),
		true
	)

	if coveragePath then
		require 'SetupCoverage'.dumpCoverage( coveragePath )
	end

end

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

local function deleteHelper( rxTest, wfCatalog, deleteFunc, byDocValue )

	local docStore = wfCatalog:docStore()

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local dstPath = child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), 'WFCatDeleteTest' )
	WFFileUtils.mkdir( dstPath )
	
	local filePath = child( dstPath, rxTest:testName() .. 'DeleteTest' )

	local mockImage = KSData.make( assetId )

	KSData.saveToFile( mockImage, filePath )
	
	rxTest:finally( function()
		WFFileUtils.delete( filePath )
	end )

	rxTest:expectEqual( WFFileUtils.exists( filePath ), "file" )

	local relativeFilePath = WFFileUtils.removeVolumeFromPath( filePath )
	local volumeGuid = WFFileUtils.getVolumeGuidForPath( filePath ) 

	local fileArray1 = {
		[ 'original' ] = {
			[ 'master' ] = { 
				relativePath = relativeFilePath,
				volumeGuid = volumeGuid
			},
			[ 'xmp' ] = {
				path = filePath,
			}
		},
		[ 'proxy' ] = {
			[ '2560' ] = { 
				relativePath = relativeFilePath,
				volumeGuid = volumeGuid
			}
		}
	}

	local spm = {
		links = {
			["/rels/master"] = {
				href = "i/am/a/href",
			},
			["/rels/proxy/2560"] = {
				href = "i/am/a/href",
			}
		}
	}

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = assetId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2001-01-01T01:01:01',
				_serverProvidedMetadata = spm,
				_localOnly = {
					files = fileArray1,
				}
			},

		':ignore:' )

	-- Check Annotations
	
	local docValue = docStore:docValue( assetId )
	
	local originalPath = docValue
		:pluck( "_localOnly", "files", "original", "master", "relativePath" )

	rxTest:expectNotEqual( originalPath, nil )

	local proxyPath = docValue
		:pluck( "_localOnly", "files", "proxy", "2560", "relativePath" )

	rxTest:expectNotEqual( proxyPath, nil )

	-- The following should successfully delete the local asset

	if byDocValue then
	
		rxTest:expectSingleValue(
			docValue:flatMapLatest( function( docValue )
				return deleteFunc( wfCatalog, { docValue } ) 
			end ), true )

	else
		rxTest:expectSingleValue( deleteFunc( wfCatalog, assetId ), true )
	end
	
	rxTest:expectEqual( WFFileUtils.exists( filePath ), false )
	
	return assetId
	
end

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

function WFCatDeleteTest.rxTestDeleteLocalOriginalsByDocValues( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId = deleteHelper( rxTest, fakeWFCatalog, WFCatDelete.deleteLocalOriginalsByDocValues, true )
	
	-- Check that the doc store annotation is gone
	
	local originalPath = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "original", "master", "relativePath" )

	rxTest:expectSingleValue( originalPath, nil )

	local originalVolumeGuid = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "original", "master", "volumeGuid" )

	rxTest:expectSingleValue( originalVolumeGuid, nil )

	local xmpPath = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "original", "xmp", "path" )

	rxTest:expectSingleValue( xmpPath, nil )
	
	-- Cleanup
	
	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, { assetId } )
	)
	
end

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

local function bulkDeleteHelper( rxTest, wfCatalog, deleteFunc )

	local docStore = wfCatalog:docStore()

	local assetIds = {}
	
	for i=1,500 do

		local assetId = KSIdGenerator.uuidInLowercaseHex()

		local dstPath = child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), 'WFCatDeleteTest' )
		WFFileUtils.mkdir( dstPath )
		
		local filePath = child( dstPath, rxTest:testName() .. 'DeleteTest' )

		local mockImage = KSData.make( assetId )

		KSData.saveToFile( mockImage, filePath )

		rxTest:finally( function()
			WFFileUtils.delete( filePath )
		end )

		rxTest:expectEqual( WFFileUtils.exists( filePath ), "file" )

		local relativeFilePath = WFFileUtils.removeVolumeFromPath( filePath )
		local volumeGuid = WFFileUtils.getVolumeGuidForPath( filePath ) 

		local fileArray1 = {
			[ 'original' ] = {
				[ 'master' ] = { 
					relativePath = relativeFilePath,
					volumeGuid = volumeGuid
				}
			},
			[ 'proxy' ] = {
				[ '2560' ] = { 
					relativePath = relativeFilePath,
					volumeGuid = volumeGuid
				}
			}
		}

		local spm = {
			links = {
				["/rels/master"] = {
					href = "i/am/a/href",
				},
				["/rels/proxy/2560"] = {
					href = "i/am/a/href",
				}
			}
		}

		rxTest:expectSingleValue(

			docStore:setDocValue {
					_id = assetId,
					type = 'asset',
					subtype = 'image',
					captureDate = '2001-01-01T01:01:01',
					_serverProvidedMetadata = spm,
					_localOnly = {
						files = fileArray1,
					}
				},

			':ignore:' )

		assetIds[ i ] = assetId

	end
	
	local docValues = docStore:bulkDocValue( assetIds )

	-- The following should successfully delete the local asset

	rxTest:expectSingleValue(
		docValues:flatMap( function( docValues )
		
			-- Throw in a bad document to see what happens.
	
			docValues[ #docValues + 1 ] = {
				iamnotreal = true,
			}

			-- Throw in a non-existing doc id to see what happens.
			
			docValues[ #docValues + 1 ] = {
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'asset',
				subtype = 'image',
				captureDate = '2001-01-01T01:01:01',
			}
		
			return deleteFunc( wfCatalog, docValues ) 
			
		end ), true )
	
	return assetIds
	
end

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

function WFCatDeleteTest.rxTestBulkDeleteLocalOriginalsByDocValues( rxTest )

	rxTest:setTimeLimit( 60 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetIds = bulkDeleteHelper( rxTest, fakeWFCatalog, WFCatDelete.deleteLocalOriginalsByDocValues, true )
	
	-- Check that the doc store annotation is gone
	
	for _, assetId in ipairs( assetIds ) do
	
		local originalPath = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "original", "master", "relativePath" )

		rxTest:expectSingleValue( originalPath, nil )

		local xmpPath = docStore:docValue( assetId )
			:pluck( "_localOnly", "files", "original", "xmp", "path" )

		rxTest:expectSingleValue( xmpPath, nil )
		
		local volumeGuid = docStore:docValue( assetId )
			:pluck( "_localOnly", "files", "original", "master", "volumeGuid" )
		
		rxTest:expectSingleValue( volumeGuid, nil )
		
	end
		
	-- Cleanup
	
	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, assetIds )
	)
	
end

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

function WFCatDeleteTest.rxTestDeleteStream( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local dstPath = child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), 'WFCatDeleteTest' )
	WFFileUtils.mkdir( dstPath )
	
	local originalFilePath = child( dstPath, 'originalFilePath' )
	local originalRelativePath = WFFileUtils.removeVolumeFromPath( originalFilePath )
	local originalVolumeGuid = WFFileUtils.getVolumeGuidForPath( originalFilePath )

	local mockImage = KSData.make( assetId )

	KSData.saveToFile( mockImage, originalFilePath )
	
	rxTest:finally( function()
		WFFileUtils.delete( originalFilePath )
	end )
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( originalFilePath ) )
	
	local fileArray = {
		[ 'original' ] = { --original/proxies
			[ 'master' ] = {
				relativePath = originalRelativePath,
				volumeGuid = originalVolumeGuid
			},
		},
	}

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = assetId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2001-01-01T01:01:01',
				_localOnly = {
					files = fileArray,
					originalHeight = 150,
					originalWidth = 100,
				}
			},

		':ignore:' )
	
	-- Setup delete stream subject
	
	-- The deletion stream from WFCatDelete is not a
	-- ReplaySubject. And hence we will receive the 
	-- notification, only when we were already listening
	-- when deletion is happening. But the expectData
	-- function blocks on that call and hence we
	-- need to setup another stream to actually
	-- test this functionality
	
	local deletionStreamReplaySubject = Rx.ReplaySubject( 1 )
	local deletionStreamSubn = WFCatDelete.getDeletionStream()
		:take( 1 )
		:map( pack )
		:subscribe( function( data )
			deletionStreamReplaySubject:onNext( data )
		end )
		
	
	-- Delete the file
	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, { assetId } )
	)

	local trashAssetId = getTrashAssetForDeletedAsset( rxTest, fakeWFCatalog, 1, assetId )
	rxTest:expectOnlyCompleted( WFCatDelete.deleteTrashAssets( fakeWFCatalog, { trashAssetId } ) )

	rxTest:observe( deletionStreamReplaySubject )
		:expectData( { true, originalFilePath } )
		
	deletionStreamSubn:dispose()
	
end

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

function WFCatDeleteTest.rxTestDeleteDocStoreFileArray( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local assetDocValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local fileArray = KSTableUtils_derefOpt( assetDocValue, '_localOnly', 'files' )

	WFCatDelete.deleteDocStoreFileArray( fileArray )

	-- Make sure all files were deleted except develop settings and aux

	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.proxyFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask2FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.xmpDevelopSettingsFilePath ) )
		-- Develop settings are never deleted by this API, which is intentional.

end

--==============================================================================
-- @mark setup
--==============================================================================


function WFCatDeleteTest.setUp( inCoveragePath )

	coveragePath = inCoveragePath

end

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

return WFRxTestCase.wrapSuite( 'WFCatDeleteTest', WFCatDeleteTest )
