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

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

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

	-- RxLua
local Rx = require 'Rx'

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

	-- WF.core.lua.threading
local WFWorkerThread = require 'WFWorkerThread'

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

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


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

local check = assert( WFArguments.check )

local runUntilDone = WFRxRunUntilDone

local function nop() end

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

local logger = WFLogger 'WFOAuth2Session'
--	:setLevel( 'debug' )


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

-- @mark WFOAuth2Session class

--------------------------------------------------------------------------------
--[[!
	\class WFOAuth2Session

	TO DO: Write new documentation for this
]]--

local WFOAuth2Session = {

	_sessionImp = false,
		-- (WFWorkerThread) reference to the worker thread

	_sessionId = false,
		-- (string) client ID (typically Oz-style UUID)

	_hasValidLogin = false,
		-- (ReplayObservable< boolean > ) true if login is valid
		
	_hasValidLoginSubscription = false,
		-- (Subscription) holding the hasValidLogin subscription
		
	WFCloseSupport.addCloseSupportMethods,

}

--------------------------------------------------------------------------------
--[[!
	\brief Creates or restores an OAuth2 session for a client.

	If the client ID has been used previously within the system,
	the existing OAuth2 state is restored. If not, this call starts
	an initially invalid session. You must then call \c :storeTokens to
	provide the initial credentials.

	\param sessionId (string) The client ID. This is typically an Oz-style
		UUID, but that is not enforced here. It can be any non-empty string.
]]--

function WFOAuth2Session:__init( sessionId, optServerBase )

	check( 'WFOAuth2Session', '+string' )

	self._sessionId = sessionId

	logger:infof( "Created session ID %s via %s", sessionId, ':traceback:' )

	optServerBase = optServerBase or ''
	
	local sessionIdAndMaybeServerBase = sessionId .. ":::" .. optServerBase

	self:setOwned { _sessionImp = WFWorkerThread( "WFOAuth2SessionMessageProcessor.lua", sessionIdAndMaybeServerBase ) }

end

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

function WFOAuth2Session:close()

	if self._hasValidLoginSubscription then
		self._hasValidLoginSubscription:dispose()
		self._hasValidLoginSubscription = nil
	end
	
end

--------------------------------------------------------------------------------
--[[!
	\brief Assign new tokens to this OAuth2 session.

	Typically only done once at login time, though this is not enforced.

	\param accessToken (string)
	\param refreshToken (string)
	\param optDeviceToken (string)

	\return Observable< true > resolves when compelted
]]--

function WFOAuth2Session:storeTokens( accessToken, refreshToken, optDeviceToken )

	check( 'WFOAuth2Session', '+string', '+string', '?+string' )

	logger:infof( "oAuth2Session:storeTokens for session ID %s", self._sessionId )

	return self._sessionImp:request( 'storeTokens', accessToken, refreshToken, optDeviceToken )
		:call( runUntilDone )

end

--------------------------------------------------------------------------------
--[[!
	\brief Fetches the tokens for this OAuth2 session.

	\return Observable< accessToken (string), refreshToken (string), optDeviceToken (string) >
]]--

function WFOAuth2Session:fetchTokens()

	check( 'WFOAuth2Session' )

	logger:infof( "oAuth2Session:fetchTokens for session ID %s", self._sessionId )

	return self._sessionImp:request( 'fetchTokens' )

end

--------------------------------------------------------------------------------
--[[!
	\brief Performs a simple asynchronous HTTP GET request and signs the response
		using OAuth2.

	\param url (string) The URL for the request.
	\param options (optional, table) Options to modify the request.
	\param sequencing (optional, string) A string specifying the sequencing model
			Valid values include 'LIFO' and 'FIFO'. Defaults to FIFO if it doesn't
			find a match.

	\return <code>Observable< data, headers ></code> An observable that resolves
		to the response from HTTP server.

	\see WFRxHttpClient.get. The options are the same.
]]--

function WFOAuth2Session:get( url, options, sequencing )

	check( 'WFOAuth2Session', '+string', '?table', '?string' )

if url:match( '/v1%.0/' ) and url:match( 'photos%.adobe%.io' ) then
 logger:errorf( "OLD OLD OLD OLD OLD GET %s via %s", url, ':traceback:' )
 halt()
