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

WFCatAvatar.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2018 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains 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 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 Systems Incorporated.

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

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

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local WFFolderWalker = require "WFFolderWalker"
local WFAsyncFileUtils = require "WFAsyncFileUtils"

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

	-- WF.web
local KSJsonDecoder = require "KSJsonDecoder"

	-- WF.core.logging
local WFLogger = require 'WFLogger'

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

local logger = WFLogger 'WFCatAvatar'
local cachedAvatarUrl


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

local WFCatAvatar = {}

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

local function handleAvatarGetResponse( response, headers, wfCatUser,
										avatarDir )

	if headers.status_code and headers.status_code == "302" then
	
		local redirectUrl = headers.Location
		
		if redirectUrl then
		
			return wfCatUser:get( redirectUrl )
			
				:flatMap( function( redirectResponse, redirectHeaders )
					return handleAvatarGetResponse( redirectResponse,
								redirectHeaders, wfCatUser,
												avatarDir )
				end )
				
		end
		
	elseif response and headers.status_code
							and headers.status_code == "200" then
							
		local key = "Content-Type"
		local ext = headers[ key ] and headers[ key ]:gsub( "^.*/", "" )
		ext = ext or ""
		
		local uuid = KSIdGenerator.uuid()
		local avatarPathWithoutExtension = KSPathUtils.child( avatarDir, uuid )
		local avatarImagePath = string.format( "%s.%s",
									avatarPathWithoutExtension, ext:lower() )
		
		-- Remove old avatar image file(s). Shouldn't be more than one or two.
		local dir = avatarDir
		if dir then
		
			for filePath in WFFolderWalker( dir ) do
			
				if filePath ~= avatarImagePath then
					pcall( WFFileUtils.deleteFile, filePath )
				end
				
			end
			
		end

		return WFAsyncFileUtils.saveToFile( response, avatarImagePath )
		
			:map( function()
				return avatarImagePath
			end )
			
	end

end

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

-- If the avatar has recently changed, perhaps on another client, we'll likely
-- get a 302 redirect response when trying to download the avatar image.

local function downloadAvatarFromUrl_imp( wfCatUser, avatarDir, getUrl, recursionDepth )

	-- Arbitrary recursion depth limit. Probably won't ever see a 'recursionDepth'
	-- higher than 1.
	if recursionDepth > 10 then
		return Observable.throw( "too many redirects for avatar download" )
	end

	return wfCatUser:get( getUrl )
	
		:flatMap( function( response, headers )
		
			if response then
			
				return handleAvatarGetResponse( response, headers,
								wfCatUser, avatarDir )
								
			elseif headers and headers.status_code == "302" and headers.Location then
			
				return downloadAvatarFromUrl_imp( wfCatUser, avatarDir, headers.Location, recursionDepth + 1 )
				
			else
			
				return Observable.throw( "download failed" )
				
			end
			
		end )

end

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

local function downloadAvatarFromUrl( wfCatUser, avatarDir, getUrl )
	return downloadAvatarFromUrl_imp( wfCatUser, avatarDir, getUrl, 0 )
end

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

