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

WFCatStyleBinaryFetcherTest.lua

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

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

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

if not require 'WFPlatformInfo'.hasACR then return {} end

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

local Rx = require 'Rx'
local KSData = require 'KSData'
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local WFRxTestCase = require 'WFRxTestCase'
local WFCatStyleBinaryFetcher = require 'WFCatStyleBinaryFetcher'
local WFCatSpecMock = require 'WFCatSpecMock'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFRxHttpClient = require 'WFRxHttpClient'
local WFMockHttpRequest = require 'WFMockHttpRequest'
local WFFileUtils = require 'WFFileUtils'
local KSPathUtils = require 'KSPathUtils'

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

local kUserId = '87f526b44f9e41eca6cb5a550f84ceb6'
	-- Helpful bash one-liner. Put a new uuid on the clipboard (mac cmdline):
	-- uuidgen	| perl -p -e 's/-//g;s/[A-Z]/lc($&)/ge;' | pbcopy

local priority = WFRxHttpClient.requestPriorityLow
local cost = WFRxHttpClient.requestCostDefault

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

local WFCatStyleBinaryFetcherTest = {}

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

local function makeSuccessfulResponseHeaders( response )

	return {
		status_code = '200',
		[ 'Content-Length' ] = KSData.length( response ),
	}

end

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

local function makeUrlResponse( url, responseString, maybeCost )

	local response = KSData.make( responseString )

	return {
		url = url,
		response = response,
		headers = makeSuccessfulResponseHeaders( response ),
		options = {
			requestCost = maybeCost or cost,
			requestPriority = priority,
			followRedirects = true,
			requestHeaders = {
				{
					header = "Prefer",
					value = "download-direct",
				},
			}
		}
	}

end

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

local function pack( ... )

	return { ... }

end

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

local function pluckAssetId( success, assetId )

	return assetId

end

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

local function toSet( array )

	return KSTableUtils.mapArrayToSet( array )

end

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

local function ensureStyleDestinationFolderExistsfor( catalog )

	-- Make sure that the parent folder exists.
	-- Finding it using a dummy path.
	
	local dummyCameraProfilePath = catalog:fileSpec():pathForDCP( "Dummy.dcp" )
	local dummyPresetPath = catalog:fileSpec():pathForStyle( "6f607906eefa4e9fb80da8fde2805c11" )

	WFFileUtils.mkdir( KSPathUtils.parent( dummyCameraProfilePath ))
	WFFileUtils.mkdir( KSPathUtils.parent( dummyPresetPath ))
end

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

local function addStyleAssets( rxTest, catalog, styleType, count, optCameraModel )

	local docValues = {}
	local assetIds = {}
	
	local isCameraProfile = styleType == "camera_profile"
	
	local fileName
	if isCameraProfile then
		fileName = "Dummy profile.dcp"
	else
		fileName = "Dummy style.xmp"
	end
	
	for i= 1, count do
		
		local id = KSIdGenerator.uuidInLowercaseHex()
		assetIds[ i ] = id
		docValues[ i ] = {
		
			_id = id,
			type = "asset",
			subtype = styleType,
			name = "dummy name",
			importSource = {
				sha256 = "dummy sha",
			},
			fileName = fileName,
			resolvedDigest = isCameraProfile and "pseudo-resolved-digest",
			cameraProfileDigest = isCameraProfile and "pseudo-profile-digest",
			model = isCameraProfile and optCameraModel,
		}
		
	end
	
	rxTest:expectSingleValue(

		catalog:docStore():bulkSetDocValue( docValues ),

		':ignore:' )
		
	return assetIds
	
end

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

local function setMasterOverlay( rxTest, catalog, assetIds )

	local docValues = {}
	
	for assetId in ipairs( assetIds ) do
		
		docValues[ #docValues + 1 ] = {

			_id = assetId,
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v2/catalogs/blah/blah/master',
					},
				},
			},
		}
		
	end
	
	rxTest:expectSingleValue(

		catalog:docStore():bulkApplyOverlay( docValues ),

		':ignore:' )
	
