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

NmbConnectors.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2018 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 Observable = Rx.Observable

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

	-- WF.core.utils
local KSDate = require 'KSDate'
local KSIdGenerator = require 'KSIdGenerator'

local KSTableUtils = require 'KSTableUtils'
local WFCatAlbums = require 'WFCatAlbums'
local WFCatUtils = require "WFCatUtils"
local WFCloseSupport = require "WFCloseSupport"
local WFRxScheduler = require "WFRxScheduler"

	-- WF.web
local KSJsonEncoder = require 'KSJsonEncoder'
local KSJsonDecoder = require "KSJsonDecoder"

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

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

local derefOpt = KSTableUtils.derefOpt
local logger = WFLogger 'NmbConnectors'

local testConfig = require "WFConfiguration".connectionConfig

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

local CONNECTOR_CONFIG_LOCAL_DOC = 'connectorConfigCache'

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

local configUrl = "https://lightroom.adobe.com/api/connectors"
local stageConfigUrl = "https://stage.adobelr.com/api/connectors"

local NmbConnectors = {}

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

local function distinctTableCompare( _, tbl1, _, tbl2 )

	return KSTableUtils.deepEquals( tbl1, tbl2 )

end

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

local function createProjectAlbum( catalog, serviceId, projectName, maybeSourceAlbumFullDocId, maybeCoverId )
	
	local docStore = catalog:docStore()

	local projectId = KSIdGenerator.uuidInLowercaseHex()	

	local projectAlbumDoc = {
		_id = projectId,
		type = 'album',
		subtype = 'project',
		name = projectName,
		sourceAlbum = maybeSourceAlbumFullDocId and {
			id = maybeSourceAlbumFullDocId,
		},
		cover = maybeCoverId and {
			id = maybeCoverId,
		},
		publishInfo = {
			version = 2,
			serviceId = serviceId
		},
	}
	
	projectAlbumDoc = WFCatUtils.addUserCreatedFields( projectAlbumDoc )
	
	-- Wait until all of the above operations have completed,
	-- then extract the local doc ID from doc store.
	
	return docStore:setDocValue( projectAlbumDoc )
		:map( function()
			return projectId
		end )
		
end

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

local function createConnectionWithAssetIds( catalog, serviceId, assetIds, projectName, maybeSourceAlbumId, maybeCoverId )
	
	return createProjectAlbum( catalog, serviceId, projectName, maybeSourceAlbumId, maybeCoverId )
		:flatMap( function( projectAlbumId )
			return WFCatAlbums.addAssetsToAlbum( catalog, projectAlbumId, assetIds )
				:concat( catalog:docStore():docValue( projectAlbumId ) )
				:takeLast( 1 )
				:map( function( projectAlbum )
					return { albumId = projectAlbum._localId, projectAlbumId = projectAlbum._id }
				end )
		end )
		
end

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

function NmbConnectors.createConnectionFromPhotos( catalog, serviceId, assetIds, maybeProjectName )
	
	-- SPAM [matthewj 2018-08-10]: This really should default on the UI side, but for now
	-- with no UI, we'll populate this here
	maybeProjectName = maybeProjectName or KSDate.politeDateFromTime( KSDate.timeNow(), 'medium', 'short' )
	
	return createConnectionWithAssetIds( catalog, serviceId, assetIds, maybeProjectName )
	
end

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

function NmbConnectors.setProjectState( catalog, albumId, newState )

	local docStore = catalog:docStore()

	return docStore:applyOverlay( {
				_id = albumId,
				publishInfo = {
					projectState = newState,
				}
			} )
		:takeLast( 0 )
end

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

function NmbConnectors.getConnectionAttributes( catalog, projectAlbumId )

	return catalog:docStore():docValue( projectAlbumId )
		:flatMapLatest( function( projectDoc )
			local attrs = {}
			if projectDoc and not projectDoc._empty then
				attrs.serviceId = derefOpt( projectDoc, 'publishInfo', 'serviceId' )
				attrs.created = derefOpt( projectDoc, 'publishInfo', 'created' )
				attrs.updated = derefOpt( projectDoc, 'publishInfo', 'updated' )
				attrs.projectState = derefOpt( projectDoc, 'publishInfo', 'projectState' )
				attrs.sourceAlbumId = derefOpt( projectDoc, 'sourceAlbum', 'id' )
				-- older implementations put the editUrl in the wrong place so check there first
				attrs.editUrl = derefOpt( projectDoc, 'publishInfo', 'remoteLinks', 'edit' ) or projectDoc.url
				if type( attrs.editUrl ) ~= 'string' then 
					attrs.editUrl = derefOpt( projectDoc, 'publishInfo', 'remoteLinks', 'edit', 'href' )
				end
				attrs.viewUrl = derefOpt( projectDoc, 'publishInfo', 'remoteLinks', 'view', 'href' )
				attrs.redirectUrl = derefOpt( projectDoc, 'publishInfo', 'remoteLinks', 'redirect', 'href' )
				attrs.userCreated = derefOpt( projectDoc, 'userCreated' )
				attrs.userUpdated = derefOpt( projectDoc, 'userUpdated' )
			end
			return Observable.just( attrs )
		end )
