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

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

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

local Rx = require "Rx"
local WFArguments = require 'WFArguments'

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

local WFRxRunUntilDone = require "WFRxRunUntilDone"

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

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

-- @mark LrmCatCollaboration namespace

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

local LrmCatCollaboration = {}

local kIsCatalogComment = {}

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

local kFavoriteFields = [[ spacefavorite.created, fu.firstName, fu.lastName, fu.fullName,
		author_id, spacefavorite.assetId, spacefavorite.spaceId, space.thisFeedbackUserId ]]

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

local kCommentFields = [[ body, comment.created, fu.firstName, fu.lastName, fu.fullName,
		author_id, comment.assetId, comment.spaceId, space.thisFeedbackUserId ]]

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

local nextIndex
do
	local index = 0
	nextIndex = function() index = index + 1 return index end
end

local kDocIdIndex				= nextIndex()
local _ --[[ kSourceIndex ]]	= nextIndex()
local _ --[[ kAlbumIdIndex ]]	= nextIndex()
local _ --[[ kAlbumNameIndex ]]	= nextIndex()
local _ --[[ kUpdateTimeStampIndex ]]	= nextIndex()
local kBodyIndex				= nextIndex()
local kCreatedIndex				= nextIndex()
local kAuthorFirstNameIndex		= nextIndex()
local kAuthorLastNameIndex		= nextIndex()
local kAuthorFullNameIndex		= nextIndex()
local kAuthorIdIndex			= nextIndex()
local kAssetIdIndex				= nextIndex()
local kSpaceIdIndex				= nextIndex()
local kThisFeedbackUserId		= nextIndex()

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

local kCommentOnSpaceByAlbumClause = [[
		FROM
			comment
		LEFT JOIN
			space
		ON
			space.docId == comment.spaceId
		LEFT JOIN
			feedback_user fu
		ON
			fu.userId == comment.author_id
		WHERE
			comment.assetId == ? AND comment.spaceId
		IN (
			SELECT
				spaceId
			FROM
				space_album
			WHERE
				space_album.albumId == ?
		)
		ORDER BY
			comment.created]]

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

local function formatComment( idRow, optIndexOfFirstCommentField )

	local comment = {}

	local offset = 0

	if optIndexOfFirstCommentField then
		offset = optIndexOfFirstCommentField - kBodyIndex
	end

	-- doc id is always first, regardless of offset
	comment.id = idRow[ kDocIdIndex ]

	comment.body = idRow[ kBodyIndex + offset ]
	comment.created = idRow[ kCreatedIndex + offset ]
	comment.spaceId = idRow[ kSpaceIdIndex + offset ]
	comment.assetId = idRow[ kAssetIdIndex + offset ]
	comment.type = 'comment'

	local commentAuthorId = idRow[ kAuthorIdIndex + offset ]

	local thisFeedbackUserId = idRow[ kThisFeedbackUserId + offset ]

	local isYou = commentAuthorId == thisFeedbackUserId
			or commentAuthorId == nil

	comment.author = {
		firstName = idRow[ kAuthorFirstNameIndex + offset ],
		fullName = idRow[ kAuthorFullNameIndex + offset ],
		lastName = idRow[ kAuthorLastNameIndex + offset ],
		id = commentAuthorId,
		isYou = isYou
	}

	return comment

end

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

local function formatFavorite( idRow )

	local favorite = {}

	-- doc id is always first, regardless of offset
	favorite.id = idRow[ 1 ]

	favorite.created = idRow[ 6 ]
	favorite.assetId = idRow[ 11 ]
	favorite.spaceId = idRow[ 12 ]
	favorite.type = 'favorite'

	local favoriteAuthorId = idRow[ 10 ]

	local thisFeedbackUserId = idRow[ 13 ]

	local isYou = favoriteAuthorId == thisFeedbackUserId
			or favoriteAuthorId == nil

	favorite.author = {
		firstName = idRow[ 7 ],
		fullName = idRow[ 9 ],
		lastName = idRow[ 8 ],
		id = favoriteAuthorId,
		isYou = isYou
	}

	return favorite

