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

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

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

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

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

	-- WF.core.reactive
local KSPromises = require 'KSPromises'

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

	-- WF.core.os
local WFDeviceInfo = require 'WFDeviceInfo'
local WFFileUtils = require 'WFFileUtils'
local KSPathUtils = require 'KSPathUtils'
local WFPlatformInfo = require 'WFPlatformInfo'

local OzFilePaths = require "OzFilePaths"

	-- WF.core.lua
local KSData = require 'KSData'

	-- WF.oz_client
local WFOzPurgeSettings = require 'WFOzPurgeSettings'

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

	-- WF.web.json
local KSJsonEncoder = require 'KSJsonEncoder'

	-- Lrmobile_models
local LrmCatalogTestHelper = require 'LrmCatalogTestHelper'
local LriImport = require 'LriImport'

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

local LriImportTest = {}

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

local derefOpt = assert( KSTableUtils.derefOpt )

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

local catalog, catalogId

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

function LriImportTest.setUp()

	local _

	catalog, _, catalogId = LrmCatalogTestHelper.makeCatalogService()

end

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

function LriImportTest.tearDown()

	if catalog then

		catalog:close()

	end

end

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

local function makeImportDocValue( isVideo, assetIdOrNil )

	local assetId = assetIdOrNil or KSIdGenerator.uuidInLowercaseHex()
	
	local docValue
	
	if isVideo then
		docValue = {
			_docId = assetId,
			subtype = 'video',
			captureDate = "2022-06-12T11:01:10.000-07:00",
			importSource = {
				originalHeight = 1440,
				importTimestamp = "2022-06-18T22:19:50Z",
				contentType = "video/mp4",
				sha256 = "df125122ffed78df4eaf137eae0cc185ddc133fa46d7e08161e3dabcec2d09ca",
				fileSize = 22410541,
				originalWidth = 1080,
				fileName = "20220612_120034_07.mp4",
			},
			video = {
				chromaSubsampling = "4:2:0",
				storageWidth = 1440,
				duration = {
					4989,
					1000,
				},
				displayAspectRatio = {
					4,
					3,
				},
				bitDepth = 8,
				audioChannel = "stereo",
				colorRange = "limited",
				frameRate = {
					90001,
					1000,
				},
				videoBitrate = 35868393,
				videoCodec = "AVC",
				audioSampleSize = 16,
				colorMatrix = "unknown",
				audioCodec = "AAC",
				transferFunction = "unknown",
				fieldOrder = "progressive",
				colorPrimaries = "bt601",
				audioBitrate = 64000,
				audioSampleRate = 44100,
				storageHeight = 1080,
				pixelAspectRatio = {
					1,
					1,
				},
			},
			xmp = {
				tiff = {
					Orientation = "left bottom",
					Make = "nikon",
				},
			},
			_localOnly = {
				localUrl = '1234',
			}
		
		}
		
	else
		docValue = {
			_docId = assetId,
			subtype = 'image',
			captureDate = '2012-01-02T20:54:00.485',
			importSource = {
				fileName = 'Image01.jpg',
				filePath = '/var/mobile/0001', -- different format?
				fileSize = 10000,
				sha256 = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',
				contentType = 'image/jpeg',
				originalWidth = 1200,
				originalHeight = 1000,
			},
			location = {
				longitude = -121.891392,
				latitude = 37.335753,
				altitude = 26,
				direction = 290.00,
				reference = 'C',
			},
			develop = {
				croppedHeight = 150,
				croppedWidth = 150,
				xmpCameraRaw = '<xmp />',
				userOrientation = 2,
			},
			xmp = {
				dc = {
					rights = 'Copyright',
				},
				exif = {
					ExposureTime = { 100, 1 },
					FNumber = { 100, 1 },
					ApertureValue = { 100, 1 },
					FocalLength = { 100, 1 },
					ISOSpeedRatings = 100,
				},
				tiff = {
					Make = 'Make',
					Model = 'Model',
				},
				aux = {
					Lens = 'Lens',
					IsMergedHDR = true,
					IsMergedPanorama = true,
				}
			},
			_localOnly = {
				localUrl = '1234',
			}
		}
	end 
	
	return docValue

