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

LrmAlbumAssetUtilsTest.lua

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

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

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

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

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

	-- WF.managed_catalog
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local LrmAlbumAssetUtils = require 'LrmAlbumAssetUtils'
local WFCatOzTestSession = require 'WFCatOzTestSession'

	-- WF.core.crypto
local WFCrypto = require 'WFCrypto'

	-- Lrmobile_models
local LrmCatCollaboration = require 'LrmCatCollaboration'

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

local derefOpt = KSTableUtils.derefOpt

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

local LrmAlbumAssetUtilsTest = {}

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

local function createFakeWFCatalog( rxTest )

	local optArgs = {}
	optArgs.indexDefinitions = 'LrmCatalogIndexDefinitions.lua'

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

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

local function buildDocValue( rxTest, wfCatalog, assetId, overrides )

	assetId = assetId or KSIdGenerator.uuidInLowercaseHex()
	
	return WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, overrides )

end

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

function LrmAlbumAssetUtilsTest.rxTestRemoveAssetsFromOwnAlbum( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetDoc = {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'album_asset',
		album = {
			id = albumId,
		},
		asset = {
			id = assetId,
		},
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumAssetDoc ) )
	
	local albumAssetObserve = rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	
	albumAssetObserve:expectDataWithinTime( 2, {{ assetId }} )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( wfCatalog, albumId, { assetId } )
		, { assetId } )
		
	albumAssetObserve:expectDataWithinTime( 2, {} )
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestRemoveAssetsFromOwnAlbum_assetNotInAlbum( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetObserve = rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( wfCatalog, albumId, { assetId } )
		, {} )
		
	albumAssetObserve:expectDataWithinTime( 2, {} )
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp == nil
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestRemoveAssetsFromOwnAlbum_removeCoverAsset( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo',
		cover = {
			id = assetId
		}
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetDoc = {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'album_asset',
		album = {
			id = albumId,
		},
		asset = {
			id = assetId,
		},
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumAssetDoc ) )
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			return derefOpt( albumDocValue, 'cover', 'id' )
		end )
	, assetId )
	
	local albumAssetObserve = rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	
	albumAssetObserve:expectDataWithinTime( 2, {{ assetId }} )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( wfCatalog, albumId, { assetId } )
		, { assetId } )
		
	albumAssetObserve:expectDataWithinTime( 2, {} )
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil and not albumDocValue.cover
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestRemoveAssetsFromOwnAlbum_albumNotExists( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectOnlyError( LrmAlbumAssetUtils.removeAssetsFromOwnAlbum( wfCatalog, albumId, { assetId } )
		, 'invalidAlbum' )

	rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	:expectData( {} )
	:dispose()

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToOwnAlbum( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, { assetId } )
		, { assetId } )
		
	rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	:expectDataWithinTime( 3, {{ assetId }} )
	:dispose()
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToOwnAlbum_withOrder( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local order = { [ assetId ] = 'a' }
	rxTest:expectSingleValue( LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, { assetId }, order )
		, { assetId } )
		
	rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId, sortOrder FROM album_asset WHERE albumId = ?", albumId
	} )
	:expectDataWithinTime( 3, {{ assetId, 'a' }} )
	:dispose()
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToOwnAlbum_existingAsset( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetDoc = {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'album_asset',
		album = {
			id = albumId,
		},
		asset = {
			id = assetId,
		},
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumAssetDoc ) )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, { assetId } )
		, {} )
		
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil
		end )
	, false )

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToOwnAlbum_assetExistenceCombination( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc1 = buildDocValue( rxTest, wfCatalog )
	local assetDoc2 = buildDocValue( rxTest, wfCatalog )
	
	local assetId1 = assetDoc1._id
	local assetId2 = assetDoc2._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc1 ) )
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc2 ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		type = 'album',
		subtype = 'collection',
		name = 'foo'
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetDoc1 = {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'album_asset',
		album = {
			id = albumId,
		},
		asset = {
			id = assetId1,
		},
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumAssetDoc1 ) )
	
	local observeAlbumAsset = rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT COUNT(*) FROM album_asset WHERE albumId = ?", albumId
	} )
	
	observeAlbumAsset:expectDataWithinTime( 2, {{ 1 }} )
	
	rxTest:expectSingleValue( LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, { assetId1, assetId2 } )
		, { assetId2 } )

	observeAlbumAsset
	:expectDataWithinTime( 3, {{ 2 }} )
	:dispose()
	
	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( albumDocValue )
			local userUpdatedTimestamp = derefOpt( albumDocValue, 'userUpdated' )
			return userUpdatedTimestamp ~= nil
		end )
	, true )

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToOwnAlbum_albumNotExists( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local wfCatalog = createFakeWFCatalog( rxTest )
	
	local assetDoc = buildDocValue( rxTest, wfCatalog )
	local assetId = assetDoc._id
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	
	local assetDocFromDocStore
	
	rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
		:map( function( docValue )
			assetDocFromDocStore = docValue
			return true
		end ) )
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectOnlyError( LrmAlbumAssetUtils.addAssetsToOwnAlbum( wfCatalog, albumId, { assetId } )
		, 'invalidAlbum' )

	rxTest:observe( wfCatalog:indexStore():query{
		sql = "SELECT assetId FROM album_asset WHERE albumId = ?", albumId
	} )
	:expectData( {} )
	:dispose()

