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

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.os
local WFPlatformInfo = require 'WFPlatformInfo'

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

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

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

	-- Lrmobile_models
local LrmAlbumModel = require 'LrmAlbumModel'
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatAlbumTestUtils = require 'WFCatAlbumTestUtils'

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

if not WFPlatformInfo.isMobile() then
	return {}
end

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

local LrmAlbumModelTest = {}

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

local derefOpt = KSTableUtils.derefOpt

local TEST_TIMEOUT = 10

local kAlbumId = KSIdGenerator.uuidInLowercaseHex()

local kExpectedAssetListOutput

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


if WFPlatformInfo.isAndroid then

	-- Android uses fewer attributes.
	-- Sending more attributes cause jni conversion
	-- to be slower.
	kExpectedAssetListOutput= {
		captureDate = "2022-04-25T10:29:10.805Z",
		croppedHeight = 1000,
		croppedWidth = 1000,
		fileName = "Image01.jpg",
		hasThumbnail = 1,
		importedByUser = "",
		isPopulated = true,
		isVideo = false,
		modifiedDate = "2022-04-25T10:29:10.805Z",
		thumbnailMD5 = "109c9df400f53d2ef2d2b927e0762b86",
		needsUpdateAISettings = false,
	}

else

	kExpectedAssetListOutput = {
		apertureValue = "100.0",
		cameraMaker = "Make",
		cameraModel = "Model",
		captureDate = "2022-04-25T10:29:10.805Z",
		contentType = "image/*",
		commentCount = 0,
		croppedHeight = 1000,
		croppedWidth = 1000,
		exposureTime = "100",
		favoriteCount = 0,
		fileName = "Image01.jpg",
		flag = "unflagged",
		fnumber = "100.0",
		hasThumbnail = 1,
		isMergedHDR = 0,
		importedByUser = "",
		isVideo = 0,
		isoSpeedRatings = "100",
		modifiedDate = "2022-04-25T10:29:10.805Z",
		rating = 0,
	}

end

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

local function getSubmodel( model, submodelName )

	return model.models[submodelName]

end

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

local function makeSubmodel( model, submodelName, ... )

	return getSubmodel( model, submodelName )( model, ... )

end

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

local function selectFirst( a, _ )
	return a
end

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

-- @mark assetList model

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

function LrmAlbumModelTest.rxTestCannotInitModelWithoutWFCatalog( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	-- fails with a nil value
	rxTest:expectSpecificError('bad argument #1 (wfCatalog) to albumModel (WFCatalog expected, got nil)', function ()
		LrmAlbumModel.albumModel( nil, kAlbumId )
	end)

	-- succeeds with a wfCatalog instance
	LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

end

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

function LrmAlbumModelTest.rxTestAssetList( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			_id = kAlbumId,
			type = 'album',
			docId = kAlbumId,
			overlay = {
				subtype = 'collection',
				private = false,
				download = false,
				updated = '2012-08-15T15:22:29',
				created = '2012-08-15T15:22:29',
			},
		},
		':ignore:' )

	local assetId

	rxTest:waitFor(
		WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 1, {
			captureDate = '2022-04-25T10:29:10.805Z'
		} )
			:map( function( assetIds )
				assetId = assetIds[1]
			end )
	)

	if WFPlatformInfo.isAndroid then

		-- need to grab thumbnailMD5 as thumbnail file content is randomized
		-- and also importTimestamp

		rxTest:waitFor( wfCatalog:docStore():docValue( assetId )
			:map( function( assetDoc )
				kExpectedAssetListOutput.thumbnailMD5 = assetDoc._localOnly.thumbnail2x.md5
				kExpectedAssetListOutput.importDate = assetDoc.importSource.importTimestamp
			end )
		)

	end

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( albumModel.value )
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( nil )

	local args = {
			'captureDate', 	-- sortCriteria
			true, 			-- ascending
			{}, 			-- assetFilter
			{},				-- assetTypeFilter
			{},				-- keyword
			{},				-- edited
			0, 				-- ratingFilter
			'>=', 			-- ratingComparisonOperator,
		}

	local assetList = makeSubmodel( albumModel, 'assetListModel',  unpack( args ) ).value
		:pluck( 'albumInfo', 'photos' )
		:map( function( photos )
			if photos then
				for _, photo in ipairs( photos ) do
					photo.id = nil
				end
			end

			return photos
		end )


	local assetListObs = rxTest:observe( assetList )
	rxTest:finally( function()
		assetListObs:dispose()
	end )

	assetListObs:expectData( {
		kExpectedAssetListOutput
	} )