end

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

local function writeRendition( docValue, renditionName, contentOrNil )

	local catalogPath = OzFilePaths.getCatalogBasePath( catalogId )

	local docId = docValue._docId

	local relativePath = OzFilePaths.getRelativeBlobPath( docId, catalogId )

	local contentId = WFCatFileConductorConstants.renditionContentId( docValue )

	if renditionName == 'thumbnail2x' then
		relativePath = KSPathUtils.child( relativePath, 'th_' .. contentId .. '.jpg' )
	elseif renditionName == 'preview' then
		relativePath = KSPathUtils.child( relativePath, 'pv_' .. contentId .. '.jpg' )
	else
		return nil
	end

	local renditionData = KSData.make( contentOrNil or renditionName )

	local sha256 = renditionData:digest( 'SHA256' )

	KSData.saveToFile( renditionData, KSPathUtils.child( catalogPath, relativePath ) )

	renditionData:close()

	return relativePath, sha256

end

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

local function writeProxyOrMaster( docValue, proxyOrMaster, contentOrFileNameOrNil )

	local originalsFolder = OzFilePaths.getStandardOriginalFolderDocumentsPath( catalogId )

	local captureDate = assert( docValue.captureDate )

	local absolutePath = OzFilePaths.originalsFolderForDate( originalsFolder, captureDate )

	WFFileUtils.mkdir( absolutePath )

	local fileName = assert( derefOpt( docValue, 'importSource', 'fileName' ) )

	if proxyOrMaster == 'master' then
		absolutePath = KSPathUtils.child( absolutePath, fileName )
	elseif proxyOrMaster == 'proxy' then
		absolutePath = KSPathUtils.child( absolutePath, 'proxy_' .. fileName .. '.jpg' )
	else
		return nil
	end

	if not contentOrFileNameOrNil or not contentOrFileNameOrNil:find( '/' ) then

		local renditionData = KSData.make( contentOrFileNameOrNil or proxyOrMaster )

		local sha256 = renditionData:digest( 'SHA256' )

	 	absolutePath = KSData.saveToUniquelyNamedFile( renditionData, absolutePath )

		renditionData:close()

		return OzFilePaths.makeRelativeFrom( absolutePath, 'Originals' ), sha256

	else

		-- contentOrFileNameOrNil is path

		absolutePath = WFFileUtils.copyToUniquelyNamedFile( contentOrFileNameOrNil, absolutePath )

		local data = KSData.memoryMappedContentsOfFile( absolutePath )
		local sha256 = data:digest( 'SHA256' )

		data:close()

		return OzFilePaths.makeRelativeFrom( absolutePath, 'Originals' ), sha256

	end
end

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

local function getShaFromDisk( docValue, renditionName )

	local rendition = derefOpt( docValue, '_localOnly', 'files', renditionName )

	if not rendition then
		return nil
	end

	local path = OzFilePaths.getAbsolutePathInCatalog( catalogId, rendition.path )
	if not WFFileUtils.exists( path ) then
		return nil
	end

	local data = KSData.memoryMappedContentsOfFile( path )
	local shaFromDisk = data:digest( 'SHA256' )

	data:close()

	return shaFromDisk

end

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

local function removeImportedFiles( docValue )

	local renditions = { 'thumbnail2x', 'preview', 'proxy', 'master' }

	for _, renditionName in ipairs( renditions ) do

		local rendition = derefOpt( docValue, '_localOnly', 'files', renditionName )
		if rendition then

			local path = OzFilePaths.getAbsolutePathInCatalog( catalogId, rendition.path )

			pcall( WFFileUtils.delete, path )

		end

	end

end

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