end

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

function LrmAlbumAssetUtilsTest.rxTestAddAssetsToNewAdhocAlbumAndShare_testNightly( rxTest )

	rxTest:setTimeLimit( 300 )

	local testSession, wfCatalog, _, pusher, puller, _, _ = WFCatOzTestSession.setupOzTest( rxTest )
	assert( testSession )

	-- add asset document 

	local userId = wfCatalog:ozUserId()
	local catalogId = wfCatalog:ozCatalogId()
	
	local tempTestFolder = WFTestFileUtils.createTestFolder( rxTest )
	local fileName = 'IMG_7763.JPG'

	local originalFilePath = WFCatAssetTestUtils.getTestDataOriginalFilePath( rxTest, tempTestFolder, fileName )
	local sha256 = WFCrypto.digestFile( originalFilePath, 'SHA256' )
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local revisionId = KSIdGenerator.uuidInLowercaseHex()
	
	local assetDocValue = WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath, userId )
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	puller:getUpdatesContinuously()
	
	local sendNUpdates = rxTest:observe( pusher:_DEBUG_sendNUpdates( 1 ) )
	sendNUpdates:expectCompletedWithinTime( 30 )
	sendNUpdates:dispose()

	local observeDocOnOz = rxTest:observe( wfCatalog:docStore():ongoingDocValue( assetId )
			:map( function( docValue )
				local receivedUpdateFromOz = docValue._serverProvidedMetadata ~= nil
				return receivedUpdateFromOz
			end ) )
			
	observeDocOnOz:expectDataWithinTime( 40, true )
	observeDocOnOz:dispose()

	puller:stopContinuousUpdates()

	-- create and share adhoc album

	local albumName = 'adhocAlbum'
	local albumId
	rxTest:expectSingleValue( LrmAlbumAssetUtils.addAssetsToNewAdhocAlbumAndShare( wfCatalog, albumName, { assetId } )
		:map( function( newAlbumId, didCreateWithOz )
			albumId = newAlbumId
			return didCreateWithOz
		end ),	
		true )

	-- Check shared album url exists

	local nonNilResult = false
	rxTest:expectSingleValue( LrmCatCollaboration.sharedAlbumUrl( wfCatalog, albumId, nonNilResult )
		:map( function( url )
			return url ~= nil
		end ), true )

	-- Check asset is in the album

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce( {
			sql = "SELECT assetId FROM album_asset WHERE albumId = ?",
			albumId,
			singleColumnQuery = true,
		} ), { assetId } )

end

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

return WFRxTestCase.wrapSuite( 'LrmAlbumAssetUtilsTest', LrmAlbumAssetUtilsTest )