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

WFCatSearchOnlineRequest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 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 Disposable = Rx.Disposable
local Subject = Rx.Subject
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

local KSJsonEncoder = require "KSJsonEncoder"
local KSJsonDecoder = require "KSJsonDecoder"
local KSTableUtils = require "KSTableUtils"
local WFTableArchiving = require 'WFTableArchiving'

local WFCatSearchAndSortConstants = require "WFCatSearchAndSortConstants"
local WFCatOnlineSearchResultTableHandler = require 'WFCatOnlineSearchResultTableHandler'
local WFConfiguration = require "WFConfiguration"
local WFRxHttpClient = require 'WFRxHttpClient'

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

local searchLogger = WFLogger( 'WFCatSearchOnlineRequest' )
local autoCompleteLogger = WFLogger( 'WFCatSearchOnlineAutoComplete' )
local facetsLogger = WFLogger( 'WFCatSearchOnlineFacets' )

--------------------------------------------------------------------------------
local CURRENT_ONLINE_ASSET_SOURCE = "lightroom"
local PAGE_SIZE = WFCatSearchAndSortConstants.PAGE_SIZE
--------------------------------------------------------------------------------
local OnlineSearchStatus = WFCatSearchAndSortConstants.OnlineSearchStatus

--============ Logging =======================
local _latestRequestCounter = 0
-- not sure of checking a logger active stauts, hence to prevent the unwanted logging process, this flag is used
local onlineRequestLoggingNeeded =  WFConfiguration.searchConfiguration.onlineRequestLoggingNeeded

--------------------------------------------------------------------------------
local function search_debugPrint( logger, messageType, ... )
	if messageType == "e" or onlineRequestLoggingNeeded then
		WFSearchDebugLogging.search_debugPrint( "WFCatSearchOnlineRequest", logger, messageType, ... )
	end
end

--------------------------------------------------------------------------------
local KSDate_timeNowForAnimation = require 'KSDate'.timeNowForAnimation
local function search_debug_getCurrentTime()
	return KSDate_timeNowForAnimation()
end

--------------------------------------------------------------------------------
local function search_debugPrintTimeConsumed( logger, operationIdentifier, sessionIdentifier , 
												expectedMaxTime , startTime, endTime, 
												additionalData )
	WFSearchDebugLogging.search_debugPrintTimeConsumed( 
		"WFCatSearchOnlineRequest", logger,
		operationIdentifier, sessionIdentifier,expectedMaxTime, 
		startTime, endTime, additionalData )

end

--------------------------------------------------------------------------------
local function getFormattedTimeForSearchDebug( )
	if onlineRequestLoggingNeeded then
		return WFSearchDebugLogging.getFormattedTimeForSearchDebug( "WFCatSearchAndSort" )
	else
		return ""
	end
end

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