local function testSingleImportWithProxyAndRenditions( test, isVideo )

	local assetArray = {}

	local importDocValue = makeImportDocValue( isVideo )

	local assetId
	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local thumbnailPath, thumbnailSha = writeRendition( importDocValue, 'thumbnail2x' )

	local previewPath, previewSha = writeRendition( importDocValue, 'preview' )

	local proxyPath, proxySha = writeProxyOrMaster( importDocValue, 'proxy' )

	local importAsset = {
		renditions = {
			{
				renditionName = 'thumbnail2x',
				renditionData = thumbnailPath
			},
			{
				renditionName = 'preview',
				renditionData = previewPath,
			}
		},
		proxy = proxyPath,
		docValue = KSJsonEncoder.encodeOzUploadJson( importDocValue ),
		deviceId = deviceId,
		jobAssetId = 1,
	}

	assetArray[ #assetArray + 1 ] = importAsset

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	local userId

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return catalog:ozUserIdP():mapDataP_new( function( uId )

				userId = uId

			end )

		end )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					assetId = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

				end )
		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local proxyShaFromDisk = getShaFromDisk( docValue, 'proxy' )

					return {
						importSource = {
							importedOnDevice = docValue.importSource.importedOnDevice,
							importedBy = docValue.importSource.importedBy,
						},
						proxy = {
							sha256 = proxyShaFromDisk
						},
					}

				end )

		end )

		:continueWhenValueMatchesFunction( function()

			return 'data', {
				importSource = {
					importedOnDevice = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId ),
					importedBy = userId,
				},
				proxy = {
					sha256 = proxySha
				},
			}

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return getShaFromDisk( docValue, 'thumbnail2x' )

				end )

		end )

		:continueWhenValueMatches( 'data', thumbnailSha )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return getShaFromDisk( docValue, 'preview' )

				end )

		end )

		:continueWhenValueMatches( 'data', previewSha )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local localAssetId = derefOpt( docValue, 'importSource', 'localAssetId' )
					local deviceId = derefOpt( docValue, 'importSource', 'uniqueDeviceId' )

					return { id = localAssetId, deviceId = deviceId }

				end )

		end )

		:continueWhenValueMatches( 'data', {
			id = '1234',
			deviceId = WFDeviceInfo.uniqueDeviceId()
		} )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					removeImportedFiles( docValue )

					local copyDocValue = KSTableUtils.deepCopy( docValue )
					copyDocValue._docId = importDocValue._docId

					return LrmCatalogTestHelper.equalToSourceTable( copyDocValue, importDocValue )

				end )

		end )

		:continueWhenValueMatches( 'data', importDocValue )

end

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

function LriImportTest.testSingleImportWithProxyAndRenditions_imageAsset( test, isVideo )

	testSingleImportWithProxyAndRenditions( test, false )
	
end

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


function LriImportTest.testSingleImportWithProxyAndRenditions_videoAsset( test, isVideo )

	testSingleImportWithProxyAndRenditions( test, true )
	
end

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