end

if url:match( '/v2/' ) and url:match( 'api%.ozstage%.com' ) then
 logger:errorf( "WRONG WRONG WRONG V2 GET %s via %s", url, ':traceback:' )
 halt()
end

	logger:infof( "GET URL %s\n\noptions %s\n\nvia %s", url, options, ':traceback:' )

	return self._sessionImp:request( 'get', url, options, sequencing )

end

--------------------------------------------------------------------------------
--[[!
	\brief Performs a simple asynchronous HTTP PUT request and signs the response
		using OAuth2.

	\param url (string) The URL for the request.
	\param data (\c KSData or string) data to send with the request. Note,
		a string may be interpreted as a file path if the dataFormat option
		is set to "filePath".
	\param options (optional, table) options to modify the request

	\return <code>Observable< data, headers ></code> An observable that resolves
		to the response from HTTP server.

	\see WFRxHttpClient.put. The options are the same.
]]--

function WFOAuth2Session:put( url, data, options )

	check( 'WFOAuth2Session', '+string', 'KSData|string|number', '?table' )

if url:match( '/v1%.0/' ) and url:match( 'photos%.adobe%.io' ) then
 logger:errorf( "OLD OLD OLD OLD OLD PUT %s via %s", url, ':traceback:' )
 halt()
end

if url:match( '/v2/' ) and url:match( 'api%.ozstage%.com' ) then
 logger:errorf( "WRONG WRONG WRONG V2 PUT %s via %s", url, ':traceback:' )
 halt()
end

	logger:infof( "PUT URL %s\n\noptions %s\n\nvia %s", url, options, ':traceback:' )

	return self._sessionImp:request( 'put', url, data, options )

end

--------------------------------------------------------------------------------
--[[!
	\brief Reports whether the OAuth2 session has a valid login at the moment.

	\return <code>Observable< boolean ></code> \c true whenever we have a valid
		refresh token. When it changes to \c false, the user has been forcibly
		logged out.
]]--

function WFOAuth2Session:hasValidLogin()

	check( 'WFOAuth2Session' )

	if not self._hasValidLogin then
		self._hasValidLogin = self._sessionImp:request( 'hasValidLogin' )
		self._hasValidLoginSubscription = self._hasValidLogin:subscribe( nil, nop, nop )
	end

	return self._hasValidLogin

end

--------------------------------------------------------------------------------
--[[!
	\brief Logs the user out. Deletes all store tokens and pretend this session
	never existed.

	\return <code>Observable< true ></code> Resolves when done. You can ignore
		this Observable.
]]--

function WFOAuth2Session:logout()

	check( 'WFOAuth2Session' )

	logger:info( "Logout via %s", ':traceback:' )

	return self._sessionImp:request( 'logout' )

end

--------------------------------------------------------------------------------
--[[!
	\brief Simulates forced logout. Should only be used in testing.

	\return <code>Observable< true ></code> Resolves when done. You can ignore
		this Observable.
]]--

function WFOAuth2Session:simulateForcedLogout()

	check( 'WFOAuth2Session' )

	logger:errorf( "Simulate forced logout via %s", ':traceback:' )

	return self._sessionImp:request( 'simulateForcedLogout' )
		:call( runUntilDone )

end

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

-- @mark miscellaneous

--------------------------------------------------------------------------------
--[[!
	\brief Retrieves this document store as a string.

	\return (string) The string representation.
]]--

function WFOAuth2Session:__tostring()

	check( 'WFOAuth2Session' )

	return string.format( "WFOAuth2Session( %s )", self._sessionId )

end

--------------------------------------------------------------------------------
--[[!
	\brief (Intended for debugging use only) Generates code coverage statistics
		for the worker thread that implemented this doc store.

	\warning This function should only be used by WFOAuth2SessionTest.lua.

	\return <code>Observable< empty ></code> Completes when coverage data has been
		generated. (You may safely ignore this Observable.)
]]--

function WFOAuth2Session:DEBUG_dumpCoverage( path )

	check( 'WFOAuth2Session', '+string' )

	return self._sessionImp:request( 'DEBUG_dumpCoverage', path )
		:call( runUntilDone )

end

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

return Rx.Class 'WFOAuth2Session'( WFOAuth2Session )
