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

WFCatOzMaintenanceTrackerTestFactory.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2020 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.

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

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

	-- com.adobe.wichitafoundation.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local WFModelsUtils = require 'WFModelsUtils'
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.oz_client.managed_catalog
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatSpecMock = require 'WFCatSpecMock'

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

local kCatalogId = KSIdGenerator.uuidInLowercaseHex()
local kUserId = KSIdGenerator.uuidInLowercaseHex()

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

return function( optSubmodelsByImplementation, maybeExistingTestClass )

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

	local function callFunctionOrSubmodel( rxTest, wfCatalog, fnNamespace, fnName, ... )
		
		if optSubmodelsByImplementation then
		
			return WFModelsUtils.callFunctionOrSubmodel( rxTest, optSubmodelsByImplementation, wfCatalog, fnNamespace, fnName, nil, ... )
		
		end
		
		return WFModelsUtils.callFunctionOrSubmodel( rxTest, optSubmodelsByImplementation, wfCatalog, fnNamespace, fnName, ... )
					
	end
	
	----------------------------------------------------------------------------

	local WFCatOzMaintenanceTrackerTestSuite = maybeExistingTestClass or {}

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

	local maintenanceError = {
		status_code = '503',
		[ 'X-Result-Code' ] = 'MaintenanceError'
	}

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

	local function createCatalogSpecForTest( rxTest )

		local expectedResponses = {
			{
				url = string.format( "/v2/catalogs/%s/updates", kCatalogId ),

				fail = {
					"Uh oh",
					maintenanceError
				}

			},
			{
				url = string.format( "/v2/accounts/%s", kUserId ),

				fail = {
					string.format( [[ {
						"base": "https://photos.adobe.io",
						"updates": {
							"id": "8462f5ba3b39fd050d3cd6b7352ba561",
							"created": "2015-05-27T00:34:56.669987Z",
							"updated": "2015-05-27T00:38:29.160369Z",
							"type": "account",
							"revision_ids": [
								"a1e880f718a348b89663cf38f9f29d23",
								"cd1ab30cf8464ef2a9e736b50c0ba33d",
								"96f33a7215e04c9aa49346bbf8aae921",
								"813fdbda1b244c2d9f41008ba63eedba"
							],
							"payload": {
								"hello": "hi"
							}
						}
					} ]], kCatalogId ),
					maintenanceError
				},

				options = {
					bypassRateLimiter = true,
				}

			},
			{
				url = string.format( "/v2/accounts/%s", kUserId ),

				response = string.format( 
					[[ {
						"base": "https://photos.adobe.io",
						"updates": {
							"id": "8462f5ba3b39fd050d3cd6b7352ba561",
							"created": "2015-05-27T00:34:56.669987Z",
							"updated": "2015-05-27T00:38:29.160369Z",
							"type": "account",
							"revision_ids": [
								"a1e880f718a348b89663cf38f9f29d23",
								"cd1ab30cf8464ef2a9e736b50c0ba33d",
								"96f33a7215e04c9aa49346bbf8aae921",
								"813fdbda1b244c2d9f41008ba63eedba"
							],
							"payload": {
								"hello": "hi"
							}
						}
					}]], kCatalogId ),

				headers = {
					status_code = '200',
				},

				options = {
					bypassRateLimiter = true,
				}

			}

		}
		
		local catalogSpec = WFCatSpecMock( kCatalogId, kUserId, rxTest, expectedResponses )
		
		return catalogSpec

	end

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

	local function createWFCatalogForTestWithSpec( rxTest, wfCatalogSpec )

		local indexSpecScript

		if WFPlatformInfo.isMobile() then
			indexSpecScript = 'LrmCatalogIndexDefinitions.lua'
		end

		return WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, wfCatalogSpec, nil, indexSpecScript )

	end

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

	function WFCatOzMaintenanceTrackerTestSuite.rxTestOzMaintenanceNotificationStream( rxTest )

		rxTest:setTimeLimit( 100 )

		-- The following wfCatalogSpec expects 3 HTTP requests:
		--
		-- The first request is a request to the catalog update feed. This will return a 503
		-- which indicates that the account may be in maintenance mode.
		--
		-- The second request is a request to the Oz account API. This will also return the 503,
		-- which verifies the account is under maintenance.
		--
		-- The final request is a request to the Oz account API that resolves successfully. This
		-- indicates that the user's account is no longer under maintenance.
		
		local wfCatalogSpec = createCatalogSpecForTest( rxTest )

		local wfCatalog = createWFCatalogForTestWithSpec( rxTest, wfCatalogSpec )

		local optArgs = {
			httpErrorStream = wfCatalogSpec:watchHttpErrors()
		}

		local ozMaintenanceStreamOrSubmodel = 
				callFunctionOrSubmodel( 
					rxTest, wfCatalog, 
					'WFCatOzMaintenanceTracker',
					'getOzMaintenanceNotificationStream', 
					wfCatalog, optArgs )

		local ozMaintenanceStreamObserver = rxTest:observe( ozMaintenanceStreamOrSubmodel
			:map( function( ozMaintenanceInfo )
			
				if ozMaintenanceInfo.accountAndErrorInfo then
					ozMaintenanceInfo.accountAndErrorInfo = '<account_info>'
				end

				return ozMaintenanceInfo
			
			end ) )

		ozMaintenanceStreamObserver:expectDataWithinTime( 5, { 
				id = 'catalog', 
				isUnderMaintenance = false
			} )

		-- Trigger the first HTTP request. This should cause the WFCatOzMaintenanceTracker
		-- to start polling the user account document.
		
		local catalogUrl = string.format( "/v2/catalogs/%s/updates", kCatalogId )
		
		rxTest:expectOnlyError( wfCatalogSpec:get( catalogUrl ), "Uh oh", maintenanceError )

		-- Verify that the maintenance stream detected the outage.
		
		ozMaintenanceStreamObserver:expectDataWithinTime( 5, {
				id = 'catalog',
				isUnderMaintenance = true,
				accountAndErrorInfo = '<account_info>'
			} )

		-- Verify the the maintenance stream detected when the outage ended.
		
		ozMaintenanceStreamObserver:expectDataWithinTime( 5, { 
				id = 'catalog', 
				isUnderMaintenance = false
			} )
		
		ozMaintenanceStreamObserver:dispose()
		
	end

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

	function WFCatOzMaintenanceTrackerTestSuite.rxTestOzMaintenanceNotificationStream_Non503( rxTest )

		rxTest:setTimeLimit( 100 )

		local expectedResponses = {
			{
				url = string.format( "/v2/catalogs/%s/updates", kCatalogId ),

				fail = {
					"Uh oh",
					maintenanceError
				}

			},
			{
				url = string.format( "/v2/accounts/%s", kUserId ),

				response = string.format( 
					[[ {
						"base": "https://photos.adobe.io",
						"updates": {
							"id": "8462f5ba3b39fd050d3cd6b7352ba561",
							"created": "2015-05-27T00:34:56.669987Z",
							"updated": "2015-05-27T00:38:29.160369Z",
							"type": "account",
							"revision_ids": [
								"a1e880f718a348b89663cf38f9f29d23",
								"cd1ab30cf8464ef2a9e736b50c0ba33d",
								"96f33a7215e04c9aa49346bbf8aae921",
								"813fdbda1b244c2d9f41008ba63eedba"
							],
							"payload": {
								"hello": "hi"
							}
						}
					}]], kCatalogId ),

				headers = {
					status_code = '200',
				},

				options = {
					bypassRateLimiter = true,
				}

			}

		}
		
		local catalogSpec = WFCatSpecMock( kCatalogId, kUserId, rxTest, expectedResponses )

		local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

		local optArgs = {
			httpErrorStream = catalogSpec:watchHttpErrors()
		}

		local ozMaintenanceStreamOrSubmodel = 
				callFunctionOrSubmodel( 
					rxTest, wfCatalog, 
					'WFCatOzMaintenanceTracker',
					'getOzMaintenanceNotificationStream', 
					wfCatalog, optArgs )

		local maintenanceModeDetected = false
		
		local ozMaintenanceStreamObserver = rxTest:observe( ozMaintenanceStreamOrSubmodel
			:tap( function( _, underMaintenance )
				if underMaintenance then
					maintenanceModeDetected = true
				end
			end ) )
		
		ozMaintenanceStreamObserver:expectDataWithinTime( 5, { 
				id = 'catalog', 
				isUnderMaintenance = false
			} )
		
		-- Trigger the first HTTP request. This should cause the WFCatOzMaintenanceTracker
		-- to start polling the user account document.
		
		local catalogUrl = string.format( "/v2/catalogs/%s/updates", kCatalogId )
		
		rxTest:expectOnlyError( catalogSpec:get( catalogUrl ), "Uh oh", maintenanceError )

		-- No outage should be detected. Verify that this is the case.
		
		ozMaintenanceStreamObserver:expectDataWithinTime( 5, {
				id = 'catalog',
				isUnderMaintenance = false, 
			} )
			
		rxTest:expectEqual( false, maintenanceModeDetected )
		
		ozMaintenanceStreamObserver:dispose()

	end

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

	function WFCatOzMaintenanceTrackerTestSuite.rxTestOzMaintenanceNotificationStreamDisposal( rxTest )

		local expectedResponses = {
			{
				url = string.format( "/v2/catalogs/%s/updates", kCatalogId ),

				fail = {
					"Uh oh",
					maintenanceError
				}

			}
		}
		
		local catalogSpec = WFCatSpecMock( kCatalogId, kUserId, rxTest, expectedResponses )

		local wfCatalog = createWFCatalogForTestWithSpec( rxTest, catalogSpec )

		local optArgs = {
			httpErrorStream = catalogSpec:watchHttpErrors()
		}

		local ozMaintenanceStreamOrSubmodel, maybeParentModel = callFunctionOrSubmodel( 
					rxTest, wfCatalog, 
					'WFCatOzMaintenanceTracker',
					'getOzMaintenanceNotificationStream', 
					wfCatalog, optArgs )

		-- Quickly dispose of it.
		
		if maybeParentModel then
			ozMaintenanceStreamOrSubmodel = nil
			maybeParentModel:close()
		else
			ozMaintenanceStreamOrSubmodel:dispose()
		end
		
		-- Now make sure that Oz Maintenance errors do not cause us to poll the user's account.
		
		-- Trigger the first HTTP request. This should NOT cause the WFCatOzMaintenanceTracker
		-- to start polling the user account document.
		
		local catalogUrl = string.format( "/v2/catalogs/%s/updates", kCatalogId )
		
		rxTest:expectOnlyError( catalogSpec:get( catalogUrl ), "Uh oh", maintenanceError )
		
		rxTest:sleep( 0.5 )
			-- Wait for the WFCatOzMaintenanceTracker to poll. It should not poll. If it does,
			-- this test will fail because the wfCatalogSpec does not include any expected HTTP
			-- requests related to the WFCatOzMaintenanceTracker.

	end

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

	return maybeExistingTestClass and WFCatOzMaintenanceTrackerTestSuite 
			or WFRxTestCase.wrapSuite( 'WFCatOzMaintenanceTrackerTest', WFCatOzMaintenanceTrackerTestSuite )

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

end