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

WFOzClient.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.

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

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

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

	-- WF.core.os
local WFLocalizationUtils = require 'WFLocalizationUtils'

	-- WF.core.utils
local WFArguments = require 'WFArguments'
local KSJsonDecoder = require 'KSJsonDecoder'

	-- WF.oz_client.catalog
local OzCatalogUrls = require 'OzCatalogUrls'

	-- WF.oz_client.ims
local WFImsAppIdentity = require 'WFImsAppIdentity'

	-- WF.web.http_client
local WFRxHttpClient = require 'WFRxHttpClient'

	-- WF.web.utils
local WFURI = require 'WFURI'


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

local check = assert( WFArguments.check )

local logger = WFLogger 'WFOzClient'
--	:setLevel 'debug'


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

local WFOzClient = {}

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

-- [bzibble 2014-06-13]: fix #3772741: when the app language is set to automatic,
-- we need to translate the language code that we get from Mac OS to make it match
-- the format expected by IMS.

local function sanitizeLanguageCode( code )

	local replacements = {
		[ "zh-Hans" ] = "zh_cn", -- Chinese simplified
		[ "zh-Hant" ] = "zh_tw", -- Chinese traditional
	}

	return replacements[ code ] or code

end

--------------------------------------------------------------------------------
--[[!
	\brief Retrieves the authorization URL for IMS.

	\param optOverrides a table of options that override system defaults.

	If provided, this table should include the following keys:

		- \c baseServerUrl to be used in lieu of OzCatalogUrls.baseServerUrl
		- \c languageCode to be used in lieu of WFLocalizationUtils.getLanguageCode()
		- \c regionCode to be used in lieu of WFLocalizationUtils.getRegionCode()
		- \c systemUserName and \c macAddress: if present, these will drive the
			creation of an IMS device_id; if not, the corresponding APIs in WFDeviceInfo
			will be used (but they may not be completely implemented on all platforms)
		- \c queryParamOverrides if present, a set of arguments to use in the construction
			of the IMS authorization query path (these override anything otherwise derived
			by this function)

	\return <code>Observable < string ></code> resolves to the authorization URL
]]--

function WFOzClient.getImsAuthUrl( optOverrides )

	check( '?table' )

	optOverrides = optOverrides or {}

	local apiVersion = optOverrides.apiVersion or 'v2'

	logger:infof( "WFOzClient.getImsAuthUrl %s\n\ncalled via %s\n\n",
				  optOverrides, ':traceback:' )

	local baseServerUrl = optOverrides.baseServerUrl or OzCatalogUrls.baseServerUrl

	local imsClientId = WFImsAppIdentity.getClientId()

	local url = WFURI.resolve( baseServerUrl, string.format( '/%s/accounts/00000000000000000000000000000000',
															 apiVersion ) )

	local options = {
		returnHttpErrorsAsData = true,
		requestHeaders = {
			{ header = 'Authorization', value = 'Bearer' },
				-- intentionally empty bearer token; this prompts Oz to tell us how to auth
			{ header = 'X-Api-Key', value = imsClientId },
		}
	}

	local wfDeviceId

	if optOverrides.systemUserName and optOverrides.macAddress then

		wfDeviceId = WFImsAppIdentity.generateDeviceId {
			systemUserName = optOverrides.systemUserName,
			macAddress = optOverrides.macAddress,
		}

		WFImsAppIdentity.setImsDeviceId( wfDeviceId )

	end

	return WFRxHttpClient.get( url, options )

		:map( function( data, headers )

			local authHeader = headers[ 'Www-Authenticate' ]
							or headers[ 'WWW-Authenticate' ]

				-- This is really WWW-Authenticate, but somewhere in the Mac and iOS
				-- client libs it seems to be switching the case on us. :-(
				-- Update (rseeler): Android returns "WWW-Authenticate", so I'm
				-- checking both.

			local authUrl = authHeader and authHeader:match( [[auth_url="([^"]+)"]] )

			if type( authUrl ) ~= 'string' or #authUrl == 0 then
				logger:errorf( "Did not find WWW-Authenticate header in this response\n\n"
					.. "URL = %s\n\nData = %s\n\nHeaders = %s\n\n",
					url, data, headers )
				error( "WWW-Authenticate header missing or auth_url field missing" )
			end


			local clientId = WFImsAppIdentity.getClientId()

			authUrl = authUrl:replace( 'CarouselTest1', clientId )
			authUrl = authUrl:replace( 'LightroomTest1', clientId )

			local parsedUrl = WFURI.parse( authUrl )
			local queryOpts = WFURI.parseQueryString( parsedUrl.query or "" )

			local languageCode = sanitizeLanguageCode( optOverrides.languageCode
									or WFLocalizationUtils.getLanguageCode() )
			local regionCode = optOverrides.regionCode
									or WFLocalizationUtils.getRegionCode()

			-- [immisch 2020-05-27]: regionCode can be nil, also https://jira.corp.adobe.com/browse/WF-1145 
			if languageCode and regionCode then
				queryOpts.locale = languageCode .. '_' .. regionCode
			else
				queryOpts.locale = languageCode 
			end

			queryOpts.device_id = wfDeviceId

			if optOverrides.queryParamOverrides then
				for k, v in pairs( optOverrides.queryParamOverrides ) do
					queryOpts[ k ] = v
				end
			end

			authUrl = WFURI.buildUri
			{
				scheme = parsedUrl.scheme,
				authority = parsedUrl.authority,
				path = parsedUrl.path,
				query = WFURI.buildQueryString( queryOpts ),
			}

			local imsBaseUrl = WFURI.buildUri {
				scheme = parsedUrl.scheme,
				authority = parsedUrl.authority,
				path = parsedUrl.path:gsub( "/authorize$", "" ),
				-- intentionally omit query
			}

			return authUrl, imsBaseUrl

		end )

end

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

function WFOzClient.getJumpUrlForUrl( url, targetClientId, accessToken, optOverrides )

	if not targetClientId or not accessToken then
		return Observable.just( url )
	end

	return WFOzClient.getImsAuthUrl( optOverrides )
					
		:flatMap( function( _, imsBaseUrl )

			local params = {
				target_client_id = targetClientId,
				target_redirect_uri = url,
				bearer_token = accessToken
			}

			local requestUrl = string.format( '%s/%s', imsBaseUrl, 'jumptoken/v1' )

			local paramsStr = require 'WFURI'.buildQueryString( params )

			local putOptions = {
				requestMethod = 'POST',
				requestHeaders = {
					{
						header = 'Content-Type',
						value = 'application/x-www-form-urlencoded',
					},
				},
			}

			return WFRxHttpClient.put( requestUrl, paramsStr, putOptions )
					
		end )
		
		:map( function( rawJson )

			local _, jsonResult = pcall( KSJsonDecoder.decodeWebSafeJson, rawJson, true )

			if type( jsonResult ) ~= 'table' then
				jsonResult = {}
			end

			if jsonResult.jump then
				return jsonResult.jump
			end

			return url
		
		end )
		
		:catch( function( errorMessage )
		
			logger:errorf( "getJumpUrlForUrl failed with error: %s", errorMessage )
			return Observable.just( url )
		
		end )

end

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

return WFOzClient