local function testSingleImportWithRenditionsAndMaster( test, isVideo )

	local assetArray = {}

	local importDocValue = makeImportDocValue( isVideo )
	
	local assetId
	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local thumbnailPath, thumbnailSha = writeRendition( importDocValue, 'thumbnail2x' )

	local previewPath, previewSha = writeRendition( importDocValue, 'preview' )

	local masterPath, masterSha = writeProxyOrMaster( importDocValue, 'master' )

	local importAsset = {
		renditions = {
			{
				renditionName = 'thumbnail2x',
				renditionData = thumbnailPath
			},
			{
				renditionName = 'preview',
				renditionData = previewPath,
			}
		},
		master = masterPath,
		docValue = KSJsonEncoder.encodeOzUploadJson( importDocValue ),
		deviceId = deviceId,
		jobAssetId = 1,
	}

	assetArray[ #assetArray + 1 ] = importAsset

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	local userId

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return catalog:ozUserIdP():mapDataP_new( function( uId )

				userId = uId

			end )

		end )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					assetId = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

				end )
		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local masterShaFromDisk = getShaFromDisk( docValue, 'master' )

					return {
						importSource = {
							importedOnDevice = docValue.importSource.importedOnDevice,
							importedBy = docValue.importSource.importedBy,
						},
						master = {
							sha256 = masterShaFromDisk,
						},
					}

				end )

		end )

		:continueWhenValueMatchesFunction( function()

			return 'data', {
				importSource = {
					importedOnDevice = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId ),
					importedBy = userId,
				},
				master = {
					sha256 = masterSha,
				},
			}

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local thumbnail = derefOpt( docValue, '_localOnly', 'files', 'thumbnail2x' )
					local path = OzFilePaths.getAbsolutePathInCatalog( catalogId, thumbnail.path )

					local thumbnailData = KSData.memoryMappedContentsOfFile( path )
					local thumbnailShaFromDisk = thumbnailData:digest( 'SHA256' )

					return thumbnailShaFromDisk

				end )

		end )

		:continueWhenValueMatches( 'data', thumbnailSha )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local preview = derefOpt( docValue, '_localOnly', 'files', 'preview' )
					local path = OzFilePaths.getAbsolutePathInCatalog( catalogId, preview.path )

					local previewData = KSData.memoryMappedContentsOfFile( path )
					local previewShaFromDisk = previewData:digest( 'SHA256' )

					return previewShaFromDisk

				end )

		end )

		:continueWhenValueMatches( 'data', previewSha )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local localAssetId = derefOpt( docValue, 'importSource', 'localAssetId' )
					local deviceId = derefOpt( docValue, 'importSource', 'uniqueDeviceId' )

					return { id = localAssetId, deviceId = deviceId }

				end )

		end )

		:continueWhenValueMatches( 'data', {
			id = '1234',
			deviceId = WFDeviceInfo.uniqueDeviceId()
		} )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					removeImportedFiles( docValue )

					local copyDocValue = KSTableUtils.deepCopy( docValue )
					copyDocValue._docId = importDocValue._docId

					return LrmCatalogTestHelper.equalToSourceTable( copyDocValue, importDocValue )

				end )

		end )

		:continueWhenValueMatches( 'data', importDocValue )

end

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

function LriImportTest.testSingleImportWithRenditionsAndMaster_imageAsset( test, isVideo )

	testSingleImportWithRenditionsAndMaster( test, false )
	
end

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


function LriImportTest.testSingleImportWithRenditionsAndMaster_videoAsset( test, isVideo )

	testSingleImportWithRenditionsAndMaster( test, true )
	
end

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

local function testSingleImportWithRenditions( test, isVideo )

	local assetArray = {}

	local importDocValue = makeImportDocValue( isVideo )

	local assetId
	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local thumbnailPath, thumbnailSha = writeRendition( importDocValue, 'thumbnail2x' )

	local previewPath, previewSha = writeRendition( importDocValue, 'preview' )

	local importAsset = {
		renditions = {
			{
				renditionName = 'thumbnail2x',
				renditionData = thumbnailPath
			},
			{
				renditionName = 'preview',
				renditionData = previewPath,
			}
		},
		docValue = KSJsonEncoder.encodeOzUploadJson( importDocValue ),
		deviceId = deviceId,
		jobAssetId = 1,
	}

	assetArray[ #assetArray + 1 ] = importAsset

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	local userId

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return catalog:ozUserIdP():mapDataP_new( function( uId )

				userId = uId

			end )

		end )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					assetId = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

				end )
		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return {
						importSource = {
							importedOnDevice = docValue.importSource.importedOnDevice,
							importedBy = docValue.importSource.importedBy,
						},
					}

				end )

		end )

		:continueWhenValueMatchesFunction( function()

			return 'data', {
				importSource = {
					importedOnDevice = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId ),
					importedBy = userId,
				},
			}

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return getShaFromDisk( docValue, 'thumbnail2x' )

				end )

		end )

		:continueWhenValueMatches( 'data', thumbnailSha )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					local previewShaFromDisk = getShaFromDisk( docValue, 'preview' )

					removeImportedFiles( docValue )

					return previewShaFromDisk
				end )

		end )

		:continueWhenValueMatches( 'data', previewSha )

end

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

