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

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

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

	-- RxLua
local Rx = require 'Rx'

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

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

	-- WF.oz_client.managed_catalog.sync
local WFCatSyncClientBase = require 'WFCatSyncClientBase'
local WFOzUserTestSession = require 'WFOzUserTestSession'
local WFCatUser = require 'WFCatUser'
local WFCatalog = require 'WFCatalog'

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

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

local check = assert( WFArguments.check )

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

-- @mark WFCatSyncClientBaseTest namespace

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

local WFCatSyncClientBaseTest = {}

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

local function stripWobblyBits( changedDocsResult )

	changedDocsResult.continueFrom = '<number>'

	for i, doc in ipairs( changedDocsResult ) do
	
		doc._serverProvidedMetadata = nil
		
		if doc.type ~= 'asset' then
			doc._revId = nil
		end
		
		if type( doc ) == 'table' then
		
			for key, value in pairs( doc ) do
			
				if key == '_changeCounter' then
				
					doc[ key ] = '<number>'
				
				end
			
			end
		
		end
		
	end

	return changedDocsResult

end

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

local function sortByDocId( docs )

	table.sort( docs, function( a, b ) return a._id < b._id end )
	return docs
	
end

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

local function observeIsOnOz( rxTest, catalog, docId )

	return rxTest:observe( catalog:docStore():ongoingDocValue( docId )
		:map( function( docValue )

			local isLabeledOz = not not ( docValue._labels and docValue._labels.oz )
			
			return isLabeledOz

		end )
	)

end

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