end

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

local function getSpaceForAlbum( wfCatalog, albumId )

	return wfCatalog:indexStore():queryOnce{
			sql = [[SELECT spaceId FROM space_album WHERE albumId == ?]],
			singleColumnQuery = true,
			quiesceUntilUpToDate = true,
			albumId
		}
		:flatMap( function( idRows )
			-- If there is a space for this album, return the ID for that space.
			local spaceId = KSTableUtils.derefOpt( idRows, 1 )

			if not spaceId then
				return Rx.Observable.throw( "Album is not shared"  )
			end

			return Rx.Observable.just( spaceId )
		end )

end

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

function LrmCatCollaboration.hasCommentsInAlbum( wfCatalog, albumId )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT
						comment.docId
					FROM
						comment
					WHERE
						spaceId
					IN (
						SELECT
							spaceId
						FROM
							space_album
						WHERE
							space_album.albumId == ? )
					AND
						comment.assetId
					NOT IN (
						SELECT
							docId
						FROM
							trashAssets )
					LIMIT 1 ]],
			albumId }
		:pluck( 1, 1 )

end

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

function LrmCatCollaboration.hasSpaceFavoritesInAlbum( wfCatalog, albumId )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT
						spacefavorite.docId
					FROM
						spacefavorite
					WHERE
						spaceId IN (SELECT spaceId FROM space_album WHERE albumId == ?)
					AND
						spacefavorite.assetId NOT IN (SELECT docId FROM trashAssets)
					LIMIT 1]],
			albumId }
		:pluck( 1, 1 )

end

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

