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

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'


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

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

local createFakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog
local child = KSPathUtils.child

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, docStore, deleteFunc, byDocValue )

	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( docStore, { docValue } ) 
			end ), true )

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

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

function WFCatDeleteTest.rxTestDeleteLocalOriginalByDocId( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

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

	rxTest:expectSingleValue( relativePath, nil )
	
	local volumeGuid = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "original", "master", "volumeGuid" )

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

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

function WFCatDeleteTest.rxTestDeleteLocalOriginalsByDocValues( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId = deleteHelper( rxTest, docStore, 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

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

function WFCatDeleteTest.rxTestDeleteLocalProxiesByDocValues( rxTest )

	rxTest:setTimeLimit( 10 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId = deleteHelper( rxTest, docStore, WFCatDelete.deleteLocalProxiesByDocValues, true )
	
	-- Check that the doc store annotation is gone
	
	local proxyPath = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "proxy", "2560", "relativePath" )
	
	rxTest:expectSingleValue( proxyPath, nil )
		
	local volumeGuid = docStore:docValue( assetId )
		:pluck( "_localOnly", "files", "proxy", "2560", "volumeGuid" )

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

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

local function bulkDeleteHelper( rxTest, docStore, deleteFunc )

	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( docStore, docValues ) 
			
		end ), true )
	
	return assetIds
	
end

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

function WFCatDeleteTest.rxTestBulkDeleteLocalProxiesByDocValues( rxTest )

	rxTest:setTimeLimit( 60 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetIds = bulkDeleteHelper( rxTest, docStore, WFCatDelete.deleteLocalProxiesByDocValues, true )
	
	-- Check that the doc store annotation is gone
	
	for _, assetId in ipairs( assetIds ) do
	
		local proxyPath = docStore:docValue( assetId )
			:pluck( "_localOnly", "files", "proxy", "2560", "relativePath" )
		
		rxTest:expectSingleValue( proxyPath, nil )
		
		local volumeGuid = docStore:docValue( assetId )
			:pluck( "_localOnly", "files", "proxy", "2560", "volumeGuid" )
		
		rxTest:expectSingleValue( volumeGuid, nil )
		
	end
		
	-- Cleanup
	
	rxTest:expectOnlyCompleted(
		WFCatDelete.deleteAssets( fakeWFCatalog, assetIds )
	)
	
end

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

function WFCatDeleteTest.rxTestBulkDeleteLocalOriginalsByDocValues( rxTest )

	rxTest:setTimeLimit( 60 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetIds = bulkDeleteHelper( rxTest, docStore, 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

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


function WFRxTestCase.setUp( inCoveragePath )

	coveragePath = inCoveragePath

end

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

return WFRxTestCase.wrapSuite( WFCatDeleteTest )
