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

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.

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

local WFPlatformInfo = require 'WFPlatformInfo'

if WFPlatformInfo.isDesktop then
	return {} -- not used by Desktop
end

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


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

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

	-- WF.core.os
local WFFileUtils = require 'WFFileUtils'
local WFProgressReporter = require 'WFProgressReporter'

--local KSDate = require 'KSDate'
	-- WF.oz_clients.utils
local OzFilePaths = require "OzFilePaths"

	-- WF.core.table
--local WFTableArchiving = require 'WFTableArchiving'

	-- WF.oz_client.managed_catalog
local WFCatBinaryUploader = require 'WFCatBinaryUploader'
local WFCatFileSpec = require 'WFCatFileSpec'
local WFCatMetadataPuller = require 'WFCatMetadataPuller'
local WFCatMetadataPusher = require 'WFCatMetadataPusher'
local WFCatTestSession = require 'WFCatTestSession'
local WFCatalog = require 'WFCatalog'

local check = assert( WFArguments.check )

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

local WFCatLrmBinaryUploaderTest = {}

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

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

local wfCatalog, catalogId, catalogPath, testDataPath, testDataTemplate

local testData = {}

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

local kImageNonChunked = 'image-non-chunked'
local kImageChunked = 'image-chunked'
local kImagePano = 'image-pano'
local kVideoNonChunked = 'video-non-chunked'
local kVideoChunked = 'video-chunked'
local kXmpDevelop = 'xmp-develop'

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

function WFCatLrmBinaryUploaderTest.setUpClass()

	testDataPath = KSPathUtils.getStandardFilePath( "testdata" )
	testDataPath = KSPathUtils.child( testDataPath, 'assets' )

end

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

function WFCatLrmBinaryUploaderTest.setUp()
	
	testDataTemplate = {
		[ kImageNonChunked ] = KSPathUtils.child( testDataPath, '1.5MB.jpg' ),
		[ kImageChunked ] = KSPathUtils.child( testDataPath, '12MB.jpg' ),
		[ kVideoChunked ] = KSPathUtils.child( testDataPath, '32MB.mov' ),
		[ kVideoNonChunked ] = KSPathUtils.child( testDataPath, '4.3MB.mov' ),
		[ kXmpDevelop ] = KSPathUtils.child( testDataPath, 'xmp_0.5MB.rdf' ),
		[ kImagePano ] = KSPathUtils.child( testDataPath, 'SonyNEX-5_Panorama.JPG' ),
	}

end

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

function WFCatLrmBinaryUploaderTest.copyTestFile( FileIdx )

	local relPath = 'Originals/test'

	local path = testDataTemplate[ FileIdx ]
	local fileName = KSPathUtils.leafName( path )
	local dest = KSPathUtils.child( catalogPath, relPath )

	WFFileUtils.mkdir( dest )
	
	dest = KSPathUtils.child( dest, fileName )
	
	if not WFFileUtils.exists( dest ) then
		WFFileUtils.copy( path, dest )
	end
	
	testData[ FileIdx ] = KSPathUtils.child( relPath, fileName )

end

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

function WFCatLrmBinaryUploaderTest.tearDown()
	
	wfCatalog:close()
	
	WFFileUtils.delete( KSPathUtils.child( catalogPath, 'Originals/test' ) )
	
	catalogPath = nil
	catalogId = nil
	
end

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

local options = {
	continuousTaskDelays = continuousTaskDelays,
	isCatalogPuller = true,
}

local function removeEmptyErrorHeader( errorMessage )

	return string.gsub( errorMessage, '<h4>(.[^<]+)</h4><ul></ul>', '' )

end

local function remove60SecondsWarning( errorMessage )

	return string.gsub( errorMessage, "<li>(.[^<]+)Doc store hasn't updated with the presence of uploaded(.[^<]+)</li>", '' )

end

local function removeGetCleanOzAccountError( errorMessage )

	-- Remove intentionally generated error related to getCleanOzAccount
	return string.gsub( errorMessage, '<li>(.[^<]+)getCleanOzAccount(.[^<]+)</li>', '' )

end

local function getDiagnosticLogErrorsIgnoringAccountError( uploader )

	-- Get errors encountered from diagnostic log, ignore account error.
	local diagnosticLogErrors = removeGetCleanOzAccountError( uploader:DEBUG_getDiagnosticLogErrors() )
	return remove60SecondsWarning( diagnosticLogErrors )
end

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

local function pollBinariesInFlight( uploader )

	return Observable.timer( .1, 1, WFRxScheduler.sharedScheduler() )
	:map( function()
		return uploader._numberOfScheduledBinaryUploads
	end )

end
------------------------------------------------------------------------