function LrmCatCollaboration.addComment( wfCatalog, albumId, assetId, comment )

	if not ( #comment > 0 ) then
		error( "LrmCatCollaboration:addComment: The comment body must be a non-empty string", 2 )
	end

	local result
	if albumId then
		result = getSpaceForAlbum( wfCatalog, albumId )
	else
		result = Rx.Observable.just( kIsCatalogComment )
	end

	local commentId
	return result
		:flatMap( function( spaceId )

			if not spaceId then
				return Rx.Observable.throw( "No sharing space" )
			end

			return wfCatalog:docStore():docValue( spaceId )

		end )

		:flatMap( function( spaceDocValue )

			local spaceId = spaceDocValue._id or spaceDocValue._docId

			if spaceId == kIsCatalogComment then
				spaceId = nil
			end

			local thisSpaceUserSection = KSTableUtils.derefOpt( spaceDocValue, '_serverProvidedMetadata', 'this_space_user' )

			local isSharedWithYouAsset, spaceFeedbackUserId

			if spaceId and thisSpaceUserSection then

				isSharedWithYouAsset = true
					-- this_space_user should only be present in shared-with-you spaces.

				spaceFeedbackUserId = KSTableUtils.derefOpt( thisSpaceUserSection, 'user', 'id' )

			end

			commentId = KSIdGenerator.uuidInLowercaseHex()
			local created = KSDate.timeInIso8601Format( nil, false )

			-- fill in the other values that we expect the server will provide
			local ozUserId = wfCatalog:ozUser():userId()
			return wfCatalog:ozUser():userInfoCache():docValue( ozUserId )
				:flatMap( function( userInfo )

					local author = {
						first_name = KSTableUtils.derefOpt( userInfo, 'first_name' ),
						last_name = KSTableUtils.derefOpt( userInfo, 'last_name' ),
						full_name = KSTableUtils.derefOpt( userInfo, 'full_name' ),
						id = isSharedWithYouAsset and spaceFeedbackUserId or userInfo._id,
					}

					local overlay = {
						_id = commentId,
						type = 'comment',
						subtype = 'user',
						body = comment,
						asset = { id = assetId },
						_localOnly = {
							created = created,
							author = author
						}
					}

					if isSharedWithYouAsset then
						overlay._spaceId = spaceId
					else
						overlay.space = { id = spaceId }
					end

					return wfCatalog:docStore():applyOverlay( overlay )

				end )
		end )
		:map( function()
			return commentId
		end )
		:call( WFRxRunUntilDone )

end

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

function LrmCatCollaboration.toggleMySpaceFavorite( wfCatalog, assetId, albumId )

	local ozUserId
	local spaceId, isSharedWithYouAsset, spaceFeedbackUserId

	local toggle = getSpaceForAlbum( wfCatalog, albumId )

		:flatMap( function( spaceId )

			return wfCatalog:docStore():docValue( spaceId )

		end )

		:flatMap( function( spaceDocValue )

			spaceId = spaceDocValue._id or spaceDocValue._docId

			local thisSpaceUserSection = KSTableUtils.derefOpt( spaceDocValue, '_serverProvidedMetadata', 'this_space_user' )

			if spaceId and thisSpaceUserSection then

				isSharedWithYouAsset = true
					-- this_space_user should only be present in shared-with-you spaces.

				spaceFeedbackUserId = KSTableUtils.derefOpt( thisSpaceUserSection, 'user', 'id' )

			end

			local ozUserId = wfCatalog:ozUser():userId()
			return wfCatalog:ozUser():userInfoCache():docValue( ozUserId )

		end )

		:flatMap( function( localUserInfo )

			local feedbackUserId = spaceFeedbackUserId or ozUserId

			return wfCatalog:indexStore():queryOnce{
					sql = [[SELECT docId FROM spacefavorite WHERE assetId == ? AND
								spaceId == ? AND author_id == ? ]],
					singleColumnQuery = true,
					quiesceUntilUpToDate = true,
					assetId,
					spaceId,
					feedbackUserId
				}
				:flatMap( function( idRows )

					local spaceFavoriteId = KSTableUtils.derefOpt( idRows, 1 )

					if not spaceFavoriteId then

						local author = {
							first_name = KSTableUtils.derefOpt( localUserInfo, 'first_name' ),
							last_name = KSTableUtils.derefOpt( localUserInfo, 'last_name' ),
							full_name = KSTableUtils.derefOpt( localUserInfo, 'full_name' ),
							id = spaceFeedbackUserId or localUserInfo._id,
						}

						spaceFavoriteId = KSIdGenerator.uuidInLowercaseHex()

						local overlay = {
							_id = spaceFavoriteId,
							type = 'favorite',
							subtype = 'user',
							asset = {
								id = assetId,
							},
							_localOnly = {
								created = KSDate.timeInIso8601Format( nil, false ),
								author = author,
							}
						}

						if isSharedWithYouAsset then
							overlay._spaceId = spaceId
						else
							overlay.space = { id = spaceId }
						end

						return wfCatalog:docStore():applyOverlay( overlay )

					else

						return wfCatalog:docStore():deleteDoc( spaceFavoriteId )

					end

				end )

		end )

	return toggle

end

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

local function getSyncTerms( docType )

	if docType == 'asset' then
		return {
			fetchSql = [[SELECT docId, spaceId FROM space_asset WHERE assetId == ?]],
			isShared = [[SELECT
							space_asset.docId, spaceId FROM space_asset
						INNER JOIN
							space
						ON
							space_asset.spaceId == space.docId
						WHERE
							assetId == ? AND space.private == 0]],
			docId = 'assetId',
			space_doc_id = 'space_asset_id',
			space_doc = 'space_asset',

		}
	elseif docType == 'album' then
		return {
			fetchSql = [[SELECT docId, spaceId FROM space_album WHERE albumId == ?]],
			isShared = [[SELECT
							space_album.docId, spaceId FROM space_album
						INNER JOIN
							space
						ON
							space_album.spaceId == space.docId
						WHERE
							albumId == ? AND space.private == 0]],
			docId = 'albumId',
			space_doc_id = 'space_album_id',
			space_doc = 'space_album',
		}
	else
		error "docType must be 'asset' or 'album'";
	end
end

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

function LrmCatCollaboration.isAlbumShared( wfCatalog, albumId )

	local syncTerms = getSyncTerms('album')

	return wfCatalog:indexStore():queryOnce{
			sql = syncTerms.isShared,
			quiesceUntilUpToDate = true,
			albumId
		}
		:map( function( idRows )

			-- If there is a space for this album, return true
			return not not KSTableUtils.derefOpt( idRows, 1 )

		end )
end

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

function LrmCatCollaboration.shareAssetOrAlbum( wfCatalog, docType, docId, spaceSubtype, allowDownload )

	-- Revel defaults the ability to download shared albums to true, while Lr wants it to be false
	-- adding a param to control this and, if not set, the Lr default will be used

	if allowDownload == nil then
		allowDownload = false
	end


	-- The current sharing model for albums calls for a 1-1 mapping from an 'album' or 'asset' document to
	-- a 'space'. In order to share the document, a 'space' needs to be created and the document
	-- added to it.

	-- This function creates a space and adds the provided document to it by creating a
	-- 'space_album' or 'space_asset' document as appropriate. If a space already exists for this document,
	-- it returns the ID of that 'space'.

	local syncTerms = getSyncTerms( docType )

	local result = wfCatalog:indexStore():queryOnce{
		sql = syncTerms.fetchSql,
		quiesceUntilUpToDate = true,
		docId }
		:flatMap( function( idRows )

			-- If there is a space for this album, return the ID for that space.
			local spaceId = KSTableUtils.derefOpt( idRows, 1, 2 )
			if spaceId then

				return wfCatalog:docStore():applyOverlay {
						_id = spaceId,
						private = false,
					}
					:map( function()
						return spaceId
					end )

			end

			spaceId = KSIdGenerator.uuidInLowercaseHex()

			local space_doc_id = KSIdGenerator.uuidInLowercaseHex()

			-- Save the album information in a localOnly section in the space itself. Oz does
			-- not have a separate Add Album to Space API for 2.0. We need to send a single
			-- request to create a space and add the album to it.  When this API is present, we
			-- should sync the space_album document separately as is done for album_asset
			-- documents.

			return wfCatalog:docStore():applyOverlay {
					_id = spaceId,
					type = 'space',
					_localOnly = {
						[ docType ] = {
							[ syncTerms.docId ] = docId,
							[ syncTerms.space_doc_id ] = space_doc_id,
						},
					},
					subtype = 'shared',
					download = allowDownload,
					private = false,
					metadata = false,
					location = false,
				}
				:map( function()
					return wfCatalog:docStore():applyOverlay {
							type = syncTerms.space_doc,
							_id = space_doc_id,
							space = {
								id = spaceId,
							},
							[ docType ] = {
								id = docId,
							}
						}
						:map( function()
							return spaceId
						end )
				end )

		end )

	return result

end

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

function LrmCatCollaboration.unshareAssetOrAlbum( wfCatalog, docType, docId )

	local syncTerms = getSyncTerms( docType )

	return wfCatalog:indexStore():queryOnce{
		sql = syncTerms.fetchSql,
		quiesceUntilUpToDate = true,
		docId }
		:flatMap( function( idRows )

			local spaceId = KSTableUtils.derefOpt( idRows, 1, 2 )

			if not spaceId then
				return Rx.Observable.throw( docType .. " is not shared" )
			end

			return wfCatalog:docStore():applyOverlay {
					type = 'space',
					_id = spaceId,
					private = true,
				}
				:map( function()
					return true
				end )

		end )

end

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

local function sharedAlbumUrl_imp( docStore, spaceId )

	return docStore:ongoingDocValue( spaceId )
		:map( function ( docValue )

			return KSTableUtils.derefOpt( docValue, 'links', '/rels/web_space', 'href' )
				or KSTableUtils.derefOpt( docValue, '_serverProvidedMetadata',
													'links', '/rels/web_space', 'href' )

		end )

end

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

function LrmCatCollaboration.ongoingSharedAssetOrAlbumUrl( wfCatalog, docType, docId, optSpaceId )

	local syncTerms = getSyncTerms( docType )

	if optSpaceId then
		return sharedAlbumUrl_imp( wfCatalog:docStore(), optSpaceId )
	end

	return wfCatalog:indexStore():query{ sql = syncTerms.fetchSql, docId }
		:flatMap( function( idRows )

			local spaceId = KSTableUtils.derefOpt( idRows,  1, 2 )
			if spaceId then
				return sharedAlbumUrl_imp( wfCatalog:docStore(), spaceId )
			else
				local result = Rx.ReplaySubject( 1 )
				result:onNext( nil )

				return result
			end

		end )

end

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

function LrmCatCollaboration.sharedAssetOrAlbumUrl( wfCatalog, docType, docId, nonNilResult )

	local syncTerms = getSyncTerms( docType )

	if nonNilResult then
		return LrmCatCollaboration.ongoingSharedAssetOrAlbumUrl( wfCatalog, docType, docId )
			:filter( function( result )
				return result ~= nil
			end )
			:take( 1 )

	else
		return wfCatalog:indexStore():queryOnce{
			sql = syncTerms.fetchSql,
			quiesceUntilUpToDate = true,
			docId }
			:flatMap( function ( status, idRows )

				local spaceId = KSTableUtils.derefOpt( idRows, 1, 2 )
				if spaceId then
					return wfCatalog:docStore():docValue( spaceId )
						:pluck( 'links', '/rels/web_space', 'href' )
				else
					return Rx.Observable.just( nil )
				end
			end )

	end

end

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

function LrmCatCollaboration.resolveAlbumFromSpace( wfCatalog, spaceId )

	return wfCatalog:indexStore():queryOnce{
			sql = [[SELECT albumId FROM space_album WHERE spaceId == ?]],
			singleColumnQuery = true,
			quiesceUntilUpToDate = true,
			spaceId
		}
		:pluck( 1 )

end

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

function LrmCatCollaboration.shareAlbum( wfCatalog, albumId, spaceSubtype )
	-- NOTE: spaceSubtype does not seem to be used
	return LrmCatCollaboration.shareAssetOrAlbum( wfCatalog, 'album', albumId, spaceSubtype )

end

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

function LrmCatCollaboration.unshareAlbum( wfCatalog, albumId )

	return LrmCatCollaboration.unshareAssetOrAlbum( wfCatalog, 'album', albumId )

end

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

function LrmCatCollaboration.sharedAlbumUrl( wfCatalog, albumId, nonNilResult )

	return LrmCatCollaboration.sharedAssetOrAlbumUrl( wfCatalog, 'album', albumId, nonNilResult )

end

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

local function collectComments( rows )
	local result = {}

	for i, row in ipairs( rows ) do
		result[ #result + 1 ] = formatComment( row )
	end

	return result
end

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

function LrmCatCollaboration.getComments( wfCatalog )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT DISTINCT comment.docId, "space", 0, "catalog", 0, ]] .. kCommentFields .. [[
					FROM
						comment
					INNER JOIN
						album_asset
					ON
						comment.assetId == album_asset.assetId
					INNER JOIN
						space_album
					ON
						space_album.albumId == album_asset.albumId
					LEFT JOIN
						space
					ON
						space.docId == comment.spaceId
					LEFT JOIN
						feedback_user fu
					ON
						fu.userId == comment.author_id
					WHERE
						comment.spaceId == space_album.spaceId
				]],
			calm = 0.25 }
		:map( collectComments )