end

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

-- @mark cover asset model

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

function LrmAlbumModelTest.rxTestAlternativeCoverAssetId( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay{
			type = 'album',
			_id = kAlbumId,
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )


	local assetIds = rxTest:getFirstValue( WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 1 ) )

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( albumModel.value )
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( nil )

	local alternativeCoverAssetIdObs = rxTest:observe( makeSubmodel( albumModel, 'alternativeCoverAssetId' ).value )
	rxTest:finally( function()
		alternativeCoverAssetIdObs:dispose()
	end )


	alternativeCoverAssetIdObs:expectData( assetIds[ 1 ] )

end

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

-- @mark cover image model

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

-- Brief: Test that we are returning the asset with the oldest capture date as 
-- the cover image for albums

function LrmAlbumModelTest.rxTestCoverAssetIdIsOldestCaptureDateAsset( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:waitFor( wfCatalog:docStore():applyOverlay{
			type = 'album',
			_id = kAlbumId,
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		}
	)


	rxTest:waitFor( WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 3 ) )
	
	-- Get the assetId with the oldest captured date
	
	local oldestAssetId = rxTest:getFirstValue(

		wfCatalog:indexStore():queryOnce{ 
			sql = 'SELECT docId, captureDate FROM coreInfo' 
		}

		:map( function( rows )

			local oldestCaptureDate = KSDate.timeInIso8601Format()
			local oldestAssetId
			
			for _,row in pairs( rows ) do
				
				local rowCaptureDate = derefOpt( row, 2 )
				
				if oldestCaptureDate > rowCaptureDate then
				
					oldestCaptureDate = rowCaptureDate
					oldestAssetId = derefOpt( row, 1 )
					
				end
				
			end

			return oldestAssetId

		end )
	)

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( getSubmodel( albumModel, 'coverImageModel' )( albumModel ).value )
	
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( oldestAssetId )

end

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

-- Brief: Test that file name should be the secondary sort criteria when capture 
-- date is the same or missing.

function LrmAlbumModelTest.rxTestCoverAssetIdIsFilenameAscAsset( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local optOverrides = { captureDate = '2012-08-15T15:22:29', randomFileName = true }
	
	rxTest:waitFor( wfCatalog:docStore():applyOverlay {
				type = 'album',
				_id = kAlbumId,
				private = false,
				download = false,
				updated = '2012-08-15T15:22:29',
				created = '2012-08-15T15:22:29',
			}
		)


	rxTest:waitFor( 
		WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 3, optOverrides ) 
	)
	
	-- Get the assetId with the highest Lexicographic value, a being the highest and z the lowest.
	
	local assetIdWithHighestLexicographicValue = rxTest:getFirstValue( 
		
		wfCatalog:indexStore():queryOnce{ 
			sql = 'SELECT docId, fileName FROM coreInfo' 
		}

		:map( function( rows )	
			
			local assetNameWithHighestLexicographicOrder = derefOpt( rows, 1, 2 )
			local assetIdWithHighestLexicographicOrder = derefOpt( rows, 1, 1 )
						
			for _,row in pairs( rows ) do
							
				local assetName = derefOpt( row, 2 )
				
				if assetNameWithHighestLexicographicOrder > assetName then
					
					assetNameWithHighestLexicographicOrder = assetName
					assetIdWithHighestLexicographicOrder = derefOpt( row, 1 )
					
				end
				
			end
			
			return assetIdWithHighestLexicographicOrder

		end )
	)

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( getSubmodel( albumModel, 'coverImageModel' )( albumModel ).value )
	
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( assetIdWithHighestLexicographicValue )