function LriImportTest.testSingleImportWithRenditions_imageAsset( test, isVideo )

	testSingleImportWithRenditions( test, false )
	
end

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

function LriImportTest.testSingleImportWithRenditions_videoAsset( test, isVideo )

	testSingleImportWithRenditions( test, true )
	
end

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

if not WFPlatformInfo.isAndroid then
-- Android doesn't have the large 12MB.jpg copied to the bundle.

	function LriImportTest.testLinkedFiledImport( test )

		local assetArray = {}

		local importDocValue = makeImportDocValue()

		local assetId
		local deviceId = KSIdGenerator.uuidInLowercaseHex()

		local thumbnailPath = writeRendition( importDocValue, 'thumbnail2x' )
		local previewPath = writeRendition( importDocValue, 'preview' )

		local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )
		testDataPath = KSPathUtils.child( testDataPath, 'assets' )

		local proxyPath = writeProxyOrMaster( importDocValue, 'proxy', KSPathUtils.child( testDataPath, '1.5MB.jpg' ) )
		local masterPath = writeProxyOrMaster( importDocValue, 'master', KSPathUtils.child( testDataPath, '12MB.jpg' ) )

		local importAsset = {
			renditions = {
				{
					renditionName = 'thumbnail2x',
					renditionData = thumbnailPath
				},
				{
					renditionName = 'preview',
					renditionData = previewPath,
				}
			},
			proxy = proxyPath,
			master = masterPath,
			docValue = KSJsonEncoder.encodeOzUploadJson( importDocValue ),
			deviceId = deviceId,
			jobAssetId = 1,
		}

		assetArray[ #assetArray + 1 ] = importAsset

		local importOptions = {
			minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
		}

		local userId

		WFAsyncTestCase( test, 10 )

			:waitFor( function()

				return catalog:ozUserIdP():mapDataP_new( function( uId )

					userId = uId

				end )

			end )

			:waitFor( function()

				return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
					:mapDataP_new( function( assetIds )

						assetId = assetIds[ 1 ].assetId

						return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

					end )
			end )

			:continueWhenValueMatches( 'data', true )

			:waitFor( function()

				return catalog:docStore():docValueOnceP( 'asset', assetId )
					:mapDataP_new( function( docValue )

						local proxyShaFromDisk = getShaFromDisk( docValue, 'proxy' )
						local masterShaFromDisk = getShaFromDisk( docValue, 'master' )

						return {
							importSource = {
								importedOnDevice = docValue.importSource.importedOnDevice,
								importedBy = docValue.importSource.importedBy,
							},
							proxy = {
								existsOnDisk = proxyShaFromDisk and true or false,
							},
							master = {
								existsOnDisk = masterShaFromDisk and true or false,
							}
						}

					end )

			end )

			:continueWhenValueMatchesFunction( function()

				return 'data', {
					importSource = {
						importedOnDevice = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId ),
						importedBy = userId,
					},
					proxy = {
						existsOnDisk = true,
					},
					master = {
						existsOnDisk = true,
					}
				}

			end )

			:waitFor( function()

				return catalog:docStore():docValueOnceP( 'asset', assetId )
					:mapDataP_new( function( docValue )

						removeImportedFiles( docValue )

						local copyDocValue = KSTableUtils.deepCopy( docValue )
						copyDocValue._docId = importDocValue._docId

						return LrmCatalogTestHelper.equalToSourceTable( copyDocValue, importDocValue )

					end )

			end )

			:continueWhenValueMatches( 'data', importDocValue )

	end
end

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

