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

WFTokenTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2011 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 WFTimer = require 'WFTimer'

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

	-- WF.core.reactive
local KSPromises = require 'KSPromises'

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

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

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

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

	-- WF.oz_client.user
local WFOzUserTestSession = require 'WFOzUserTestSession'

	-- WF.web.json
local KSJsonDecoder = require 'KSJsonDecoder'

	-- WF.web.oauth2_client
local WFOAuth2Session = require 'WFOAuth2Session'


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

local WFTokenTest = {}

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

local TEST_WAIT_TIME = 300

local ozUserId
local oAuthSession
local setupP
local accessToken
local refreshToken
local origClientId
local origClientSecret

local userTestSession

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

local function cleanupAfterTestP()

	-- Restore standard client ID and secret.

	WFImsAppIdentity.setClientIdAndSecret( origClientId, origClientSecret )

	-- Release test user.

	if userTestSession then

		return userTestSession:releaseUser()
			:call( WFRxPromiseBridge )

	else

		return KSPromises.resolvedPromiseP( 'data', true )

	end

end

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

function WFTokenTest.setUp()
	
	origClientId, origClientSecret = WFImsAppIdentity.getClientIdAndSecret()
	WFImsAppIdentity.setClientIdAndSecret( 'WFClientTest1FastExpire',
										   '85d94a95-a2cd-4537-af0c-824f16c349fe' )
	
	-- Perform the necessary setup for using device tokens.
	
	local wfDeviceId = WFImsAppIdentity.generateDeviceId()
	WFImsAppIdentity.setImsDeviceId( wfDeviceId )
	
	userTestSession = WFOzUserTestSession( 'WFTokenTest' )

	setupP = userTestSession:getCleanOzAccount()

		:tap( function( _ozUserId )

			ozUserId = _ozUserId
			oAuthSession = WFOAuth2Session( ozUserId )

			accessToken, refreshToken = userTestSession:accessAndRefreshTokens()
				-- TODO [scouten 2015-11-23]: We don't have device token.
				-- Does this matter?

		end )

		:call( WFRxPromiseBridge )

		:runUntilDoneP()

end

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

local function doSomethingWithOzAndReturnTokensP()
	
	return oAuthSession:get( OzCatalogUrls.baseServerUrl
					.. '/v1.0/accounts/00000000000000000000000000000000' )
	
		:call( WFRxPromiseBridge )
	
		:mapDataP( function( ... )
	
			return KSKeychainService.readP( ozUserId )
		
		end )

		:mapDataP( function( status, data )
							
			local decodedJson = KSJsonDecoder.decodeWebSafeJson_stripNull( data )
			
			if decodedJson and decodedJson.access_token and decodedJson.refresh_token then
				return 'data', decodedJson.access_token, decodedJson.refresh_token
			else
				return 'error', "No access token or refresh token in keychain."
			end
			
		end )

		:runUntilDoneP()

end

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

-- @mark Begin tests

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

function WFTokenTest.ROUTINELY_FAILING_testDeviceTokenRefreshCycles_testNightly( test )

	local ACCESS_TOKEN_LIFETIME = 10
	local REFRESH_TOKEN_LIFETIME = 25 - ACCESS_TOKEN_LIFETIME
	local DEVICE_TOKEN_LIFETIME = 60 - REFRESH_TOKEN_LIFETIME - ACCESS_TOKEN_LIFETIME
	local TOKEN_BUFFER = 1

	local STEP_1_TIME_ESTIMATE = 2

	WFAsyncTestCase( test, TEST_WAIT_TIME )

		:waitFor( setupP )
				
		:waitFor( function()

			 return doSomethingWithOzAndReturnTokensP()

		end )
		:whenResolvedDo( function( status, access_token, refresh_token )

			assert( status == 'data' )
			assert( access_token == accessToken, "Access token does not match!" )
			assert( refresh_token == refreshToken, "Resfresh token does not match!" )
			
		end )

		:waitFor( function()
		
			return WFTimer.afterDelayP( ACCESS_TOKEN_LIFETIME + TOKEN_BUFFER - STEP_1_TIME_ESTIMATE )
			:mapDataP( function()
			
				return doSomethingWithOzAndReturnTokensP()
		
			end )
			
		end )
		:whenResolvedDo( function( status, access_token, refresh_token )
			
			assert( status == 'data' )
			assert( access_token ~= accessToken, "The access token should have been refreshed." )
			assert( refresh_token == refreshToken, "The refresh token should not have been refreshed." )
			accessToken = access_token
			
		end )
		
		:waitFor( function()
		
			return WFTimer.afterDelayP( REFRESH_TOKEN_LIFETIME + TOKEN_BUFFER )
			:mapDataP( function()
			
				return doSomethingWithOzAndReturnTokensP()
				
			end )
			
		end )
		:whenResolvedDo( function( status, access_token, refresh_token )
			
			assert( status == 'data', "Expected 'data' but got '" .. status .. "': " .. tostring( access_token ) )
			assert( access_token ~= accessToken, "The access token should have been refreshed." )
			assert( refresh_token ~= refreshToken, "The refresh token should have been refreshed." )
			accessToken = access_token
			refreshToken = refresh_token
			
		end )
		
		:waitFor( function()
		
			return WFTimer.afterDelayP( DEVICE_TOKEN_LIFETIME + TOKEN_BUFFER )
			:mapDataP( function()
			
				return doSomethingWithOzAndReturnTokensP()
		
			end )
			
		end )
		:whenResolvedDo( function( status, data )
			
			assert( status == 'error',
				"We should no longer be able to communicate with Oz since all of our tokens have expired." )
		
		end )
		
		:waitFor( function()
			return cleanupAfterTestP()
		end )

end

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

return WFTokenTest