end

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

function NmbConnectors.updateAvailableConnectorsCache( catalog )
	local catalogSpec = catalog:ozCatalogSpec()
	
	local languageCode = WFLocalizationUtils.getLanguageCode()
	local url =  catalogSpec:isStageEnv() and stageConfigUrl or configUrl
	--local WFZString = require "WFZString" -- may need to add this trick to get a working language code in the process...
	--local languageCode = WFZString.fromZString( "$$$/locale_metric/ISO_639_1/LanguageCode=en" )
	if languageCode then
		url = url.."?locale="..languageCode
	end
	return catalogSpec:get( url,
					{ 	requestHeaders = {
							{ header = 'locale', value = languageCode },
						},
					} )
		:map( KSJsonDecoder.decodeWebSafeJson_stripNull )
		:flatMap( function( response, headers )
			local config = KSJsonDecoder.decodeWebSafeJson( response )
			if not config then
				error( 'Unexpected response to connector config request' )
			end
			-- config.sys can include a url to an extra configuration for testing
			if testConfig and testConfig.url then
				local catalogSpec = catalog:ozCatalogSpec()
				return catalogSpec:get( testConfig.url.."?locale="..languageCode, {} )
				:map( function( data, headers )
                	if headers and headers.status_code == "200" then
						local response = KSJsonDecoder.decodeWebSafeJson_stripNull_ignoreEmpty( data )
                    	local serviceId = response.serviceId
						if serviceId then 
							config[ serviceId ] = response
						end
					else
						logger:warnf( "unable to load test connector config ", testConfig.url )
					end
					return config
				end)
			else
				return Observable.just(config)
			end
		end )
		:flatMap( function( config )
			return catalog:docStore():setLocalDocValue( {
				_id = CONNECTOR_CONFIG_LOCAL_DOC, 
				config = config
			} )
		end )
		:tapOnError( function( errorResponse, errorHeaders )
			logger:warnf( "Failed to retrieve updated updated connector config", errorResponse )
		end )
end
       
--------------------------------------------------------------------------------
-- returns a list of connectors - can be filtered by status and active state

local function getAllAvailableConnectors( nmbCatalog, optDebug )
	
	return Observable.combineLatest(
		nmbCatalog:docStore():ongoingLocalDocValue( CONNECTOR_CONFIG_LOCAL_DOC ), 
		nmbCatalog:getUniversalCatalogDocumentSetting( 'connections' ) )
		:map( function( connectorDoc, activeConnections )
			
			local connectors = connectorDoc.config or {}
			
			local result = {}
			
			for t,v in pairs( connectors ) do
			
				if v.serviceId then
					if v.status == 'prod' or optDebug then
						local v1 = KSTableUtils.deepCopy( v ) -- need deepcopy so that distinctTableCompare works
						if activeConnections and activeConnections[ v.serviceId ] then
							v1.active = true
						end
						result[ #result + 1 ] = v1
					end
				end
			end
			
			return result
		end )
		:distinctUntilChanged( nil, distinctTableCompare )
		
end

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

function NmbConnectors.addConnectorModels( nmbModelBuilder )
	
	nmbModelBuilder.addCatalogBasedMostRecentValueModel( 'getAllAvailableConnectors', 
			function( wfCatalog, status )
				return getAllAvailableConnectors( wfCatalog, status )
			end )
					
	nmbModelBuilder.addCatalogBasedMostRecentValueModel( 'addActiveConnector',
			function( nmbCatalog, serviceId )
				local newConnectionInfo = {
					[ serviceId ] = {
						created = KSDate.timeInIso8601Format()
					}
				}
				-- SPAM [matthewj 2018-08-01]: This seems a bit clunky the way this API is currently written
				-- ideally, it'd be simpler to add this info without having to think about whether it's an 
				-- overlay or not.  Maybe just pushing this knowledge into NmbConnectors is fine though.
				return nmbCatalog:overlayUniversalCatalogDocumentSetting( 'connections', newConnectionInfo )
			end )
			
	nmbModelBuilder.addCatalogBasedMostRecentValueModel( 'removeActiveConnector', 
			function( nmbCatalog, serviceId )
				local newConnectionInfo = {
					[ serviceId ] = {
						_delete = true
					}
				}
				return nmbCatalog:overlayUniversalCatalogDocumentSetting( 'connections', newConnectionInfo )
			end )
			
	nmbModelBuilder.addCatalogBasedMostRecentValueModel( 'getActiveConnectors',
			function( nmbCatalog )
				return nmbCatalog:getUniversalCatalogDocumentSetting( 'connections' )
			end )

end

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

return NmbConnectors