local function performUploadTest( rxTest, fileId )

	rxTest:setTimeLimit( 360 )

	local catalogIndexDefinitions = 'LrmCatalogIndexDefinitions.lua'

	catalogId = KSIdGenerator.uuidInLowercaseHex()

	local catalogTestSession = WFCatTestSession( rxTest )

	local catalogOptions = {
		indexSpecScript = catalogIndexDefinitions,
		setupConflictResolver = false,
		docStorePath = ':memory:',
		noFileAccessDb = true,
		noCatalogSearch = true,
	}

	local catalogSpec

	rxTest:expectSingleValue( catalogTestSession:catalogSpec()
			:map( function( _catalogSpec )
					check( 'WFCatSpec' )
					catalogSpec = _catalogSpec
					return _catalogSpec
			end ),

	':ignore:' )

	catalogPath = OzFilePaths.getCatalogBasePath( catalogSpec._catalogId )

	wfCatalog = WFCatalog( catalogSpec, catalogPath, catalogOptions )
		rxTest:finally( function()
			wfCatalog:close()
		end )

	local pusher = WFCatMetadataPusher( wfCatalog, options )
		rxTest:addCloseTarget( pusher )

	local puller = WFCatMetadataPuller( wfCatalog, "Test Puller", options )
		rxTest:addCloseTarget( puller )

	local uploadOptions = {
		progressReporter = WFProgressReporter( "Binary Upload Progress Report" ),
	}

	local uploader = WFCatBinaryUploader( wfCatalog, nil, continuousTaskDelays, catalogSpec, uploadOptions )
		rxTest:addCloseTarget( uploader )

	uploader:DEBUG_resetDiagnosticLogErrors()

	rxTest:finally( function()
		puller:stopContinuousUpdates()
		pusher:stopContinuousUpdates()
		uploader:stopContinuousUploads()
		uploadOptions.progressReporter = nil
		end )

	WFCatLrmBinaryUploaderTest.copyTestFile( fileId )

	local localPath = testData[ fileId ]

	local fileAttributes = WFFileUtils.fileAttributes( KSPathUtils.child( catalogPath, localPath ) )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local docStore = wfCatalog:docStore()

	local fileName = KSPathUtils.leafName( localPath )

	localPath = KSPathUtils.child( catalogPath, localPath )
	
	local localOnly
	if WFPlatformInfo.isiOS then
		localOnly = {
			files = {
			master = {
				path = localPath
			}
		  }   
		}
	else
		localOnly = {	
			originalFilePath = localPath,
			latestOriginal = {
				filePath = localPath
			},
			original = {
				epoch = 1,
				size = fileAttributes.fileSize
			}
		}
	end
			
	
	rxTest:expectSingleValue( docStore:applyOverlay {
		_id = assetId,
		type = 'asset',
		importSource = {
			contentType = 'image/jpeg',
			fileName = fileName,
			fileSize = fileAttributes.fileSize,
			sha256 = WFCrypto.digestFile(localPath, 'SHA256'),
			importTimestamp = "2018-09-12T15:33:19Z",
			originalHeight = 3840,
			originalWidth = 5760,
			importedBy = "d220fabbdac80b98588dd19136554396",
		},
		captureDate = "2018-09-12T14:33:19Z",
		subtype = 'image',
		_localOnly = localOnly,
	}, ':ignore:' )

	pusher:sendUpdatesContinuously()
	puller:getUpdatesContinuously()

	local uploadProgressStatus = rxTest:observe( uploadOptions.progressReporter:status() )

	uploader:startContinuousUploads()

	-- Verify the successfully uploaded doc, it will have an original link.

	local observeBinariesInFlight = rxTest:observe( pollBinariesInFlight( uploader ) )
	observeBinariesInFlight:expectDataWithinTime( 10, 0 )
	
	rxTest:observe( docStore:ongoingDocValue( assetId )
		:map( function( docValue )
			return WFCatFileSpec.getOriginalLinkFromDocValue( docValue ) ~= nil
		end )
	):expectDataWithinTime( 240, true )

	-- Make sure we didn't run into any unexpected errors.
	local errorsRecordedInDiagnosticLog = getDiagnosticLogErrorsIgnoringAccountError( uploader )
	rxTest:expectEqual( "", removeEmptyErrorHeader( errorsRecordedInDiagnosticLog ) )

	if fileId == kImageNonChunked then

		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 0, -- initial
			totalBytesUploadedSoFar = 0,
		} )
		
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 1550921, -- after importing asset
			totalBytesUploadedSoFar = 0,
		} )
	
		-- Verify progress report being sent when 100% upload reached ( even for file size < 5MB )
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 1550921, 
			totalBytesUploadedSoFar = 1550921, -- as less than 5MB whole file uploaded in one shot
		} )

		-- Verify progress report reset the counters after final disposal.
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 0,
			totalBytesUploadedSoFar = 0,
		} )

	elseif kImageChunked then

		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 0, -- initial
			totalBytesUploadedSoFar = 0,
		} )
		
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 12089018, -- after importing asset
			totalBytesUploadedSoFar = 0,
		} )

		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 12089018,
			totalBytesUploadedSoFar = 5242880, -- chunk1
		} )

		uploadProgressStatus:expectDataWithinTime(240, ':ignore:' )
		--{

			--description = "Binary Upload Progress Report",
			--totalBytesNeedToUpload = 12089018,
			-- This may vary per test run as the upload happens parallel and when chunks can't be sliced exactlly
			-- equal, it is hard to predict which piece gets uploaded first.
			-- Hence for testing purpose comparision of intermediate results ignored here.
			 -- totalBytesUploadedSoFar = 6846138 or 10485760, -- chunk1+chunk2
		--} )

		-- Verify progress report being sent when 100% upload reached.
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 12089018,
			totalBytesUploadedSoFar = 12089018, -- chunk1 + chunk2 + rest
		} )
		
		-- Verify progress report reset the counters after final disposal.
		uploadProgressStatus:expectDataWithinTime(240,
		{
			description = "Binary Upload Progress Report",
			totalBytesNeedToUpload = 0,
			totalBytesUploadedSoFar = 0,
		} )

	end
	
	puller:stopContinuousUpdates()
	pusher:stopContinuousUpdates()
	uploader:stopContinuousUploads()
end

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

function WFCatLrmBinaryUploaderTest.rxTestMobileNonChunkedUpload_testNightly( rxTest )
 
	performUploadTest(rxTest, kImageNonChunked)

end

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

function WFCatLrmBinaryUploaderTest.rxTestMobileChunkedUpload_testNightly( rxTest )
 
	performUploadTest(rxTest, kImageChunked)

end

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

return WFRxTestCase.wrapSuite( WFCatLrmBinaryUploaderTest )

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