local WFCatSearchOnlineRequest = {
	_catalog = false,
	_catUser = false,
	
	_url = false,
	_searchOptions = false,
	_onlineSearchTerms = false,

	_isSearchActive = false,
	_currentStart = false,
	_limit = false,
	
	_totalAvailable = false,
	_totalReceived = false,
	_receivedAssets = false,
	_receivedAssetsMap = false,
	_currentReceivedCount = false,
	
	_searchResultSubject = false,
	_executionStatus = false,
	
	Disposable,
}
--============== Local methods ================--
-------------------- getLightroomDataFromSearchResult ---------------------------
local function getLightroomDataFromSearchResult( searchResult, headers )
	local resultSets = searchResult and searchResult.result_sets
	if resultSets then
		for _,resultSet in pairs( resultSets ) do
			local name = resultSet.name
			if name == CURRENT_ONLINE_ASSET_SOURCE then
			
				local curentItemsCount = ( resultSet.items and #resultSet.items ) or 0
				local totalCount = resultSet.total_items
					
				if onlineRequestLoggingNeeded then	
					
					local message = string.format( 
						"Search: request-id: %s, current page count: %s, total items: %s", 
							headers[ 'x-request-id' ], curentItemsCount, totalCount )
					search_debugPrint( searchLogger, "d", message, 
									_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n") 
				end
		
				return resultSet.items,  curentItemsCount, totalCount
			end
		end
	else
		if onlineRequestLoggingNeeded then	
			search_debugPrint( searchLogger, "d", "Search : WFCatSearchOnlineRequest: no result set V2" , 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
		end
	end
end

-------------------- getAssetId ---------------------------
local function getAssetId( assetDetails )
	local assetId =  assetDetails[ "asset_id" ]
	assert( assetId, "assetId is expected to be present in the V2 result as asset_id")
	return assetId
end

-------------------- addNextDataRangeTerms ---------------------------
local function addNextDataRangeTerms( self )
	self._onlineSearchTerms["start_index" ] = self._currentStart
	self._onlineSearchTerms["limit" ] = self._limit 
end

-------------------- initializeCommonTerms ---------------------------
local function initializeCommonTerms( self )
	self._onlineSearchTerms[ "scope" ] 				= 	{ CURRENT_ONLINE_ASSET_SOURCE  }
	self._onlineSearchTerms[ "sort_order" ] 		=  	"desc"
	self._onlineSearchTerms[ "sort_orderby" ] 		=  	"relevancy"
	self._onlineSearchTerms[ "fields_excluded" ] 	= 	{ "links", "tags" }
	
end

-------------------- analyseSearchResultData ---------------------------
local function analyseSearchResultData( self, searchResult, headers, requestTimeCounter )
	local ignoreCurrentResult = false
	if requestTimeCounter ~= _latestRequestCounter then
		-- we are not the latest request
		ignoreCurrentResult = true
		return nil, ignoreCurrentResult
	end
	
	local dataReceived  = false
	local metrics = searchResult.metrics
	if metrics then
	
		if onlineRequestLoggingNeeded then	
			local searchResultMetricsData = WFTableArchiving.tableToString( metrics )
			search_debugPrint( searchLogger, "d", "Search: request-id: " .. headers[ 'x-request-id' ] .. " | result metrics " .. searchResultMetricsData, 
							_latestRequestCounter, getFormattedTimeForSearchDebug() ,"\n") 
		end
		
		-- since we have only one search scope,  metrics.total_hits is our total_hits
		-- hence not changing this to the page's totalCount
		if self._totalAvailable == false then
			self._totalAvailable = metrics.total_hits
		end
	end
	
	if self._totalAvailable > 0 then
		local lightroomResults, currentCount = getLightroomDataFromSearchResult( searchResult, headers )
		currentCount = currentCount or 0
		self._currentReceivedCount = currentCount
		
		if currentCount > 0 then
			dataReceived = true
			self._totalReceived = self._totalReceived + currentCount
			local currentAssets = {}
			if not self._receivedAssets then
				self._receivedAssets = {}
			end
			
			if not self._receivedAssetsMap then
				-- currently there are some issues with Federer results
				-- in the successive page requests, we get the repeated data
				-- this results in empty grid.
				-- we are peventing the same here, by checking against present
				-- itemsMap
				self._receivedAssetsMap = {}
			end
			for _, assetDetails in ipairs( lightroomResults ) do
			
				local assetId = getAssetId( assetDetails )
				if not  self._receivedAssetsMap[ assetId ]  then
					table.insert( self._receivedAssets, assetId )
					table.insert( currentAssets, assetId )
					self._receivedAssetsMap[ assetId ] = true
				else
					search_debugPrint( searchLogger, "d", "Search: received duplicate asset ".. assetId, 
							_latestRequestCounter, getFormattedTimeForSearchDebug() ,"\n")
				end
				
			end
		
			if #currentAssets > 0 then
				WFCatOnlineSearchResultTableHandler.updateSearchTable( self._catalog, self._currentStart, 
					currentAssets )
			end
			
		end
		
	end
	-- if current count is 0, we report nil and handle this as completedUnexpectedly case 
	-- if total received is not same as total available
	return dataReceived, ignoreCurrentResult
end

-------------------- isFurtherRequestPossible ---------------------------
local function isFurtherRequestPossible( self )
	
	local totalReceived = self._totalReceived
	if ( self._currentReceivedCount % PAGE_SIZE ~= 0 ) then
		-- we have an issue of one item missing
		-- so temp fixing
		totalReceived = totalReceived + 1
	end
	
	return  totalReceived < self._totalAvailable 
end

-------------------- setNewSearchDataRange ---------------------------
local function setNewSearchDataRange( self )
	if self._totalAvailable  == false then
		-- we have not recieved the total count
		-- so let us initialize the detais
		self._currentStart = 0
		self._limit = PAGE_SIZE
		self._totalReceived = 0
	else
		if isFurtherRequestPossible( self ) then
			-- we have more elements to obtain
			self._currentStart = self._totalReceived  
			local endLimit = PAGE_SIZE -- math.min( self._totalReceived+PAGE_SIZE, self._totalAvailable )
			self._limit = endLimit
		end
	end
	self._currentReceivedCount  = 0
	addNextDataRangeTerms( self ) 
end

-------------------------- handleSearchCompletion -------------------------------------------
local function sendSearchCompletion( self, status )
	-- TBD handle the status
	self._isSearchActive = false
	self._searchResultSubject:onNext( status ) 
	self._searchResultSubject:onCompleted()
end
	
-------------------------- handleSearchCompletion -------------------------------------------
local function sendSearchStatus( self, status, isCompleted )
	self._executionStatus = status
	if isCompleted then
		self._isSearchActive = false
	end
	
	self._searchResultSubject:onNext(  )
	
	if isCompleted then
		self._searchResultSubject:onCompleted(  )
	end
end
		

-------------------------- startNextSetSearch -------------------------------------------
local function startNextSetSearch( self )

	local currentCounter = _latestRequestCounter
	if not self._searchResultSubject then
		self._searchResultSubject  = Subject( )
	end
	
	if self._catUser then
		setNewSearchDataRange( self )
		
		local searchOptions = KSTableUtils.deepCopy( self._searchOptions )
		searchOptions.requestPriority = WFRxHttpClient.requestPriorityHighest
		searchOptions.requestCost = WFRxHttpClient.requestCostMaximizeBandwidth
			-- Prioritize this HTTP request.
		
		-- update the request headers to use the user-provided request-id
		local maybeRequestId = self._onlineSearchTerms.requestId
		if maybeRequestId then
		
			self._onlineSearchTerms.requestId = nil -- reset the requestId so it isn't used for subsequent calls.
			local requestHeaders = searchOptions.requestHeaders or {}
			table.insert( requestHeaders, { header = 'X-Request-Id', value = maybeRequestId } )
			searchOptions.requestHeaders = requestHeaders
			
		end
		
		local searchTerms = KSTableUtils.deepCopy( self._onlineSearchTerms )
		-- this is an internal data field so, let us remove before sending to USS
		searchTerms.isNetworkAvailable = nil
		
		if onlineRequestLoggingNeeded then
			local startString = "\n----------------------------------\n"
			
			if _latestRequestCounter == 1 then
				search_debugPrint( searchLogger, "d", "Search: url " .. self._url, 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
			end
			
			local searchInputData = WFTableArchiving.tableToString( searchTerms )
			if maybeRequestId then
				searchInputData = string.format( "requestId: %s \n", maybeRequestId ) .. searchInputData
			end
			
			search_debugPrint( searchLogger, "d", startString .. "Search: requestData \n" .. searchInputData .. "\n", 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
							
		end
		
		local jsonData = KSJsonEncoder.prettyPrint( searchTerms )
		
		local dataObservable = self._catUser:put( self._url, jsonData, searchOptions )
		
		dataObservable : map( function( searchResult, headers )
			local didResultAnalysisSucceed, message = pcall ( function ()
				if  self._isSearchActive then
					local success, searchResultTable = pcall( KSJsonDecoder.decodeWebSafeJson, searchResult )
					local ignoreCurrentResult = false
					if success then
						local dataReceived
						dataReceived, ignoreCurrentResult = analyseSearchResultData( self, searchResultTable, headers, currentCounter ) 
						if dataReceived then
							
							-- we received the data, so sending progress
							sendSearchStatus( self, OnlineSearchStatus.InProgress , false )
							
							if isFurtherRequestPossible( self ) then
								-- we have more data, so place the next page request
								startNextSetSearch( self )
							else
								-- all data receieved completly
								sendSearchStatus( self, OnlineSearchStatus.Completed , true )
							end
						else
							if not ignoreCurrentResult then
								-- no data received
								-- if available vs received if there is an issue, let us send CompletedUnexpectedly
								if self._totalAvailable ~= self._totalReceived then
									sendSearchStatus( self, OnlineSearchStatus.CompletedUnexpectedly , true )
								else
									sendSearchStatus( self, OnlineSearchStatus.Completed , true )
								end
							end
						end
						
					else
						sendSearchCompletion( self, false )
					end
				end
			end )
			if not didResultAnalysisSucceed then
				search_debugPrint( searchLogger, "d","\n********** IMP ERROR IN PROCESSING ***************\n Search: processing result error \n" .. message .. "\n", 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
			end
			
		end )
		:tapOnError( function( response, headers )
			local decoded, responseTable = pcall( KSJsonDecoder.decodeWebSafeJson, response )
			
			if decoded and type( responseTable ) == 'table' then
				-- TODO [anmurark 2018-05-17]: Take action based on the response error code
				
				local searchResponseData = WFTableArchiving.tableToString( responseTable )
				local startString = "\n----------------------------------\n"
				search_debugPrint( searchLogger, "e", startString .. "Search: responseError \n" .. searchResponseData .. "\n", 
							headers['x-request-id'], getFormattedTimeForSearchDebug(), "\n" ) 

			end
			
			sendSearchStatus( self, OnlineSearchStatus.CompletedUnexpectedly , true )

		end)
		:call( WFRxRunUntilDone ) 
	end
	
	return self._searchResultSubject
end

-------------------------- startFacetValuesSearch -------------------------------------------
local function startFacetValuesSearch( self )
	
	self._searchResultSubject = Subject( )
	
	if self._catUser then
	
		-- Set limit to 0 to ignore items in the response
		self._limit = 0	
		self._currentStart = 0
		addNextDataRangeTerms( self )
		
		local searchOptions = KSTableUtils.deepCopy( self._searchOptions )
		local searchTerms = KSTableUtils.deepCopy( self._onlineSearchTerms )
		
		if onlineRequestLoggingNeeded then
			local startString = "\n----------------------------------\n"
			
			search_debugPrint( facetsLogger, "d", "FacetedSearch: url " .. self._url, 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
			
			local searchInputData = WFTableArchiving.tableToString( searchTerms )
			
			search_debugPrint( facetsLogger, "d", startString .. "FacetedSearch: requestData \n" .. searchInputData .. "\n", 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
		
		end
		
		local jsonData = KSJsonEncoder.prettyPrint( searchTerms )
		local startTime = search_debug_getCurrentTime()
		
		searchOptions.requestPriority = WFRxHttpClient.requestPriorityHighest
		searchOptions.requestCost = WFRxHttpClient.requestCostMaximizeBandwidth
			-- Prioritize this HTTP request.
		
		local dataObservable = self._catUser:put( self._url, jsonData, searchOptions )
		
		dataObservable : map( function( searchResult, headers )
			local success, searchResultTable = pcall( KSJsonDecoder.decodeWebSafeJson, searchResult )
			local facetsMap = {}
			
			if success then
			
				local resultSets = searchResultTable.result_sets
				if resultSets then
					for _, resultSet in ipairs( resultSets ) do
						local name = resultSet.name
						if name == CURRENT_ONLINE_ASSET_SOURCE then
							
							local facetsArray = resultSet.facets or {}
							for _, facetData in ipairs( facetsArray ) do
								facetsMap[ facetData.facet_field ] = facetData.facet_values
							end
							
							if onlineRequestLoggingNeeded then
								local totalTime = search_debug_getCurrentTime() - startTime
								local startString = "\n----------------------------------\n"
								local firstLine = string.format( "FacetedSearch: response { req id: %s } %s sec\n", headers[ 'x-request-id' ], totalTime )
								local facetsData = WFTableArchiving.tableToString( facetsMap )
								
								search_debugPrint( facetsLogger, "d", startString .. firstLine .. facetsData .. "\n",
										getFormattedTimeForSearchDebug(), "\n" )
								
							end
						end
					end
				end
			end
			
			self._searchResultSubject:onNext( facetsMap )
			self._searchResultSubject:onCompleted()
			
		end )
		
		:tapOnError( function( response, headers )
			
			local decoded, responseTable = pcall( KSJsonDecoder.decodeWebSafeJson, response )
			
			if decoded then
				local startString = "\n----------------------------------\n"
				local searchResponseData = WFTableArchiving.tableToString( responseTable )
				
				search_debugPrint( facetsLogger, "e", startString .. "FacetedSearch: error \n" .. searchResponseData .. "\n", 
						headers['x-request-id'], getFormattedTimeForSearchDebug(), "\n" )

			end
			
			self._searchResultSubject:onNext( {} )
			self._searchResultSubject:onCompleted()
			
		end)
		:call( WFRxRunUntilDone ) 
	end

	return self._searchResultSubject

end


-------------------------- getMetadataSearch -------------------------------------------
local function getMetadataSearch( self )
	
	self._searchResultSubject = Subject( )
	
	if self._catUser then
		
		-- set METADATA_ONLY flag to restrict the response
		local resultType = { }
		resultType["core_resultType"] = "METADATA_ONLY"
		self._onlineSearchTerms[ "hints" ] = self._onlineSearchTerms[ "hints" ] or { }
		table.insert( self._onlineSearchTerms[ "hints" ], resultType )
		
		local searchOptions = KSTableUtils.deepCopy( self._searchOptions )
		local searchTerms = KSTableUtils.deepCopy( self._onlineSearchTerms )
		
		if onlineRequestLoggingNeeded then
			local startString = "\n----------------------------------\n"
			local searchInputData = WFTableArchiving.tableToString( searchTerms )
			search_debugPrint( facetsLogger, "d", startString .. "Search: metadata request\n" .. searchInputData .. "\n", 
							_latestRequestCounter, getFormattedTimeForSearchDebug(), "\n" ) 
		
		end
		
		local jsonData = KSJsonEncoder.prettyPrint( searchTerms )
		
		searchOptions.requestPriority = WFRxHttpClient.requestPriorityHighest
		searchOptions.requestCost = WFRxHttpClient.requestCostMaximizeBandwidth
			-- Prioritize this HTTP request.
		
		local dataObservable = self._catUser:put( self._url, jsonData, searchOptions )
		
		dataObservable : map( function( searchResult )
			local success, searchResultTable = pcall( KSJsonDecoder.decodeWebSafeJson, searchResult )
			local metadataMap = {}
			if success then
			
				local resultSets = searchResultTable.result_sets
				if resultSets then
					for _, resultSet in ipairs( resultSets ) do
						local name = resultSet.name
						if name == CURRENT_ONLINE_ASSET_SOURCE then
							
							local metadataArray = resultSet.meta and resultSet.meta.field_info or {}
							
							for _, metadata in ipairs( metadataArray ) do
								metadataMap[ metadata.canonical_name ] = metadata.display_names
							end

							if onlineRequestLoggingNeeded then
								local startString = "\n----------------------------------\n"
								local metadataStr = WFTableArchiving.tableToString( metadataMap )
								search_debugPrint( facetsLogger, "d", startString .. "Search: metadata response \n" .. metadataStr .. "\n", 
										getFormattedTimeForSearchDebug(), "\n" )
							end
						end
					end
				end
			end
			
			self._searchResultSubject:onNext( metadataMap )
			self._searchResultSubject:onCompleted()
			
		end )
		:tapOnError( function( response, headers )
			
			local decoded, responseTable = pcall( KSJsonDecoder.decodeWebSafeJson, response )
			
			if decoded then
				
				local startString = "\n----------------------------------\n"
				local searchResponseData = WFTableArchiving.tableToString( responseTable )
				
				search_debugPrint( facetsLogger, "e", startString .. "Search: metadata error \n" .. searchResponseData .. "\n", 
						headers['x-request-id'], getFormattedTimeForSearchDebug(), "\n" )
			end
			
			self._searchResultSubject:onNext( {} )
			self._searchResultSubject:onCompleted()
			
		end)
		:call( WFRxRunUntilDone ) 
	end

	return self._searchResultSubject

end

--------------------------------------------------------------------------------
local function startAutoComplete( self )

	self._searchResultSubject = Subject( )
	
	if self._catUser then
		
		local startTime = search_debug_getCurrentTime()
		
		local searchOptions = KSTableUtils.deepCopy( self._searchOptions )
		
		searchOptions.requestPriority = WFRxHttpClient.requestPriorityHighest
		searchOptions.requestCost = WFRxHttpClient.requestCostMaximizeBandwidth
			-- Prioritize this HTTP request.
			
		local dataObservable = self._catUser:put( self._url, "", searchOptions )
		
		dataObservable : map( function( searchResult, headers )
			local _, searchResultTable = pcall( KSJsonDecoder.decodeWebSafeJson, searchResult )

			if onlineRequestLoggingNeeded then
				local startString = "\n----------------------------------\n"
				local autoCompleteStr = WFTableArchiving.tableToString( searchResultTable )
				search_debugPrint( autoCompleteLogger, "d", startString .. "Autocomplete: response \n" .. autoCompleteStr .. "\n", 
						getFormattedTimeForSearchDebug(), "\n" )
			end
			
			local suggestions = searchResultTable.suggested_completions or {}
			
			local endTime = search_debug_getCurrentTime()
			local expectedTime
			local suggestionCount = string.format( "%s suggestions | requestId: %s\n", #suggestions, headers[ 'x-request-id' ] )
			search_debugPrintTimeConsumed( autoCompleteLogger, "Autocomplete: URL: ", self._url, expectedTime, startTime, endTime, suggestionCount )
			
			local results = {}
			for _, autoCompleteValue in ipairs( suggestions ) do
				
				local facet_key = autoCompleteValue.facet_key or "search"
				local values = results[ facet_key ] or {}
				
				local facetValue = {}
				facetValue[ 'href' ] = autoCompleteValue.href
				facetValue[ 'score' ] = autoCompleteValue.score
				facetValue[ 'display_name' ] = autoCompleteValue.name
				facetValue[ 'value' ] = autoCompleteValue.value
				
				-- if "value" field was empty, try "id" field ( applicable for people facet )
				if facetValue[ 'value' ] == nil then
					facetValue[ 'value' ] = autoCompleteValue.id
				end
				
				-- if "id" field was empty, use "name" field
				if facetValue[ 'value' ] == nil then
					facetValue[ 'value' ] = autoCompleteValue.name
				end
				
				-- [anmurark]: the value could be number or string depending on the facet type
				-- if it can be represented as number, change it to number because it will be
				-- 1. consistent with the facet values API response from USS
				-- 2. easy to sort based on number if required.
				facetValue[ 'value' ] = tonumber( facetValue[ 'value' ] ) or facetValue[ 'value' ]
				
				values[ #values + 1 ] = facetValue
				
				results[ facet_key ] = values
				
			end
			
			local metadata = {}
			metadata[ 'requestId' ] = headers['x-request-id']
			metadata[ 'url' ] = WFCatSearchAndSortConstants.getAutoCompleteEndpoint()
			metadata[ 'count' ] = #suggestions
			metadata[ 'responseTime' ] = endTime - startTime
			
			local response = {}
			
			response[ 'suggestions' ] = results
			response[ 'metadata' ] = metadata
			
			self._searchResultSubject:onNext( response )
			self._searchResultSubject:onCompleted()
			
		end )
		
		:tapOnError( function( response, headers )
		
			local decoded, responseTable = pcall( KSJsonDecoder.decodeWebSafeJson, response )
			
			local startString = "\n----------------------------------\n"
			local requestId = headers and headers['x-request-id'] or '<no request id>'
			local searchResponseData = decoded and WFTableArchiving.tableToString( responseTable ) or tostring( response )
			local logInfo = string.format( "%s Autocomplete: error \n URL: %s \n Response: %s \n", startString, self._url, searchResponseData )
			search_debugPrint( autoCompleteLogger, "e", logInfo, requestId, getFormattedTimeForSearchDebug(), "\n" )
			
			self._searchResultSubject:onNext( {} )
			self._searchResultSubject:onCompleted()
		end)
		
		:call( WFRxRunUntilDone ) 
			
	end
	
	return self._searchResultSubject

end

--============== API's ================--
------------------- __init --------------------------------------
function WFCatSearchOnlineRequest:__init( userGivenSearchTerms )
	self._onlineSearchTerms = userGivenSearchTerms or {}
	initializeCommonTerms( self )
	self._totalReceived = 0
end

--[[ wiki links : 
V2: https://wiki.corp.adobe.com/display/adobesearch/Lightroom+Search+V2+Request+and+Response

V3: https://wiki.corp.adobe.com/display/adobesearch/Lightroom+Facets+Design
    https://wiki.corp.adobe.com/display/adobesearch/Facet+Field+Info+Design

]]
---------------------- executeOnlineSearch ----------------------------------
function WFCatSearchOnlineRequest:executeOnlineSearch( catalog, catUser, url, searchOptions )
	self._catalog = catalog
	self._catUser = catUser
	self._url = url
	self._searchOptions = KSTableUtils.deepCopy( searchOptions )
	WFCatOnlineSearchResultTableHandler.clearSearchTable( self._catalog )
	self._isSearchActive = true
	
	_latestRequestCounter = _latestRequestCounter + 1
	return startNextSetSearch( self )
end

---------------------- executeFacetValuesSearch ----------------------------------
function WFCatSearchOnlineRequest:executeFacetValuesSearch( catalog, catUser, url, searchOptions )
	self._catUser = catUser
	self._url = url
	self._searchOptions = KSTableUtils.deepCopy( searchOptions )
	
	return startFacetValuesSearch( self )
end

---------------------- categoryKeysSearch ----------------------------------
function WFCatSearchOnlineRequest:executeCategoryKeysSearch( catalog, catUser, url, searchOptions )
	self._catUser = catUser
	self._url = url
	self._searchOptions = KSTableUtils.deepCopy( searchOptions )

	return getMetadataSearch( self )
	
end

---------------------- executeAutoComplete ----------------------------------
function WFCatSearchOnlineRequest:executeAutoComplete( catalog, catUser, url, searchOptions )
	self._catUser = catUser
	self._url = url
	self._searchOptions = KSTableUtils.deepCopy( searchOptions )
	
	return startAutoComplete( self )
end

-------------------------- closeRequest -------------------------------------
function WFCatSearchOnlineRequest:closeRequest(  )
	self._onlineSearchTerms = false
	self._currentStart = false
	self._limit = false
	self._totalAvailable = false
	self._totalReceived = 0
	self._url = false
	self._searchOptions = false
	self._searchResultSubject:dispose()
	self._searchResultSubject = false
end

-------------------------- getCurrentStatus -------------------------------------
function WFCatSearchOnlineRequest:getCurrentStatus(  )
	local status = {}
	status.latestRequestStart 	=  self._currentStart
	status.latestRequestEnd 	=  self._limit
	status.totalReceived 		=  self._totalReceived
	status.totalAvailable 		=  self._totalAvailable
	status.executionStatus		=  self._executionStatus
	return status
end

return Rx.Class 'WFCatSearchOnlineRequest'( WFCatSearchOnlineRequest )