end

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

function LrmCatCollaboration.getCommentsForAlbum( wfCatalog, albumId )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT
						DISTINCT comment.docId, "space", 0, "catalog", 0, ]] .. kCommentFields .. [[
					FROM
						comment
					LEFT JOIN
						space
					ON
						space.docId == comment.spaceId
					LEFT JOIN
						album_asset
					ON
						album_asset.assetId == comment.assetId
					LEFT JOIN
						feedback_user fu
					ON
						fu.userId == comment.author_id
					WHERE
						album_asset.albumId == ?
					AND
						comment.assetId
					NOT IN (
						SELECT
							docId
						FROM
							trashAssets )
					AND
						comment.spaceId
					IN (
						SELECT
							spaceId
						FROM
							space_album
						WHERE
							space_album.albumId == ? ) ]],
			calm = 0.25,
			albumId,
			albumId }
		:map( collectComments )

end

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

function LrmCatCollaboration.getCommentsOnAlbumAsset( wfCatalog, albumId, assetId )

	return wfCatalog:indexStore():query{
				sql = [[
						SELECT
							comment.docId, "space", 0, "catalog", 0, ]] .. kCommentFields .. [[
						]] .. kCommentOnSpaceByAlbumClause,
				calm = 0.25,
				assetId,
				albumId }
	:map( collectComments )