end

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

local function addSuccessResponses( rxTest, catalogSpec, count )
	
	local response = WFMockHttpRequest()
	response:withResponseHeaders( {
		[ "Content-Length" ] = 13
	})
	
	for i = 1, count do
		local data = KSData.make( "pseudo-master" )
		
		catalogSpec:expectHttpRequest(
			response
				:get( "/v2/catalogs/blah/blah/master" )
				:respond( '200', data ) )
	end
	
end

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

local function addErrorResponses( rxTest, catalogSpec, count )
	
	local response = WFMockHttpRequest()
	response:withResponseHeaders( {
		[ "Content-Length" ] = 13
	})
	
	for i = 1, count do
		
		catalogSpec:expectHttpRequest(
			response
				:get( "/v2/catalogs/blah/blah/master" )
				:respond( '502', "Dummy error" ) )
	end
	
end

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

local function addImageAssetWithCameraModel( rxTest, docStore, cameraModel )

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'asset',
				subtype = 'image',
				captureDate = '2018-09-20T15:22:29',
				importSource = {
					fileName = "Image2.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 456,
				},
				xmp = {
					tiff = {
						Model = cameraModel,
					}
				},
			},

		':ignore:' )
end

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

function WFCatStyleBinaryFetcherTest.rxTestFetchOne( rxTest )
	rxTest:setTimeLimit( 10 )
	
	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
			makeUrlResponse( "/v2/catalogs/blah/blah/master", "pseudo-master" ),
		} )

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec, 'NmbCatalog' )

	local fetcher = WFCatStyleBinaryFetcher( catalog )
	
	local assetIds = addStyleAssets( rxTest, catalog, "preset", 1 )
	local responseStream = rxTest:observe( fetcher:getResponseStream():map( pack ) )
	fetcher:start()
	
	local presetPath = catalog:fileSpec():pathForStyle( assetIds[ 1 ] )
	setMasterOverlay( rxTest, catalog, assetIds )
	responseStream:expectData( { true, assetIds[ 1 ], presetPath } )
	
	fetcher:stop()

end


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

function WFCatStyleBinaryFetcherTest.rxTestMaxInFlight( rxTest )
	rxTest:setTimeLimit( 10 )
	
	local totalNumOfRequests = 6
	local expectedUrlResponse = {}
	for i = 1, totalNumOfRequests do
		expectedUrlResponse[ i ] = makeUrlResponse( "/v2/catalogs/blah/blah/master", "pseudo-master" )
	end
	
	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest, expectedUrlResponse )

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec, 'NmbCatalog' )
	ensureStyleDestinationFolderExistsfor( catalog )
	
	local cameraModel = "Nikon D5200"
	
	local fetcher = WFCatStyleBinaryFetcher( catalog, 2 )
	fetcher:setTestModeAndParams()
	local responseStream = rxTest:observe( 
		fetcher:getResponseStream()
			:map( pluckAssetId )
			:take( totalNumOfRequests )
			:toArray()
			:map( toSet ) )
	
	local assetIds = addStyleAssets( rxTest, catalog, "camera_profile", totalNumOfRequests, cameraModel )
	fetcher:start()
	
	setMasterOverlay( rxTest, catalog, assetIds )
	addImageAssetWithCameraModel( rxTest, catalog:docStore(), cameraModel )
	
	responseStream:expectData( KSTableUtils.mapArrayToSet( assetIds) )
	
	rxTest:expectSingleValue(

		Rx.Observable.just( fetcher:_test_getNumOfInFlightRequestArray() )
			:map( function( array )
				return math.max( unpack( array ) )
			end ),

		2 )

	fetcher:stop()
end

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

