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

WFCatBinaryDownloaderTest.lua

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

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.lua
local KSData = require 'KSData'

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

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

local WFRxHttpClient = require 'WFRxHttpClient'

local WFCatTestSession = require "WFCatTestSession"
local WFCatMetadataPusher = require "WFCatMetadataPusher"
local WFCatMetadataPuller = require "WFCatMetadataPuller"
local WFCatBinaryUploader = require "WFCatBinaryUploader"
local WFRxScheduler = require "WFRxScheduler"

	-- RxLua
local Rx = require 'Rx'
local Observable = Rx.Observable

local KSIdGenerator = require "KSIdGenerator"
local KSPathUtils = require "KSPathUtils"
local WFFileUtils = require 'WFFileUtils'
local WFCatAssetTestUtils = require "WFCatAssetTestUtils"
local WFOzDocUtils = require "WFOzDocUtils"


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

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.requestPriorityMedium
local cost = WFRxHttpClient.requestCostDefault

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

local WFCatBinaryDownloaderTest = {}

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

local function mapKSDataToString( maybeKSData )

	if not KSData.isData( maybeKSData ) then
		return string.format( "Expected KSData, got %s instead", type( maybeKSData ) ),
			   maybeKSData
	else
		return tostring( maybeKSData )
	end

end


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

local function makeSuccessfulResponseHeaders( response )

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

end

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

local function makeUrlResponse( url, responseString, maybeCost, useDirectDownload )

	local response = KSData.make( responseString )

	local result = {
		url = url,
		response = response,
		headers = makeSuccessfulResponseHeaders( response ),
		options = {
			requestCost = maybeCost or cost,
			requestPriority = priority
		}
	}
	
	if useDirectDownload then
		result.options[ 'followRedirects' ] = true
		
		result.options[ 'requestHeaders' ] = {
			{ header = 'Prefer', value = 'download-direct' }
		}
	end
	
	return result

end

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

function WFCatBinaryDownloaderTest.rxTestGetRendition( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
			makeUrlResponse( "/v2/catalogs/blah/blah/blah/t2x", 
				"pseudo-jpeg-thumbnail2x", 
				WFRxHttpClient.requestCostDownloadRendition ),
		} )
	
	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/rendition_type/thumbnail2x' ] = {
						href = '/v2/catalogs/blah/blah/blah/t2x',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectSingleValue(

		downloader:getRendition( '6f607906eefa4e9fb80da8fde2805c11', 'thumbnail2x', priority )
			:map( mapKSDataToString ),

		'pseudo-jpeg-thumbnail2x' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetRendition_errorUrlNotAvailable( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
		} )

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/rendition_type/not-thumbnail2x' ] = {
						href = '/v2/catalogs/blah/blah/blah/t2x',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectOnlyError(

		downloader:getRendition( '6f607906eefa4e9fb80da8fde2805c11', 'thumbnail2x', priority )
			:map( mapKSDataToString ),

		"No URL provided by Oz for asset ID 6f607906eefa4e9fb80da8fde2805c11 link /rels/rendition_type/thumbnail2x", 
		':ignore:', ':ignore:', ':ignore:', ':ignore:' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetRendition_errorHttpFail( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
			{
				url = "/v2/catalogs/blah/blah/blah/t2x",
				fail = {
					"Not found",
					headers = {
						status_code = '404',
					},
				},
				options = {
					requestCost = WFRxHttpClient.requestCostDownloadRendition,
					requestPriority = priority
				}
			},
		} )

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/rendition_type/thumbnail2x' ] = {
						href = '/v2/catalogs/blah/blah/blah/t2x',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectOnlyError(
		downloader:getRendition( '6f607906eefa4e9fb80da8fde2805c11', 'thumbnail2x', priority ),
		"Not found", ':ignore:', '/rels/rendition_type/thumbnail2x', '/v2/catalogs/blah/blah/blah/t2x', ':ignore:' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetProxy( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
			makeUrlResponse( "/v2/catalogs/blah/blah/proxy/blah/blah", "pseudo-dng-proxy-2560", nil, true ),
		} )

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/proxy_type/2560' ] = {
						href = '/v2/catalogs/blah/blah/proxy/blah/blah',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectSingleValue(

		downloader:getProxy( '6f607906eefa4e9fb80da8fde2805c11', '2560', priority )
			:map( mapKSDataToString ),

		'pseudo-dng-proxy-2560' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetMaster( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

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

	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v2/catalogs/blah/blah/master',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectSingleValue(

		downloader:getMaster( '6f607906eefa4e9fb80da8fde2805c11', priority )
			:map( mapKSDataToString ),

		'pseudo-master' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetMasterWithContentLengthFailure( rxTest )

	local failedResponse = makeUrlResponse( "/v2/catalogs/blah/blah/master", "pseudo-master", nil, true )
	failedResponse.headers[ 'Content-Length' ] = failedResponse.headers[ 'Content-Length' ] + 1
		-- pretend like there is suppose to be one more byte in the file to force a failure

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

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


	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v2/catalogs/blah/blah/master',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectOnlyError(
		downloader:getMaster( '6f607906eefa4e9fb80da8fde2805c11', priority ),

		"Error downloading file, dataLength doesn't match headers.Content-Length",
		':ignore:',
		'/rels/master',
		'/v2/catalogs/blah/blah/master',
		':ignore:' )

	rxTest:expectSingleValue(

		downloader:getMaster( '6f607906eefa4e9fb80da8fde2805c11' )
			:map( mapKSDataToString ),

		'pseudo-master' )

end

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

function WFCatBinaryDownloaderTest.rxTestGetXmp( rxTest )

	local catalogSpec = WFCatSpecMock( '98db98bfd554470dab2f862a4b853c40', kUserId, rxTest,

		{
			makeUrlResponse( "/v2/catalogs/blah/blah/xmp", "not-really-valid-xmp" ),
		} )
		
	local catalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	local downloader = WFCatBinaryDownloader( catalog )

	rxTest:expectSingleValue(

		catalog:docStore():setDocValue {
			_id = '6f607906eefa4e9fb80da8fde2805c11',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/xmp' ] = {
						href = '/v2/catalogs/blah/blah/xmp',
					},
				},
			},
		},

		':ignore:' )

	rxTest:expectSingleValue(

		downloader:getXmp( '6f607906eefa4e9fb80da8fde2805c11' )
			:map( mapKSDataToString ),

		'not-really-valid-xmp' )

