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

WFCatLocalStorageTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2022 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.

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

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

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

	-- WF.oz_client.managed_catalog
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatLocalStorage = require 'WFCatLocalStorage'
local WFCatMockCatalog = require 'WFCatMockCatalog'

local LrmCatalogTestHelper

if WFPlatformInfo.isMobile() then
	LrmCatalogTestHelper = require 'LrmCatalogTestHelper'
end

local NmbFileManagementSettings, LrmVolumeAgentSettings, LrmConfig

if WFPlatformInfo.isDesktop then
	NmbFileManagementSettings = require 'NmbFileManagementSettings'
elseif WFPlatformInfo.isMobile() then
	LrmVolumeAgentSettings = require 'LrmVolumeAgentSettings'
	LrmConfig = require 'LrmConfig'
end

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

local WFCatLocalStorageTest = {}

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

-- @mark Helper Functions

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

local function createFakeWFCatalog( rxTest )

	if WFPlatformInfo.isAndroid then
	
		local catalogService, wfCatalog = LrmCatalogTestHelper.makeCatalogService( rxTest )
		local dataCache = rxTest:getFirstValue( catalogService:getDataCache() )

		return wfCatalog, dataCache

	else

		local optArgs = {}
			
		if WFPlatformInfo.isiOS then
			optArgs.indexDefinitions = 'LrmCatalogIndexDefinitions.lua'
		end

		return WFCatMockCatalog.createFakeWFCatalog( rxTest , nil, nil, optArgs )

	end

end

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

local function fillDataCacheForImageAsset( rxTest, dataCache, expectedData )

	local previewData = KSData.memoryMappedContentsOfFile( expectedData.previewFilePath )
	local thumbnail2xData = KSData.memoryMappedContentsOfFile( expectedData.thumbnailFilePath )
	local fullsizeData = KSData.memoryMappedContentsOfFile( expectedData.fullsizeFilePath )
	
	expectedData.previewMd5 = previewData:digest( 'MD5' )
	expectedData.thumbMd5 = thumbnail2xData:digest( 'MD5' )
	expectedData.fullsizeMd5 = fullsizeData:digest( 'MD5' )
	
	rxTest:waitFor( dataCache:put( expectedData.previewMd5, previewData ) )
	rxTest:waitFor( dataCache:put( expectedData.thumbMd5, thumbnail2xData ) )
	rxTest:waitFor( dataCache:put( expectedData.fullsizeMd5, fullsizeData ) )
	
	rxTest:finally( function()
	
		previewData:close()
		thumbnail2xData:close()
		fullsizeData:close()
		
	end )

end

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

local function mapAwayCruft( results )

	-- This fiels are very difficult to test on the build machines.
	
	results.freeSpace = nil
	results.addlStorageUsedOnDevice = nil
	results.appAndUserData = nil  
	
	return results

end

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

local function generateAlbumAsset( albumId, assetId )

	local albumAssetId = KSIdGenerator.uuidInLowercaseHex()

	return {

		type = 'album_asset',
		_id = albumAssetId,
		album = {
				id = albumId,
			},
			asset = {
				id = assetId,
			}
		}
		
end

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

local function addAssetDocToOfflineAlbum( doc )

	local albumId = KSIdGenerator.uuidInLowercaseHex()

	local albumDoc = {
		type = 'album',
		subtype = 'collection',
		_id = albumId,
		name = 'foo',
		_localOnly = {
			useOffline = true,
		}
	}
		
	local albumAssetDoc = generateAlbumAsset( albumId, doc._id )
	
	return { doc, albumDoc, albumAssetDoc }

end

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