end

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

local function collectFavorites( rows )

	local result = {}
	for _, row in ipairs( rows ) do
		result[ #result + 1 ] = formatFavorite( row )
	end

	return result
end

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

function LrmCatCollaboration.getSpaceFavoritesOnAlbum( wfCatalog, albumId )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT
						spacefavorite.docId, "space", 0, "catalog", 0, ]] .. kFavoriteFields .. [[
					FROM
						spacefavorite
					LEFT JOIN
						space
					ON
						space.docId == spacefavorite.spaceId
					LEFT JOIN
						album_asset
					ON
						album_asset.assetId == spacefavorite.assetId
					LEFT JOIN
						feedback_user fu
					ON
						fu.userId == spacefavorite.author_id
					WHERE
						album_asset.albumId == ?
					AND
						spacefavorite.assetId NOT IN (SELECT docId FROM trashAssets)
					AND
						spacefavorite.spaceId IN (SELECT spaceId FROM space_album WHERE albumId == ?)]],
			calm = 0.25,
			albumId,
			albumId }

	:map( collectFavorites )

end

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

function LrmCatCollaboration.getSpaceFavorites( wfCatalog )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT DISTINCT spacefavorite.docId, "space", 0, "catalog", 0, ]] .. kFavoriteFields .. [[
					FROM
						spacefavorite
					LEFT JOIN
						space
					ON
						space.docId == spacefavorite.spaceId
					INNER JOIN
						album_asset
					ON
						spacefavorite.assetId == album_asset.assetId
					INNER JOIN
						space_album
					ON
						space_album.albumId == album_asset.albumId
					LEFT JOIN
						feedback_user fu
					ON
						fu.userId == spacefavorite.author_id
					WHERE
						spacefavorite.spaceId == space_album.spaceId
					]],
			calm = 0.25 }
		:map( collectFavorites )