local function testBulkAssetImport( test, isVideo )

	local assetArray = {}

	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local kMaxAssets = 10

	for i = 1, kMaxAssets do

		local importDocValue = makeImportDocValue( isVideo )

		local thumbnailPath = writeRendition( importDocValue, 'thumbnail2x', 'thumbnail2x' .. i )
		local previewPath, sha256 = writeRendition( importDocValue, 'preview', 'preview' .. i )

		importDocValue.captureDate = string.format( '2015-01-%02d', i )
		importDocValue.importSource.sha256 = sha256

		if i % 2 == 0 then

			importDocValue = KSJsonEncoder.encodeOzUploadJson( importDocValue )

		end

		local importAsset = {
			renditions = {
				{
					renditionName = 'thumbnail2x',
					renditionData = thumbnailPath
				},
				{
					renditionName = 'preview',
					renditionData = previewPath
				}
			},
			docValue = importDocValue,
			deviceId = deviceId,
			jobAssetId = i,
		}

		assetArray[ #assetArray + 1 ] = importAsset

	end

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					return type( assetIds ) == 'table' and #assetIds == kMaxAssets

				end )
		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return catalog:indexStore():queryP( 'SELECT count( docId ) FROM coreInfo' )
				:mapDataP_new( function( rows )

					return rows[ 1 ][ 1 ]

				end )

		end )

		:continueWhenValueMatches( 'data', kMaxAssets )

end

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

function LriImportTest.testBulkAssetImport_imageAsset( test )

	testBulkAssetImport( test, false )
	
end 

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

function LriImportTest.testBulkAssetImport_videoAsset( test )

	testBulkAssetImport( test, true )
	
end 

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

function LriImportTest.testFailingImport_invalidJson( test )

	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local docValue = {
		foo = 'bar',
	}

	local docValueAsString = KSJsonEncoder.encodeOzUploadJson( docValue )

	docValueAsString = docValueAsString:replace( ':', '=' )

	local importAsset = {
		docValue = docValueAsString,
		deviceId = deviceId,
		jobAssetId = 1,
	}

	local assetArray = { importAsset }

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )

		end )

		:continueWhenValueMatches( 'error', ':ignore:' )

end

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

local function testDerivedFromNonExistingAsset( test, isVideo )

	local assetArray = {}

	local importDocValue = makeImportDocValue( isVideo )

	local originCatalogId = KSIdGenerator.uuidInLowercaseHex()
	local originAssetId = KSIdGenerator.uuidInLowercaseHex()
	local originRevisionId = KSIdGenerator.uuidInLowercaseHex()

	local assetId
	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local thumbnailPath = writeRendition( importDocValue, 'thumbnail2x' )
	local previewPath = writeRendition( importDocValue, 'preview' )
	local _, sha256 = writeProxyOrMaster( importDocValue, 'master' )

	local derivedDocValue = {
		captureDate = '2015-11-09',
		importSource = {
			sha256 = sha256
		},
	}

	local originInformation = {
		catalogId = originCatalogId,
		assetId = originAssetId,
		revisionId = originRevisionId,
	}

	local importAsset = {
		renditions = {
			{
				renditionName = 'thumbnail2x',
				renditionData = thumbnailPath
			},
			{
				renditionName = 'preview',
				renditionData = previewPath,
			}
		},
		docValue = derivedDocValue,
		deviceId = deviceId,
		derivedFrom = originInformation,
		jobAssetId = 1,
	}

	assetArray[ #assetArray + 1 ] = importAsset

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return catalog:docStore():applyOverlayToDocumentP {
				docType = 'asset',
				docId = originAssetId,
				overlay = importDocValue,
			}

		end )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					assetId = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

				end )

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return derefOpt( docValue, 'xmp', 'xmpMM', 'DerivedFromDocument' )

				end )

		end )

		:continueWhenValueMatches( 'data', nil )

		:waitFor( function()

			return catalog:getCatalogIdP():mapDataP_new( function( catalogId )

				WFTableMerge.applyOverlay( originInformation, {
					catalogId = catalogId,
					assetId = { _delete = true },
				} )

				return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
					:mapDataP_new( function( assetIds )

						assetId = assetIds[ 1 ].assetId

						return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

					end )

			end )

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return derefOpt( docValue, 'xmp', 'xmpMM', 'DerivedFromDocument' )

				end )

		end )

		:continueWhenValueMatches( 'data', nil )

		:waitFor( function()

			WFTableMerge.applyOverlay( originInformation, {
				assetId = KSIdGenerator.uuidInLowercaseHex(),
			} )

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					assetId = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table'

				end )

		end )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					removeImportedFiles( docValue )

					return derefOpt( docValue, 'xmp', 'xmpMM', 'DerivedFromDocument' )

				end )

		end )

		:continueWhenValueMatches( 'data', nil )