local function markForOfflineUseAndAppendToDocs( docs, assetDoc )
	 
	if WFPlatformInfo.isMobile() then
		
		docs = KSTableUtils.appendArray( docs, addAssetDocToOfflineAlbum( assetDoc ) )
	
	else
	
		KSTableUtils.setNested( assetDoc, true, '_localOnly', 'isMarkedForOfflineUse' )
		docs[ #docs + 1 ] = assetDoc
	
	end
	
end

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

local function testGetLocalStorageSize( rxTest, optArgs )

	rxTest:setTimeLimit( 10 )

	optArgs = optArgs or {}
	optArgs.hasAllAuxType = true
	
	local binaryStrategy = optArgs.binaryStrategy

	local overrides = {}
	
	if not optArgs.storeOriginalsOnMainVolume then
		overrides.debugOriginalDirNotOnMainVolume = true
	end
	
	local wfCatalog, maybeDataCache = createFakeWFCatalog( rxTest )

	local numberOfDocumentsToCreate = 10
	
	local sizeOfOriginals = 0
	local sizeOfProxies = 0
	local sizeOfDevelopSettings = 0
	local sizeOfAuxes = 0
	local sizeOfFullSizeRenditions = 0
	local sizeOfPreviews = 0
		
	local docs = {}
		
	for i = 1, numberOfDocumentsToCreate, 1 do
	
		local docId = KSIdGenerator.uuidInLowercaseHex()
		
		local assetDocValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, docId, optArgs )
		
		if optArgs.markedForOfflineUse then
			 markForOfflineUseAndAppendToDocs( docs, assetDocValue )
		else
			docs[ #docs + 1 ] = assetDocValue
		end
		
		sizeOfOriginals = sizeOfOriginals + expectedData.originalFileSize + expectedData.xmpSidecarFileSize
		
		sizeOfProxies = sizeOfProxies + expectedData.proxyFileSize

		sizeOfDevelopSettings = sizeOfDevelopSettings + expectedData.xmpDevelopSettingsFileSize
		
		sizeOfAuxes = sizeOfAuxes + expectedData.mask1FileSize + expectedData.mask2FileSize
		sizeOfAuxes = sizeOfAuxes + expectedData.filter1FileSize + expectedData.filter2FileSize 
		sizeOfAuxes = sizeOfAuxes + expectedData.c2paFileSize
		
		sizeOfFullSizeRenditions = sizeOfFullSizeRenditions + expectedData.fullsizeFileSize
		
		sizeOfPreviews = sizeOfPreviews + expectedData.previewFileSize
		
		if maybeDataCache then
			fillDataCacheForImageAsset( rxTest, maybeDataCache, expectedData )
		end

	end
	
	rxTest:waitFor( wfCatalog:docStore():bulkSetDocValue( docs ) )
	
	local expectedData
	
	if WFPlatformInfo.isMobile() then
	
		local expectedCacheSize = 0
		local expectedLocallyStoredBinariesSize = 0
		
		if not optArgs.markedForOfflineUse then

			expectedCacheSize = sizeOfOriginals + sizeOfProxies + sizeOfDevelopSettings 
					+ sizeOfAuxes + sizeOfFullSizeRenditions + sizeOfPreviews
			
		else
			
			expectedLocallyStoredBinariesSize = sizeOfDevelopSettings 
					+ sizeOfAuxes + sizeOfFullSizeRenditions + sizeOfPreviews
					
			if binaryStrategy == LrmConfig.StrategyPreferMaster then
				expectedLocallyStoredBinariesSize = expectedLocallyStoredBinariesSize + sizeOfOriginals
				expectedCacheSize = sizeOfProxies
			end
	
			if binaryStrategy == LrmConfig.StrategyPreferProxy then
				expectedLocallyStoredBinariesSize = expectedLocallyStoredBinariesSize + sizeOfProxies
				expectedCacheSize = sizeOfOriginals
			end
		
		end
	
		expectedData = {
			photoCache = expectedCacheSize,
			storedLocally = expectedLocallyStoredBinariesSize,
		}
	
	else
	
		local expectedCacheSize = 0
		local expectedLocallyStoredBinariesSize = 0
		
		if optArgs.storeOriginalsOnMainVolume then

			if optArgs.keepAllOriginals or optArgs.markedForOfflineUse then
				expectedLocallyStoredBinariesSize = expectedLocallyStoredBinariesSize + sizeOfOriginals
			else
				expectedCacheSize = expectedCacheSize + sizeOfOriginals
			end
		
		end
		
		if optArgs.keepAllProxies or optArgs.markedForOfflineUse then
			expectedLocallyStoredBinariesSize = expectedLocallyStoredBinariesSize + sizeOfProxies
		else
			expectedCacheSize = expectedCacheSize + sizeOfProxies
		end

		-- Handle aux and xmp_develop files based on keepAllOriginals, keepAllProxies, or markedForOfflineUse flags
		if optArgs.keepAllOriginals or optArgs.keepAllProxies or optArgs.markedForOfflineUse then
			expectedLocallyStoredBinariesSize = expectedLocallyStoredBinariesSize + sizeOfDevelopSettings + sizeOfAuxes
		else
			expectedCacheSize = expectedCacheSize + sizeOfDevelopSettings + sizeOfAuxes
		end

		expectedData = {
			storedLocally = expectedLocallyStoredBinariesSize,
			photoCache = expectedCacheSize,
		}
		
	end
	
	rxTest:expectSingleValue( WFCatLocalStorage.getLocalStorageSize( wfCatalog, binaryStrategy, overrides )
		:map( function( result )
		
			if WFPlatformInfo.isDesktop then
		
				return KSTableUtils.derefOpt( result,
						optArgs.storeOriginalsOnMainVolume,
						optArgs.keepAllOriginals,
						optArgs.keepAllProxies )
					
			else
			
				return result
				
			end
		
		end )
		:map( mapAwayCruft ),
		expectedData )

end

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

-- @mark getLocalStorageSize Tests

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

if WFPlatformInfo.isDesktop then

	for _, storeOriginalsOnMainVolume in ipairs( { false, true } ) do
	
		for _, keepAllOriginals in ipairs( { false, true } ) do
		
			for _, keepAllProxies in ipairs( { false, true } ) do
			
				local testName = 'rxTestGetLocalStorageSize'
			
				if not storeOriginalsOnMainVolume then
					testName = string.format( "%s_ExternalOriginals", testName )
				end
				
				if keepAllOriginals and keepAllProxies then
					testName = string.format( "%s_KeepEverything", testName )
				elseif keepAllOriginals then
					testName = string.format( "%s_KeepAllOriginals", testName )
				elseif keepAllProxies then
					testName = string.format( "%s_KeepAllProxies", testName )
				end
			
				WFCatLocalStorageTest[ testName ] = function( rxTest )

					local optArgs = {
						storeOriginalsOnMainVolume = storeOriginalsOnMainVolume,
						keepAllOriginals = keepAllOriginals,
						keepAllProxies = keepAllProxies
					}

					testGetLocalStorageSize( rxTest, optArgs )

				end

			end

		end

	end

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

elseif WFPlatformInfo.isMobile() then

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

	WFCatLocalStorageTest[ 'rxTestGetLocalStorageSize_preferMaster' ] = function( rxTest )

		local optArgs = {
			markedForOfflineUse = true,
			binaryStrategy = LrmConfig.StrategyPreferMaster
		}

		testGetLocalStorageSize( rxTest, optArgs )

	end

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

	WFCatLocalStorageTest[ 'rxTestGetLocalStorageSize_preferProxy' ] = function( rxTest )

		local optArgs = {
			markedForOfflineUse = true,
			binaryStrategy = LrmConfig.StrategyPreferProxy
		}

		testGetLocalStorageSize( rxTest, optArgs )

	end

end

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

function WFCatLocalStorageTest.rxTestGetLocalStorageSize_markedForLocalUse( rxTest )

	local optArgs = {
		storeOriginalsOnMainVolume = true,
		markedForOfflineUse = true,
		keepAllOriginals = false,
		keepAllProxies = false,
		binaryStrategy = WFPlatformInfo.isMobile() and LrmConfig.StrategyPreferProxy or nil -- default
	}

	testGetLocalStorageSize( rxTest, optArgs )

end

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

-- @mark getRecommendedCacheSizes Tests

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

local function gbToBytes( gb )

	local multiplier = WFPlatformInfo.bytesInKilobyte()
	
	return gb * multiplier * multiplier * multiplier

end

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

function WFCatLocalStorageTest.rxTestGetRecommendedCacheSizes( rxTest )

	-- To understand the math in this test, please see the constants defined at
	-- the top of WFCatLocalStorage.getRecommendedCacheSizes.

	local wfCatalog = createFakeWFCatalog( rxTest )
	
	-- Basic Test
	
	if WFPlatformInfo.isMobile() then
		LrmVolumeAgentSettings.setTargetCacheSize( -1 )
	else
		NmbFileManagementSettings.setTargetCacheSize( -1 )
	end
	
	local optOverrides = {
		totalSpace = gbToBytes( 1000 ),
		freeSpace = gbToBytes( 101 ),
		reservedSpace = gbToBytes( 1 )
	}
	
	local expected = {
		{
			size = gbToBytes( 999 ),
			flag = 'max'
		},
		{
			size = 0,
			flag = 'min'
		},
		{
			size = -1,
			flag = 'current'
		},
		{
			size = gbToBytes( 10 ),
			flag = 'default'
		},
		{
			size = gbToBytes( 1 )
		},
		{
			size = gbToBytes( 5 )
		},
		{
			size = gbToBytes( 25 )
		},
		{
			size = gbToBytes( 50 )
		}
	}
	
	rxTest:expectSingleValue( 
			WFCatLocalStorage.getRecommendedCacheSizes( wfCatalog, optOverrides ), 
			expected )

	-- Current Cache Size Test

	if WFPlatformInfo.isMobile() then
		LrmVolumeAgentSettings.setTargetCacheSize( gbToBytes( 1 ) )
	else
		NmbFileManagementSettings.setTargetCacheSize( gbToBytes( 1 ) )
	end

	expected = {
		{
			size = gbToBytes( 999 ),
			flag = 'max'
		},
		{
			size = 0,
			flag = 'min'
		},
		{
			size = gbToBytes( 1 ),
			flag = 'current'
		},
		{
			size = gbToBytes( 10 ),
			flag = 'default'
		},
		{
			size = gbToBytes( 1 )
		},
		{
			size = gbToBytes( 5 )
		},
		{
			size = gbToBytes( 25 )
		},
		{
			size = gbToBytes( 50 )
		}
	}
	
	rxTest:expectSingleValue( 
			WFCatLocalStorage.getRecommendedCacheSizes( wfCatalog, optOverrides ), 
			expected )

end

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

-- Note: getPhotoCacheSizeStream shares an implementation with getLocalStorageSize,
-- so testing it directly is redundant. 

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

return WFRxTestCase.wrapSuite( 'WFCatLocalStorageTest', WFCatLocalStorageTest )