function WFCatStyleBinaryFetcherTest.rxTestEnsureCustomProfileExists( rxTest )
	rxTest:setTimeLimit( 10 )
	
	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest )
	addErrorResponses( rxTest, catalogSpec, 1 )
	addSuccessResponses( rxTest, catalogSpec, 1 )
	
	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec, 'NmbCatalog' )
	ensureStyleDestinationFolderExistsfor( catalog )
	
	local fetcher = WFCatStyleBinaryFetcher( catalog, 2 )
	fetcher:setTestModeAndParams()
	
	local assetIds = addStyleAssets( rxTest, catalog, "camera_profile", 1 )
	setMasterOverlay( rxTest, catalog, assetIds )
	
	local responseStream = rxTest:observe( 
		fetcher:getResponseStream():map( pack )
			:map( function( result ) 
				return result[ 1 ]
			end ))
	
	
	-- Bogus href. Should return false.
	local outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "bogus_url" ) )
	outputStream:expectDataWithinTime( 5, false )
	
	
	-- Bogus href. Should return false.
	outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "bogus_url" ) )
	outputStream:expectDataWithinTime( 5, false )
	
	
	-- Not started. Should return false.
	outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "/v2/catalogs/blah/blah/master" ) )
	outputStream:expectDataWithinTime( 5, false )
	
	fetcher:start()
	
	-- Valid url. But download failed. Should return false
	outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "/v2/catalogs/blah/blah/master" ))
	outputStream:expectData( false )
	
	-- The response stream too should get the update
	-- in case we downloaded a profile
	responseStream:expectData( false )
	
	-- Valid url. Should download and return true.
	outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "/v2/catalogs/blah/blah/master" ))
	outputStream:expectDataWithinTime( 5, true )
	
	-- The response stream too should get the update
	-- in case we downloaded a profile
	responseStream:expectData( true )
	
	-- Valid url. File already present. Should return true
	outputStream = rxTest:observe( fetcher:ensureCustomCameraProfileExists( "/v2/catalogs/blah/blah/master" ) )
	outputStream:expectDataWithinTime( 5, true )
	
	fetcher:stop()
	fetcher:close()

end

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

function WFCatStyleBinaryFetcherTest.rxTestMakeAdjustablePriorityASAPRequestForCameraModel( rxTest )
	rxTest:setTimeLimit( 10 )
	
	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest )
	addSuccessResponses( rxTest, catalogSpec, 1 )
	
	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec, 'NmbCatalog' )
	ensureStyleDestinationFolderExistsfor( catalog )
	
	local fetcher = WFCatStyleBinaryFetcher( catalog, 2 )
	fetcher:setTestModeAndParams()
	
	local cameraModel = "Apple iPhone10,3 back camera"
	local assetIds = addStyleAssets( rxTest, catalog, "camera_profile", 1, cameraModel )
	setMasterOverlay( rxTest, catalog, assetIds )

	
	-- Not started. Should return nil.
	local outputStream = rxTest:observe( fetcher:makeAdjustablePriorityASAPRequestForCameraModel( cameraModel ))
	outputStream:expectData()
	
	local refreshStream = rxTest:observe( fetcher:getRefreshStream()
		:map( pack )
		:map( function( result )
			return result[ 1]
		end ))
	
	fetcher:start()
	
	-- Should return cancel function
	local cancelFn
	rxTest:expectSingleValue(

		fetcher:makeAdjustablePriorityASAPRequestForCameraModel( cameraModel )
			:tap( function( fn )
				cancelFn = fn
			end ),

		':ignore:' )
		
	rxTest:expectEqual( type( cancelFn ) , "function" )
	refreshStream:expectData( true )
	
	-- TODO [ashj 2018-09-20]: Need to validate also
	cancelFn()
	
	fetcher:stop()
	fetcher:close()

end

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

return WFRxTestCase.wrapSuite( 'WFCatStyleBinaryFetcherTest', WFCatStyleBinaryFetcherTest )