end

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

function LriImportTest.testDerivedFromNonExistingAsset_imageAsset( test )

	testDerivedFromNonExistingAsset( test, false )
	
end 

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

function LriImportTest.testDerivedFromNonExistingAsset_videoAsset( test )

	testDerivedFromNonExistingAsset( test, true )
	
end 

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

local function testDerivedFromValidAsset( test, isVideo )

	local assetArray = {}

	local importDocValue = makeImportDocValue( isVideo )

	WFTableMerge.applyOverlay( importDocValue, {
		_serverProvidedMetadata = {
			links = {
				master = '/v1.0/foo',
			},
		},
	} )

	local originAssetId = KSIdGenerator.uuidInLowercaseHex()
	local originRevisionId = KSIdGenerator.uuidInLowercaseHex()

	local assetId

	local thumbnailPath = writeRendition( importDocValue, 'thumbnail2x' )
	local previewPath = writeRendition( importDocValue, 'preview' )
	local _, sha256 = writeProxyOrMaster( importDocValue, 'master' )

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	local catalogId
	local derivedDocValue

	WFAsyncTestCase( test, 10 )

		:waitFor( function()

			return catalog:getCatalogIdP():mapDataP_new( function( catId )

				catalogId = catId

			end )

		end )

		:waitFor( function()

			return catalog:ozUserIdP():mapDataP_new( function( userId )

				return catalog:getCatalogIdP():mapDataP_new( function( catalogId )

					derivedDocValue = {
						version = 1,
						captureDate = '2015-11-09',
						importSource = {
							sha256 = sha256,
							importedOnDevice = string.format( '%s {%s}', WFDeviceInfo.deviceName(), WFDeviceInfo.uniqueDeviceId() ),
							uniqueDeviceId = WFDeviceInfo.uniqueDeviceId(),
							importedBy = userId,
						},
						_localOnly = {
							localUrl = 'file://foo'
						}
					}

					local importAsset = {
						renditions = {
							{
								renditionName = 'thumbnail2x',
								renditionData = thumbnailPath
							},
							{
								renditionName = 'preview',
								renditionData = previewPath,
							}
						},
						docValue = derivedDocValue,
						derivedFrom = {
							catalogId = catalogId,
							assetId = originAssetId,
							revisionId = originRevisionId,
						},
						jobAssetId = 1,
					}

					assetArray[ #assetArray + 1 ] = importAsset

					return catalog:docStore():applyOverlayToDocumentP {
						docType = 'asset',
						docId = originAssetId,
						overlay = importDocValue,
					}

				end )

			end )

		end )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, assetArray, importOptions )
				:mapDataP_new( function( assetIds )

					local importedAsset = assetIds[ 1 ]
					assetId = assetIds[ 1 ].assetId

					local localUrl = importedAsset.localUrl
					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table',
						localUrl

				end )
		end )

		:continueWhenValueMatches( 'data', true, 'file://foo' )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					removeImportedFiles( docValue )

					return derefOpt( docValue, '_localOnly', 'localUrl' )

				end )

		end )

		:continueWhenValueMatches( 'data', nil )

		:waitFor( function()

			return catalog:docStore():docValueOnceP( 'asset', assetId )
				:mapDataP_new( function( docValue )

					return derefOpt( docValue, 'xmp' )

				end )

		end )

		:continueWhenValueMatchesFunction( function()

			local copiedDocValue = KSTableUtils.deepCopy( importDocValue )

			WFTableMerge.applyOverlay( copiedDocValue, derivedDocValue )

			WFTableMerge.applyOverlay( copiedDocValue, {
				xmp = {
					xmpMM = {
						DerivedFromDocument = string.format( '/v2/catalogs/%s/assets/%s', catalogId, originAssetId ),
						DerivedFromInstance = string.format( '/v2/catalogs/%s/assets/%s/revisions/%s', catalogId, originAssetId, originRevisionId )
					}
				}
			} )

			return 'data', derefOpt( copiedDocValue, 'xmp' )

		end )