end

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

-- Brief: Test that the tertiary sort is based on the docId

function LrmAlbumModelTest.rxTestCoverAssetIdIsDocIdAscAsset( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local optOverrides = { captureDate = '2012-08-15T15:22:29' }
	
	rxTest:waitFor( wfCatalog:docStore():applyOverlay {
				type = 'album',
				_id = kAlbumId,
				private = false,
				download = false,
				updated = '2012-08-15T15:22:29',
				created = '2012-08-15T15:22:29',
			}
		)


	rxTest:waitFor( 
		WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 3, optOverrides ) 
	)
	
	-- Get the assetId with the oldest captured date
	
	local oldestCaptureDateAssetId = rxTest:getFirstValue(

		wfCatalog:indexStore():queryOnce{ 
			sql = 'SELECT docId FROM coreInfo' 
		}

		:map( function( rows )

			local oldestDocId = derefOpt( rows, 1, 1 )
			
			for _,row in pairs( rows ) do
				
				local rowDocId = derefOpt( row, 1 )
				
				if oldestDocId > rowDocId then
					oldestDocId = rowDocId
				end
				
			end

			return oldestDocId

		end )
	)

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( getSubmodel( albumModel, 'coverImageModel' )( albumModel ).value )
	
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( oldestCaptureDateAssetId )

end

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

-- @mark cacheSize model

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

function LrmAlbumModelTest.rxTestCacheSize( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			type = 'album',
			_id = kAlbumId,
			name = 'test album',
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )


	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( albumModel.value )
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( nil )

	local cacheSizeObs = rxTest:observe( makeSubmodel( albumModel, 'cacheSizeModel' ).value )
	rxTest:finally( function()
		cacheSizeObs:dispose()
	end )

	cacheSizeObs:expectData( 0 )


	local assets = {
		selectFirst( WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog ) ),
		selectFirst( WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog ) )
	}

	-- make the second asset a deleted image
	KSTableUtils.setNested( assets, 'deleted_image', 2, 'subtype' )

	rxTest:waitFor( wfCatalog:docStore():bulkApplyOverlay( assets ) )

	local albumAssets = {
		WFCatAlbumTestUtils.createAlbumAsset( rxTest, kAlbumId, assets[ 1 ]._id ),
		WFCatAlbumTestUtils.createAlbumAsset( rxTest, kAlbumId, assets[ 2 ]._id )
	}

	rxTest:waitFor( wfCatalog:docStore():bulkApplyOverlay( albumAssets ) )

	cacheSizeObs:expectData( 664 )

	rxTest:waitFor( wfCatalog:docStore():deleteDoc( albumAssets[ 1 ]._id ) )

	cacheSizeObs:expectData( 0 )

end

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

-- @mark flag status

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

function LrmAlbumModelTest.rxTestFlagStatus( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )
	local userId = wfCatalog:ozUserId()

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			type = 'album',
			_id = kAlbumId,
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local albumObs = rxTest:observe( albumModel.value )
	rxTest:finally( function()
		albumObs:dispose()
	end )

	albumObs:expectData( nil )

	local docValue = WFCatAssetTestUtils.createAssetDocument(rxTest, wfCatalog )

	WFTableMerge.applyOverlay( docValue, {
			reviews = {
				[ userId ] = {
					flag = 'pick',
				}
			}
		} )

	local assetId = docValue._id

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( docValue ) )

	local flagStatusCountModel = makeSubmodel( albumModel, 'flagStatusCountModel' )

	local flagObs = rxTest:observe( flagStatusCountModel.value )

	flagObs:expectData {
			pick = 0,
			reject = 0,
			unflagged = 0,
		}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay {
			type = 'album_asset',
			_id = KSIdGenerator.uuidInLowercaseHex(),
			album = {
				id = kAlbumId,
			},
			asset = {
				id = assetId,
			}
		} )

	flagObs:expectData {
			pick = 1,
			reject = 0,
			unflagged = 0,
		}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay {
			_id = assetId,
			reviews = {
				[ userId ] = {
					flag = 'reject',
				}
			}
		} )

	flagObs:expectData {
			pick = 0,
			reject = 1,
			unflagged = 0,
		}