end

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

function LrmCatCollaboration.getSpaceFavoritesOnAlbumAsset( wfCatalog, albumId, assetId )

	return wfCatalog:indexStore():query{
			sql = [[
					SELECT
						spacefavorite.docId, "space", 0, "catalog", 0, ]] .. kFavoriteFields .. [[
					FROM
						spacefavorite
					LEFT JOIN
						space
					ON
						space.docId == spacefavorite.spaceId
					LEFT JOIN
						feedback_user fu
					ON
						fu.userId == spacefavorite.author_id
					WHERE
						spacefavorite.assetId == ? AND spacefavorite.spaceId
					IN (
						SELECT
							spaceId
						FROM
							space_album
						WHERE
							space_album.albumId == ?
					)
					ORDER BY
						spacefavorite.created]],
			assetId, albumId }

	:map( collectFavorites )

end

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

function LrmCatCollaboration.isMySpaceFavorite( wfCatalog, assetId, albumId, userId )

	return wfCatalog:indexStore():queryOnce{
			sql = [[SELECT spaceId FROM space_album WHERE albumId == ?]],
			singleColumnQuery = true,
			quiesceUntilUpToDate = true,
			albumId
		}
		:pluck( 1 )
		:flatMap( function( spaceId )

			if not spaceId then
				local result = Rx.ReplaySubject( 1 )
				result:onNext( nil )

				return result
			end

			return wfCatalog:indexStore():query{
					sql = [[SELECT docId FROM spacefavorite WHERE assetId == ? AND
								spaceId == ? AND author_id == ? ]],
					singleColumnQuery = true,
					assetId,
					spaceId,
					userId
				 }
				:map( function( idRows )

					local spaceFavoriteId = KSTableUtils.derefOpt( idRows, 1 )

					return spaceFavoriteId and true or false

				end )

		end )

end

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

return LrmCatCollaboration