function WFCatAvatar.retrieveAvatar( wfCatUser, adobeId, avatarDir )

	local url = string.format( "https://cc-api-behance.adobe.io/v2/users/%s?api_key=Nimbus1",
						adobeId )
						
	return wfCatUser:get( url )
	
		:map( function( response, headers )
		
			logger:infof( "got Behance user info" )
			
			local decodedResponse = KSJsonDecoder.decodeWebSafeJson( response )
			
			if not decodedResponse then
				error( 'Unexpected response to avatar get request' )
			end
			
			local images = KSTableUtils.derefOpt( decodedResponse, "user", "images" )
			local avatarUrl
			
			if images then
				-- Find the biggest one.
				local biggest
				for size, url in pairs( images ) do
					if not biggest or tonumber( size ) > tonumber( biggest ) then
						biggest = size
						avatarUrl = url
					end
				end
			end

			-- download
														
			-- Download only if the URL is different. Re-launch will force
			-- download to happen, or if the avatar file dir is empty.
			local empty = true
			for file in WFFolderWalker( avatarDir ) do
				if WFFileUtils.exists( file ) ~= "directory" then
					empty = false
					break
				end
			end
			
			local getUrl
			
			if ( empty and avatarUrl ) or ( avatarUrl and avatarUrl ~= cachedAvatarUrl ) then
			
				cachedAvatarUrl = avatarUrl
				getUrl = string.format( "%s?api_key=Nimbus1", avatarUrl )
				
			end

			return getUrl

		end )
		
		:flatMap( function( getUrl )
		
			if not getUrl then
				-- Normal if we've already downloaded the avatar in this
				-- session. (May implement a polling approach at a later time.)
				return Observable.just( "no download attempt" )
			end
			
			return downloadAvatarFromUrl( wfCatUser, avatarDir, getUrl )
				
		end )
		
		:tapOnError( function( response, headers )
		
			logger:infof( "failed to get Behance user info" )
			
		end )

end

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

function WFCatAvatar.updateAvatar( wfCatUser, newAvatarPath, ext )

	local avatarAssetLocation, avatarKey
	
	local KSJsonDecoder = require "KSJsonDecoder"
	local KSData = require "KSData"
	local WFRxHttpClient = require "WFRxHttpClient"
	
	return wfCatUser:put( "https://cc-api-behance.adobe.io/v2/project/editor/auto_sign_request?api_key=Nimbus1",
		string.format( "extension=%s", ext:lower() ),
		{
			requestMethod = 'POST',
			requestHeaders = {
				{
					header = "Content-Type",
					value = "application/x-www-form-urlencoded",
				},
			}
		} )
					
		:flatMap( function( response, headers )
		
			-- Successfully generated a pre-signed URL for upload of the avatar asset.
			
			logger:infof( "Successfully generated pre-signed URL for avatar upload" )
			
			local decodedResponse = KSJsonDecoder.decodeWebSafeJson( response )
			
			if decodedResponse and decodedResponse.uri then
			
				logger:infof( "Got URI for avatar upload" )

				if decodedResponse.object then								
					avatarAssetLocation = decodedResponse.object.location
					avatarKey = decodedResponse.object.key
				end
				
				local avatarData = KSData.memoryMappedContentsOfFile( newAvatarPath )

				if avatarData then
					
					-- Use the pre-signed URL to upload the avatar asset.
					
					-- Since the URL is pre-signed, we can't use wfCatUser.put, as that would include
					-- the Authorization header, which would invalidate the request.
					
					local avatarString = tostring( avatarData )
					
					return WFRxHttpClient.put( decodedResponse.uri,
										avatarString,
										{ requestMethod = "PUT" } )
						:flatMap( function( response, headers )
							-- Successfully uploaded the avatar asset.
							
							-- Update the profile avatar with the uploaded asset.
							return wfCatUser:put( "https://cc-api-behance.adobe.io/v2/profile/avatar?api_key=Nimbus1",
											string.format( "source_url=%s", avatarAssetLocation ),
											{
												requestMethod = 'POST',
												requestHeaders = {
													{
														header = "Content-Type",
														value = "application/x-www-form-urlencoded",
													},
												},
											} )
								:tap( function( response, headers )
									logger:infof( "Avatar updated successfully" )
								end )
								:tapOnError( function( errorResponse, errorHeaders )
									logger:infof( "Avatar upload failed with %s", errorResponse )
								end )
						end )
						:tapOnError( function( errorResponse, errorHeaders )
							logger:infof( "Failed toupload the avatar image" )
						end )
				end
			end
		end )
		
		:tapOnError( function( response, headers )
			logger:infof( "Failed to generate pre-signed URL for avatar upload" )
		end )

end

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

return WFCatAvatar