end

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

local function setupCatalogAndDocStore( rxTest )
	
	local catalogTestSession = WFCatTestSession( rxTest )
	
	local catalog, catalogSpec, catalogId, docStore
	
	rxTest:waitFor(
	
		catalogTestSession:catalog()
			:tap( function( _catalog )
				catalog = _catalog
				catalogSpec = _catalog:ozCatalogSpec()
				catalogId = _catalog:ozCatalogSpec():catalogId()
				docStore = catalog:docStore()
			end ) )
	
	return catalogTestSession, catalog, catalogSpec, catalogId, docStore
	
end


local options = {

	continuousTaskDelays = {
		progressiveBackoff = false,
		noWorkDelay = 1,
		maxNoWorkDelay = 1,
		errorDelay = 1,
		maxDelay = 1,
	},
	
	isCatalogPuller = true
	
}

local uploaderTaskDelays = {
		progressiveBackoff = false,
		noWorkDelay = 1,
		maxNoWorkDelay = 1,
		errorDelay = 1,
		maxDelay = 1,
}

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

function WFCatBinaryDownloaderTest.rxTestDownloadViaRedirect_testNightly( rxTest )

	rxTest:setTimeLimit( 360 )
	
	local function mapHttpResponseForTestValidation( _, _, headers)
		return {
			tonumber( headers[ 'status_code' ] ),
			headers[ 'Server' ]
		}
	end
	
	-- Have a reference to catalogTestSession in case it will be GCed before the puller use it.
	local _, catalog, catalogSpec, catalogId = setupCatalogAndDocStore( rxTest )	
		
	local pusher = WFCatMetadataPusher( catalog, options )
	rxTest:addCloseTarget( pusher )

	local puller = WFCatMetadataPuller( catalog, "Test Puller", options )
	rxTest:addCloseTarget( puller )
	
	local uploader = WFCatBinaryUploader( catalog, nil, uploaderTaskDelays, catalogSpec, {
				clearFromRecentlyUploadedSeconds = 1,
				waitForOtherUploadSeconds = .5,
				waitForCopyFailedSeconds = .5,
				timer = function( seconds )
				       return Observable.timer( seconds, WFRxScheduler.sharedScheduler() )
				end,
			} )
	rxTest:addCloseTarget( uploader )

	uploader:DEBUG_resetDiagnosticLogErrors()
	
	rxTest:finally( function()
		puller:stopContinuousUpdates()
		pusher:stopContinuousUpdates()
		uploader:stopContinuousUploads()
	end )
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local revisionId = KSIdGenerator.uuidInLowercaseHex()
		
	local testDataPath = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'testdata' ), "import-samples" )
	local testDataFolder1 = KSPathUtils.child( testDataPath, "folder-1" )
	local originalFilePath = KSPathUtils.child( testDataFolder1, "IMG_7763.JPG" )
	local sha256 = require 'WFCrypto'.digestFile( originalFilePath, 'SHA256' )
	
	local assetDoc = WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath ) 	
	assetDoc = WFCatAssetTestUtils.addOriginalPathToDocument(assetDoc, sha256, originalFilePath)
	rxTest:waitFor( catalog:docStore():setDocValue( assetDoc ))

	-- start up sync engine

	rxTest:waitFor( pusher:sendUpdatesContinuously() )
	puller:getUpdatesContinuously()
	uploader:startContinuousUploads()

	local docObserver = rxTest:observe( catalog:docStore():ongoingDocValue( assetId )
		:map( WFOzDocUtils.isValidOriginalAvailableOnOz ) )
	
	docObserver:expectDataWithinTime(360, true)
	
	
	-- Completed catalog setup. Begin test scenarios

	local downloader = WFCatBinaryDownloader( catalog )
	
	rxTest:waitFor( catalog:ozUser():enableFeature( 'allow_direct_download_redirect' ) )
	rxTest:waitFor( catalog:ozUser():disableFeature( 'disallow_direct_download_redirect' ) )
	
	rxTest:expectSingleValue( 
		downloader:getMaster( assetId ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'AmazonS3' }
	)

	rxTest:expectSingleValue( 
		downloader:getMaster( assetId ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'AmazonS3' }
	)

	local origDestPath = 
			KSPathUtils.child( 
				KSPathUtils.getStandardFilePath( 'temp' ), 
				string.format( "orig-%s.jpg", KSIdGenerator.uuidInLowercaseHex() ) )

	rxTest:expectEqual( true, not WFFileUtils.exists( origDestPath ) )

	rxTest:expectSingleValue( 
		downloader:downloadOriginalToFileStream( assetId, nil, origDestPath ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'AmazonS3' }
	)

	rxTest:expectEqual( true, WFFileUtils.exists( origDestPath ) == "file" )

	rxTest:expectEqual( true, pcall( WFFileUtils.delete, origDestPath ) )
	
	rxTest:expectSingleValue( 
		downloader:getProxy( assetId, '2560' ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'AmazonS3' }
	)

	local proxyDestPath = 
			KSPathUtils.child( 
				KSPathUtils.getStandardFilePath( 'temp' ), 
				string.format( "proxy-%s.jpg", KSIdGenerator.uuidInLowercaseHex() ) )

	rxTest:expectEqual( true, not WFFileUtils.exists( proxyDestPath ) )

	rxTest:expectSingleValue( 
		downloader:downloadProxyToLocalFileStream( assetId, '2560', nil, proxyDestPath ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'AmazonS3' }
	)

	rxTest:expectEqual( true, WFFileUtils.exists( proxyDestPath ) == "file" )

	rxTest:expectEqual( true, pcall( WFFileUtils.delete, proxyDestPath ) )
	
	--------------------------------------------------------------------------------

	local useDirectDownload = false
	
	rxTest:expectSingleValue( 
		downloader:getMaster( assetId, WFRxHttpClient.requestPriorityHighest, useDirectDownload ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)
	
	rxTest:expectSingleValue( 
		downloader:getProxy( assetId, '2560', WFRxHttpClient.requestPriorityHighest, useDirectDownload ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)

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

	rxTest:expectSingleValue( 
		downloader:getRendition( assetId, 'thumbnail2x' ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)

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

	local renditionDestPath = 
			KSPathUtils.child( 
				KSPathUtils.getStandardFilePath( 'temp' ), 
				string.format( "rendition-%s.jpg", KSIdGenerator.uuidInLowercaseHex() ) )

	rxTest:expectEqual( true, not WFFileUtils.exists( renditionDestPath ) )

	rxTest:log( "Downloading rendition to " .. renditionDestPath )

	rxTest:expectSingleValue( 
		downloader:downloadRenditionToLocalFileStream( assetId, 'thumbnail2x', WFRxHttpClient.requestPriorityHighest, renditionDestPath ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)

	rxTest:log( "Finished downloading rendition to " .. renditionDestPath )

	rxTest:expectEqual( true, WFFileUtils.exists( renditionDestPath ) == "file" )

	rxTest:expectEqual( true, pcall( WFFileUtils.delete, renditionDestPath ) )

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

	rxTest:waitFor( catalog:ozUser():disableFeature( 'allow_direct_download_redirect' ) )
	rxTest:waitFor( catalog:ozUser():enableFeature( 'disallow_direct_download_redirect' ) )

	rxTest:expectSingleValue( 
		downloader:getMaster( assetId ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)
	
	rxTest:expectSingleValue( 
		downloader:getProxy( assetId, '2560' ) 
			:map( mapHttpResponseForTestValidation ),
		{ 200, 'openresty' }
	)
	
end

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

return WFRxTestCase.wrapSuite( 'WFCatBinaryDownloaderTest', WFCatBinaryDownloaderTest )