end

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

-- @mark best photos

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

function LrmAlbumModelTest.rxTestBestPhotos( rxTest )

	local function pluckAssetIdTopicIdSortOrder( photoList )

		local photos = KSTableUtils.derefOpt( photoList, 'albumInfo', 'photos' )
		if not photos then
			return nil
		end

		local result = {}

		for _, photo in ipairs( photos ) do

			result[ #result + 1 ] = {
				photo.id,
				photo.topicId,
				photo.sortOrder
			}
		end

		return result

	end

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	-- create the normal album
	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			type = 'album',
			subtype = 'collection',
			_id = kAlbumId,
			name = 'test album',
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )

	local clusterId = KSIdGenerator.uuidInLowercaseHex()

	-- create the cluster
	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			type = 'album',
			subtype = 'cluster',
			_id = clusterId,
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )

	local albumModel = LrmAlbumModel.albumModel( wfCatalog, kAlbumId )

	local bestPhotosListModel = makeSubmodel( albumModel, 'bestPhotosListModel', clusterId )

	local bestPhotosObs = rxTest:observe( bestPhotosListModel.value:map( pluckAssetIdTopicIdSortOrder ) )

	bestPhotosObs:expectData( {} )

	local assetIds

	rxTest:waitFor( WFCatAlbumTestUtils.insertAlbumAssets( rxTest, wfCatalog, kAlbumId, 2 )
		:map( function( ids )
			assetIds = ids
			return assetIds
		end ) )


	local topicId = KSIdGenerator.uuidInLowercaseHex()

	-- create the topic
	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			type = 'album',
			subtype = 'topic',
			_id = topicId,

			parent = {
				id = clusterId
			},

			cover = {
				id = assetIds[1]
			},
			updated = '2012-08-15T15:22:29',
			created = '2012-08-15T15:22:29',
		},
		':ignore:' )

	-- create the topic album_assets

	local topicAlbumAssets = {
		WFCatAlbumTestUtils.createAlbumAsset( rxTest, topicId, assetIds[1] ),
		WFCatAlbumTestUtils.createAlbumAsset( rxTest, topicId, assetIds[2] )
	}

	topicAlbumAssets[1].order = '2305843009232293951'
	topicAlbumAssets[2].order = '2305843009232293950'

	rxTest:expectSingleValue( wfCatalog:docStore():bulkApplyOverlay ( topicAlbumAssets ),
		':ignore:' )


	local expected = {
		{
			KSTableUtils.derefOpt( topicAlbumAssets, 1 , 'asset', 'id' ),
			KSTableUtils.derefOpt( topicAlbumAssets, 1 , 'album', 'id' ),
			KSTableUtils.derefOpt( topicAlbumAssets, 1 , 'order')
		},
		{
			KSTableUtils.derefOpt( topicAlbumAssets, 2, 'asset', 'id' ),
			KSTableUtils.derefOpt( topicAlbumAssets, 2, 'album', 'id' ),
			KSTableUtils.derefOpt( topicAlbumAssets, 2, 'order')
		},
	}

	-- INFO [immisch 2020-07-16]: This is the query used on iOS
	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = [[ SELECT assetId, albumId, sortOrder FROM album_asset
						INNER JOIN albums
							ON (albums.docId == album_asset.albumId and albums.subtype == 'topic' and albums.parentId == ? )
						ORDER BY sortOrder DESC ]],
			quiesceUntilUpToDate = true,
			clusterId
		},
		expected )

	bestPhotosObs:expectData( expected )

end

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

return WFRxTestCase.wrapSuite( 'LrmAlbumModelTest', LrmAlbumModelTest )