function WFCatSyncClientBaseTest.rxTestSyncInfrastructureSetup_testNightly( rxTest )

	rxTest:setTimeLimit( 180 )
	rxTest:makeVerbose()
	
	local userTestSession = WFOzUserTestSession( rxTest, { 
											suppressStoreTokens = true,
											v2 = true 
								} )
	
	local user
	local catSpec
	local catalog
	
	local userInfoCachePath = KSPathUtils.child( KSPathUtils.getStandardFilePath 'temp', 'userCache' )
	local docStoreRootPath = KSPathUtils.getStandardFilePath 'temp'
	
	rxTest:expectSingleValue( userTestSession:getCleanOzAccount()
		
		:flatMapLatest( function( userId )
		
			check( 'uuid' )
			
			user = WFCatUser( userTestSession:ozServerBaseUrl(), userId, userInfoCachePath )
			
			rxTest:finally( function()
			
				local userDocStore = user:userInfoCache()
				
				userDocStore:haltAndDelete()
				
			end )
			
			local accessToken, refreshToken = userTestSession:accessAndRefreshTokens()
			
			return user:storeTokens( accessToken, refreshToken )
				:concat( Rx.Observable.just( true ) )
				:flatMapLatest( function()
				
					return user:createCatalog( 'lightroom' )
					
				end )
			
		end )
		
		:map( function( catalogSpec )
		
			check( 'WFCatSpec' )
			
			catSpec = catalogSpec
			
			catalog = WFCatalog( catalogSpec, docStoreRootPath, { } )
			
			rxTest:finally( function()
			
				catalog:docStore():haltAndDelete()
				catalog:indexStore():haltAndDelete()
				
			end )
			
			return true
			
		end ), true )
		
	
	rxTest:expectSingleValue(

		catalog:docStore():changedDocs { fromChangeCounter = 0 },

		{
			continueFrom = 1,
		} )
		
	-- push album doc ...

	local albumId = KSIdGenerator.uuidInLowercaseHex()

	local albumDocUrl = string.format( '%s/albums/%s',
										  catSpec:catalogHref(),
										  albumId )

	local albumDocContent = KSJsonEncoder.encodeOzUploadJson {
		subtype = 'collection',
		payload = {
			name = "WFCatSyncClientBaseTest album",
			
		},
	}

	local albumDocPutArgs = {
		requestHeaders = {
			{ header = 'Content-Type', value = 'application/json' },
			{ header = 'Accept', value = 'application/json' },
		},
	}

	rxTest:expectOnlyCompleted(
		catSpec:put( albumDocUrl, albumDocContent, albumDocPutArgs )
			:filter( function() return false end ) )
	
	local optIndexSpecScript = catalog:indexStore():indexSpecScript() 
								or 'WFCatIndexDefinitions.lua'
								
	local optSyncClientImpName = 'WFCatSyncClientImp'
	
	local options = {
		syncClientImpName = optSyncClientImpName,
		serverBase = catalog:ozUser():serverBaseUrl(),
		userId = catalog:ozUserId(),
		userInfoCacheFolder = catalog:ozUser():userInfoCacheFolder(),
		catalogId = catalog:ozCatalogId(),
		catalogDoc = catalog:ozCatalogSpec():catalogDoc(),
		indexSpecScript = optIndexSpecScript,
		rootCatalogPath = catalog:fileSpec():rootPath(),
		docStorePath = catalog:fileSpec():docStorePath(),
		testMode = true -- Only used by unit tests.
	}
	
	local syncClientBase = WFCatSyncClientBase( options )
	
	rxTest:expectOnlyCompleted( syncClientBase:startSync( true ) )
	
	-- check that we got the value from Oz
	
	local expectedDocUpdates = 
		{
			{
				_changeCounter = "<number>",
				_id = catalog:ozCatalogId(),
				_labels = {
					oz = true,
				},
				_localId = 2,
				name = "lightroom",
				subtype = "lightroom",
				type = "catalog",
			},
			{
				_changeCounter = '<number>',
				_id = albumId,
				_labels = {
					oz = true,
				},
				_localId = 1,
				name = "WFCatSyncClientBaseTest album",
				subtype = "collection",
				type = "album",
			},
			continueFrom = '<number>',
		}
	
	local docUpdates = {}
	
	local changedDocsObserver = rxTest:observe(
			catalog:docStore():scannedFilteredMappedChangesFeedSince( "WFCatMetadataPullerTest" , "reportAllChanges", {}, 2 )
		:map( function( doc )
			docUpdates[ #docUpdates + 1 ] = KSTableUtils.shallowcopy( doc )
			return sortByDocId( docUpdates )
		end )
		:map( stripWobblyBits ) )

	changedDocsObserver:expectDataWithinTime( 40, sortByDocId( expectedDocUpdates ) )

	changedDocsObserver:dispose()
	
	-- update album locally so it can sync
	
	rxTest:expectSingleValue(

		catalog:docStore():applyOverlay {
			_id = albumId,
			name = "WFCatSyncClientBaseTest album 2",
		},
		':ignore:' )
		
	-- verify local change is up on Oz
	
	local observeAlbumIsOnOz = observeIsOnOz( rxTest, catalog, albumId )
	observeAlbumIsOnOz:expectDataWithinTime( 40, true )
	
	rxTest:expectSingleValue(
		catSpec:get( albumDocUrl )
			:map( KSJsonDecoder.decodeWebSafeJson )
			:map( function( docValue )
			
				return KSTableUtils.derefOpt( docValue, 'payload', 'name' )
				
			end ),
		'WFCatSyncClientBaseTest album 2' )
	
	-- stop sync so local changes don't get up to Oz
	
	rxTest:expectOnlyCompleted( syncClientBase:stopSync() )
	
	rxTest:sleep( 5 )

	-- change local value
	
	rxTest:expectSingleValue(

		catalog:docStore():applyOverlay {
			_id = albumId,
			name = "WFCatSyncClientBaseTest reverted",
		},
		':ignore:' )
		
	rxTest:sleep( 5 )
	
	-- run request again to verify that the local change hasn't made it to Oz
	
	rxTest:expectSingleValue(
		catSpec:get( albumDocUrl )
			:map( KSJsonDecoder.decodeWebSafeJson )
			:map( function( docValue )
			
				return KSTableUtils.derefOpt( docValue, 'payload', 'name' )
				
			end ),
		'WFCatSyncClientBaseTest album 2' )
		
	syncClientBase:close()
	
end

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

return WFRxTestCase.wrapSuite( WFCatSyncClientBaseTest )