end

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

function LriImportTest.testDerivedFromValidAsset_imageAsset( test )

	testDerivedFromValidAsset( test, false )
	
end 

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

function LriImportTest.testDerivedFromValidAsset_videoAsset( test )

	testDerivedFromValidAsset( test, true )
	
end 

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

local function testDuplicateSha256( test, isVideo )

	local assetId1, assetId2
	local deviceId = KSIdGenerator.uuidInLowercaseHex()

	local importDocValue = makeImportDocValue( isVideo )

	local importAsset = {
		docValue = importDocValue,
		deviceId = deviceId,
		jobAssetId = 1,
	}

	local importOptions = {
		minimumFreeDiskSpace = WFOzPurgeSettings.minimumFreeSpace(),
	}

	WFAsyncTestCase( test, 30 )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, { importAsset }, importOptions )

				:mapDataP_new( function( assetIds )

					assetId1 = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table' and
						assetIds[ 1 ].importStatus == 'new' and assetIds[ 1 ].localUrl == '1234'

				end )

		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return LriImport.addAssetArrayToCatalogP( catalog, { importAsset }, importOptions )

				:mapDataP_new( function( assetIds )

					assetId2 = assetIds[ 1 ].assetId

					return type( assetIds ) == 'table' and type( assetIds[ 1 ] ) == 'table' and
						assetIds[ 1 ].importStatus == 'duplicate'

				end )

		end )

		:continueWhenValueMatches( 'data', true )

		:waitFor( function()

			return KSPromises.resolvedPromiseP( 'data', assetId1 == assetId2 )

		end )

		:continueWhenValueMatches( 'data', true )

end

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

function LriImportTest.testDuplicateSha256_imageAsset( test )

	testDuplicateSha256( test, false )
	
end 

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

function LriImportTest.testDuplicateSha256_videoAsset( test )

	testDuplicateSha256( test, true )
	
end 

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

local function testHasAssetBySha256( test, isVideo )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local sha256_1 = KSData.make( 'hash' ):digest( 'SHA256' )
	local sha256_2 = KSData.make( 'hash1' ):digest( 'SHA256' )

	WFAsyncTestCase( test, 30 )

		:waitFor( function()

			local status = pcall( LriImport.hasAssetBySha256P, catalog, 'hash' )

			return KSPromises.resolvedPromiseP( 'error', status )

		end )

		:continueWhenValueMatches( 'error', false )

		:waitFor( function()

			local status = pcall( LriImport.hasAssetBySha256P, catalog, 42 )

			return KSPromises.resolvedPromiseP( 'error', status )

		end )

		:continueWhenValueMatches( 'error', false )

		:waitFor( function()
		
			local subtypeStr 
			
			if isVideo then
				subtypeStr = 'image'
			else
				subtypeStr = 'video'
			end
			
			return catalog:docStore():applyOverlayToDocumentP {
				docId = assetId,
				docType = 'asset',
				overlay = {
					subtype = subtypeStr,
					importSource = {
						sha256 = sha256_1
					},
					_localOnly = {
						localUrl = '1234',
					}
				}
			}

		end )

		:waitFor( function()

			return LriImport.hasAssetBySha256P( catalog, sha256_1 )

		end )

		:continueWhenValueMatches( 'data', assetId )

		:waitFor( function()

			return LriImport.hasAssetBySha256P( catalog, sha256_2 )

		end )

		:continueWhenValueMatches( 'data', nil )

end

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

function LriImportTest.testHasAssetBySha256_imageAsset( test )

	testHasAssetBySha256( test, false )
	
end 

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

function LriImportTest.testHasAssetBySha256_videoAsset( test )

	testHasAssetBySha256( test, true )
	
end 

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

return WFRxTestCase.wrapSuite( 'LriImportTest', LriImportTest )
