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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 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.rxlua
local WFRxTestCase = require 'WFRxTestCase'

local WFCatCollaboration = require 'WFCatCollaboration'

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

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

	-- Lrmobile_models
local LrmCatAlbum = require 'LrmCatAlbum'

local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require "KSTableUtils"
local WFPlatformInfo = require 'WFPlatformInfo'


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

local LrmCatAlbumTest = {}

local TEST_TIMEOUT = 10

local catalogOptions = {
	indexDefinitions = 'LrmCatalogIndexDefinitions.lua'
}

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

function LrmCatAlbumTest.rxTestGetAlbumList( rxTest )

	rxTest:setTimeLimit( 3 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local function sortAlbumList( observable )

		return observable:map( function( rows )

			table.sort( rows, function( a, b)
				return a.albumId < b.albumId
			end )

			return rows

		end )

	end

	local albumId1 = '00000000000000000000000000000000'
	local albumId2 = '11111111111111111111111111111111'
	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	local assetId2 = KSIdGenerator.uuidInLowercaseHex()

	rxTest:waitFor(
		wfCatalog:docStore():bulkSetDocValue( {
			{
				_id = albumId1,
				type = 'album',
				subtype = 'collection',
				name = 'test 1',
				story = {
					title = 'Title Test 1'
				}
			},
			{
				_id = albumId2,
				type = 'album',
				subtype = 'collection',
				name = 'test 2',
				story = {
					title = 'Title Test 2'
				}
			},
			{
				_id = assetId1,
				type = 'asset',
				subtype = 'image',
				captureDate = "2014-11-02T11:10:09",
				importSource = {
					fileName = "fakeFile01.jpg",
					importTimestamp = "2015-11-02T08:37:24.513Z",
					originalHeight = 16,
					originalWidth = 16,
				},
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = {
							href = '/v2/1234/master',
						}
					}
				}
			},
			{
				_id = assetId2,
				type = 'asset',
				subtype = 'image',
				captureDate = "2018-11-02T11:10:09",
				importSource = {
					fileName = "fakeFile01.jpg",
					importTimestamp = "2015-11-02T08:37:24.513Z",
					originalHeight = 16,
					originalWidth = 16,
				},
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = {
							href = '/v2/1234/master',
						}
					}
				}
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'album_asset',
				asset = {
					id = assetId1,
				},
				album = {
					id = albumId1,
				}
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'album_asset',
				asset = {
					id = assetId2,
				},
				album = {
					id = albumId1,
				}
			}
		} ) )

	rxTest:expectSingleValue(
		sortAlbumList( LrmCatAlbum.getAlbumsList( wfCatalog, false ) ):take( 1 ),
		{
			{
				albumId = albumId1,
				assetCount = 0,
				coverId = "",
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 1",
				nameLC = "test 1",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 1'
			},
			{
				albumId = albumId2,
				assetCount = 0,
				coverId = "",
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 2",
				nameLC = "test 2",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 2'
			},
		}
	)

	rxTest:waitFor(
		WFCatCollaboration.shareAlbum( wfCatalog, albumId1, false, false, true )
	)

	local spaceId = rxTest:getFirstValue(
			wfCatalog:indexStore():queryOnce {
				sql = [[SELECT spaceId FROM space_album
						WHERE albumId = ?]],
				quiesceUntilUpToDate = true,
				singleColumnQuery = true,
				albumId1
			}
			:map( function( rows )
				return rows[ 1 ]
			end )
		)

	rxTest:expectSingleValue(
		sortAlbumList( LrmCatAlbum.getAlbumsList( wfCatalog, false ) ):take( 1 ),
		{
			{
				albumId = albumId1,
				assetCount = 0,
				coverId = "",
				globalSpaceId = spaceId,
				hideInOrganize = 0,
				isPrivate = true,
				isShared = true,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 1",
				nameLC = "test 1",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 1'
			},
			{
				albumId = albumId2,
				assetCount = 0,
				coverId = "",
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 2",
				nameLC = "test 2",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 2'
			},
		}
	)

	rxTest:waitFor(
		wfCatalog:docStore():applyOverlay {
			_id = KSIdGenerator.uuidInLowercaseHex(),
			type = 'comment',
			subtype = 'user',
			body = 'comment body',
			userCreated = "2015-11-02T08:37:24.513Z",
			space = { id = spaceId },
			asset = { id = assetId1 },
		}
	)

	rxTest:waitFor(
		wfCatalog:docStore():applyOverlay {
			_id = KSIdGenerator.uuidInLowercaseHex(),
			type = 'favorite',
			subtype = 'user',
			userCreated = "2017-11-02T08:37:24.513Z",
			space = { id = spaceId },
			asset = { id = assetId1 },
		}
	)

	rxTest:expectSingleValue(
		sortAlbumList( LrmCatAlbum.getAlbumsList( wfCatalog, true ) ):take( 1 ),
		{
			{
				albumId = albumId1,
				assetCount = 2,
				coverId = "",
				globalSpaceId = spaceId,
				hideInOrganize = 0,
				isPrivate = true,
				isShared = true,
				lastActivityTimestamp = "2017-11-02T08:37:24.513Z",
				localTimestamp = "",
				name = "test 1",
				nameLC = "test 1",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 1'
			},
			{
				albumId = albumId2,
				assetCount = 0,
				coverId = "",
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 2",
				nameLC = "test 2",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 2'
			},
		}
	)

	-- WF-1631 Duplicate albums due to LrW space with subtype 'gallery'

	local gallerySpaceId = KSIdGenerator.uuidInLowercaseHex()

	rxTest:waitFor(
		wfCatalog:docStore():bulkSetDocValue( {
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'space_album',
				space = {
					id = gallerySpaceId
				},
				album = {
					id = albumId1
				}
			},
			{
				_id = gallerySpaceId,
				type = 'space',
				subtype = 'gallery',
				download = false,
				favorites_disabled = true,
				userUpdated = "2019-10-31T22:24:58.114000Z",
				gallery_type = "public_profile",
				profile_info = {
					author = "Dragics",
				},
				metadata = false,
				location = false,
				private = false,
				comments_disabled = true,
				userCreated = "2019-10-31T22:24:58.114000Z",
			}
		} )
	)

	rxTest:expectSingleValue(
		sortAlbumList( LrmCatAlbum.getAlbumsList( wfCatalog, true ) ):take( 1 ),
		{
			{
				albumId = albumId1,
				assetCount = 2,
				coverId = "",
				globalSpaceId = spaceId,
				hideInOrganize = 0,
				isPrivate = true,
				isShared = true,
				lastActivityTimestamp = "2017-11-02T08:37:24.513Z",
				localTimestamp = "",
				name = "test 1",
				nameLC = "test 1",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 1'
			},
			{
				albumId = albumId2,
				assetCount = 0,
				coverId = "",
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 2",
				nameLC = "test 2",
				parentId = "root",
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
				title = 'Title Test 2'
			},
		}
	)

end

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

function LrmCatAlbumTest.rxTestDeleteAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "RootFolder", "root", true, false ),
		':ignore:' )

	local albumIds = {}

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows )
			return #albumIds
		end ),
		1 )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Album1", albumIds[ 1 ], false, false ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql =  "SELECT docId FROM albums WHERE parentId != 'root'" ,
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows )
			return #albumIds
		end ),
		1 )

	rxTest:expectSingleValue( WFCatCollaboration.shareAlbum( wfCatalog, albumIds[ 1 ], false, false, true ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )

	rxTest:expectSingleValue( LrmCatAlbum.deleteAlbum( wfCatalog, albumIds[ 1 ] ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM space_album',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		0 )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
 		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			return #albumIds
		end ),
		1 )

end


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

function LrmCatAlbumTest.rxTestCreateAlbumWithParent( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "RootFolder", "root", true, false ),
		':ignore:' )

	local albumIds

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows )
			return #albumIds
		end ),
		1 )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Album1", albumIds[ 1 ], false, false ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT parentId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( KSTableUtils.flatten ),
		{ 'root', albumIds[ 1 ] } )

end

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

function LrmCatAlbumTest.rxTestReparentAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "RootFolder", "root", true, false ),
		':ignore:' )

	local albumIds = {}
	local rootId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows)
			rootId = albumIds[1]
			return #albumIds
		end ),
		1 )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "A Folder", rootId, true, false ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Another Folder", rootId, true, false ),
		':ignore:' )

	local folderId1
	local folderId2

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM albums WHERE parentId != 'root'",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows )
			folderId1 = albumIds[1]
			folderId2 = albumIds[2]
			return #albumIds
		end ),
		2 )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Album1", folderId1, false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM albums WHERE subtype == 'collection'",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			albumIds = KSTableUtils.flatten( rows)
			albumId = albumIds[1]
			return #albumIds
		end ),
		1 )

	local userUpdated

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( docValue )
			userUpdated = docValue.userUpdated

			return userUpdated ~= nil
		end ),
		true )

	rxTest:expectSingleValue( LrmCatAlbum.reparentAlbum( wfCatalog, albumId, folderId2 ),
		':ignore:' )

	local parentIds

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT parentId FROM albums WHERE docId = ?",
			quiesceUntilUpToDate = true,
			albumId
		}
		:map( function( rows )
			parentIds = KSTableUtils.flatten( rows )
			return parentIds
		end ),
		{
			folderId2
		} )

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( docValue )
			return userUpdated ~= docValue.userUpdated
		end ),
		true )

end

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

function LrmCatAlbumTest.rxTestReparentOnItself( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Album1", "root", false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[1]

			return albumId
		end ),
		':ignore:' )

	rxTest:expectOnlyError( LrmCatAlbum.reparentAlbum( wfCatalog, albumId, albumId ),
		'Album can not be its own parent' )

end

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

function LrmCatAlbumTest.rxTestRenameAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "Album1", "root", false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[1]

			return albumId
		end ),
		':ignore:' )

	local userUpdatedTimestamp

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( docValue )
			userUpdatedTimestamp = docValue.userUpdated

			return userUpdatedTimestamp
		end ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.renameAlbum( wfCatalog, albumId, 'album2' ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( albumId )
		:map( function( docValue )
			return docValue.name == 'album2' and userUpdatedTimestamp ~= docValue.userUpdated
		end ),
		true )

end

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

function LrmCatAlbumTest.rxTestShareAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[1]

			return albumId
		end ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local spaceIds = KSTableUtils.flatten( rows )

			return #spaceIds

		end ),
		1 )
end

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

function LrmCatAlbumTest.rxTestUnshareAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM albums',
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[1]
			return #albumIds
		end ),
		1 )


	rxTest:expectSingleValue(  LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	local spaceIds

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )

	rxTest:expectOnlyCompleted( LrmCatAlbum.unshareAlbum( wfCatalog, albumId ) )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT allowInviteRequests FROM space where docId = ?",
			quiesceUntilUpToDate = true,
			spaceIds[ 1 ] }
		:map( function( rows )
			local allows = KSTableUtils.flatten( rows )
			return allows[ 1 ] or false
		end ),
		false )

end

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

function LrmCatAlbumTest.rxTestInviteByEmail( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql =  "SELECT docId FROM albums",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[ 1 ]
			return albumId
		end ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )

	local email = 'tester.lightroom@gmail.com'

	rxTest:expectSingleValue( LrmCatAlbum.inviteByEmail( wfCatalog, albumId , email ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT email FROM space_invite",
			quiesceUntilUpToDate = true
		}
		:pluck( 1, 1 ),
		email )

end

function LrmCatAlbumTest.rxTestGetInvites( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql =  "SELECT docId FROM albums",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[ 1 ]
			return albumId
		end ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )


	local email = 'tester.lightroom@gmail.com'

	rxTest:expectSingleValue( LrmCatAlbum.inviteByEmail( wfCatalog, albumId, email ),
		':ignore:' )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT email FROM space_invite",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local emails = KSTableUtils.flatten( rows )
			return #emails
		end ),
		1 )

end

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

local function createFeedbackUser( i )
	return {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'feedbackuser',

		_serverProvidedMetadata = {
			full_name = string.format( 'Full Name %s', tostring( i ) ),
			email = string.format( 'person_%s@gmail.com', tostring( i ) ),
		},
	}
end

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

function LrmCatAlbumTest.rxTestGetAccessRequests( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local feedbackUser = createFeedbackUser(1)

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql =  "SELECT docId FROM albums",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[ 1 ]
			return #albumIds
		end ),
		1 )

	rxTest:expectSingleValue( LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	local spaceIds = {}

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			_id = KSIdGenerator.uuidInLowercaseHex(),
			type = 'spaceinvite',
			subtype = 'request',

			userCreated = "2015-05-19T07:09:11.392772Z",
			userUpdated = "2015-05-19T07:09:11.392772Z",
			space = {
				id = spaceIds[1],
			},
			user = {
				id = feedbackUser._id,
			},

			status = "open",
			role = "viewer",
		},
		':ignore:' )

	local accessObs = rxTest:observe( LrmCatAlbum.getAccessRequests( wfCatalog, albumId ) )
	rxTest:finally( function()
		accessObs:dispose()
	end )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT space_invite.userId from space_invite where space_invite.subtype == 'request'" ,
			quiesceUntilUpToDate = true
		}
		:map( KSTableUtils.flatten ),
		{ feedbackUser._id } )

	accessObs:expectData(
		{ } )

end

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

function LrmCatAlbumTest.rxTestGetCollaborators( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local feedbackUser = createFeedbackUser(1)

	rxTest:expectSingleValue( LrmCatAlbum.createAlbum( wfCatalog, "ShareAlbum", 'empty', false, false ),
		':ignore:' )

	local albumId

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql =  "SELECT docId FROM albums",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			local albumIds = KSTableUtils.flatten( rows )
			albumId = albumIds[ 1 ]
			return albumId
		end ),
		':ignore:' )

	rxTest:expectSingleValue( LrmCatAlbum.shareGroupAlbum( wfCatalog, albumId, nil, true ),
		':ignore:' )

	local spaceIds

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT docId FROM space_album",
			quiesceUntilUpToDate = true
		}
		:map( function( rows )
			spaceIds = KSTableUtils.flatten( rows )
			return #spaceIds
		end ),
		1 )

	rxTest:expectSingleValue( wfCatalog:docStore():applyOverlay {
			_id = KSIdGenerator.uuidInLowercaseHex(),
			type = 'space_user',
			space = {
				id = spaceIds[1],
			},
			user = {
				id = feedbackUser._id,
			},

			status = "accepted",
			role = "viewer",
		},
		':ignore:' )

	local collabObs = rxTest:observe( LrmCatAlbum.getCollaborators( wfCatalog, albumId ) )
	rxTest:finally( function()
		collabObs:dispose()
	end )

	rxTest:expectSingleValue( wfCatalog:indexStore():queryOnce{
			sql = "SELECT space_user.userId from space_user ",
			quiesceUntilUpToDate = true
		}
		:map( KSTableUtils.flatten ),
		{ feedbackUser._id } )

	collabObs:expectData( { } )

end

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

function LrmCatAlbumTest.rxTestGetAlbumListWithSharedWithYouAlbums( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local function sortAlbumList( observable )
		return observable:map( function( rows )
			table.sort( rows, function( a, b)
				return a.albumId < b.albumId
			end )
			return rows
		end )
	end

	local albumId1 = '00000000000000000000000000000000'
	local albumId2 = '11111111111111111111111111111111'
	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	local assetId2 = KSIdGenerator.uuidInLowercaseHex()

	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceUserDocId = KSIdGenerator.uuidInLowercaseHex()
	local spaceUserDocId2 = KSIdGenerator.uuidInLowercaseHex()
	
	local ownerId = KSIdGenerator.uuidInLowercaseHex()
	local ownerName = "Spongebob Squarepants"

	local spaceFeedbackUserId = KSIdGenerator.uuidInLowercaseHex()
	local spaceFeedbackUserName = "Oolongbob Tieguanyinpants"
	local spaceFeedbackUserId2 = KSIdGenerator.uuidInLowercaseHex()
	local spaceFeedbackUserName2 = "Observer User"

	rxTest:expectSingleValue(
		wfCatalog:docStore():bulkSetDocValue( {
			{
				_id = albumId1,
				_spaceId = spaceId,
				type = 'album',
				subtype = 'collection',
				name = 'test 1',
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'space_album',
				space = {
					id = spaceId,
				},
				album = {
					id = albumId1,
				}
			},
			{
				_id = albumId2,
				_spaceId = spaceId,
				type = 'album',
				subtype = 'collection',
				name = 'test 2',
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				type = 'space_album',
				space = {
					id = spaceId,
				},
				album = {
					id = albumId2,
				}
			},
			{
				_id = ownerId,
				type = 'feedbackuser',

				_serverProvidedMetadata = {
					full_name = ownerName,
					email = string.format( '%s@gmail.com', ownerName ),
					links = {
						[ '/rels/avatars/medium' ] = {
							href = '/v2/dummy_avatar_medium',
						},
						[ '/rels/avatars/large' ] = {
							href = '/v2/dummy_avatar_large',
						},
					},
				},
			},
			{
				_id = spaceFeedbackUserId,
				type = 'feedbackuser',

				_serverProvidedMetadata = {
					full_name = spaceFeedbackUserName,
					email = string.format( '%s@gmail.com', spaceFeedbackUserName ),
					links = {
						[ '/rels/avatars/medium' ] = {
							href = '/v2/dummy_avatar_medium',
						},
						[ '/rels/avatars/large' ] = {
							href = '/v2/dummy_avatar_large',
						},
					},
				},
			},
			{
				_id = spaceFeedbackUserId2,
				type = 'feedbackuser',

				_serverProvidedMetadata = {
					full_name = spaceFeedbackUserName2,
					email = string.format( '%s@gmail.com', spaceFeedbackUserName ),
					links = {
						[ '/rels/avatars/medium' ] = {
							href = '/v2/dummy_avatar_medium',
						},
						[ '/rels/avatars/large' ] = {
							href = '/v2/dummy_avatar_large',
						},
					},
				},
			},
			{
				_id = spaceUserDocId,
				type = 'space_user',
				space = {
					id = spaceId
				},
				user = {
					id = spaceFeedbackUserId
				},
				status = "accepted",
				role = "viewer",
			},
			-- add observer role in space
			{
				_id = spaceUserDocId2,
				type = 'space_user',
				space = {
					id = spaceId
				},
				user = {
					id = spaceFeedbackUserId2
				},
				status = "accepted",
				role = "observer",
			},
			{
				_id = assetId1,
				_spaceId = spaceId,
				type = 'asset',
				subtype = 'image',
				captureDate = "2014-11-02T11:10:09",
				importSource = {
					fileName = "fakeFile01.jpg",
					importTimestamp = "2015-11-02T08:37:24.513Z",
					originalHeight = 16,
					originalWidth = 16,
				},
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = {
							href = '/v2/1234/master',
						}
					}
				}
			},
			{
				_id = assetId2,
				_spaceId = spaceId,
				type = 'asset',
				subtype = 'image',
				captureDate = "2018-11-02T11:10:09",
				importSource = {
					fileName = "fakeFile01.jpg",
					importTimestamp = "2015-11-02T08:37:24.513Z",
					originalHeight = 16,
					originalWidth = 16,
				},
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = {
							href = '/v2/1234/master',
						}
					}
				}
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				_spaceId = spaceId,
				type = 'album_asset',
				asset = {
					id = assetId1,
				},
				album = {
					id = albumId1,
				}
			},
			{
				_id = KSIdGenerator.uuidInLowercaseHex(),
				_spaceId = spaceId,
				type = 'album_asset',
				asset = {
					id = assetId2,
				},
				album = {
					id = albumId1,
				}
			},
			{
				_id = spaceId,
				type = 'space',
				subtype = 'shared',
				download = true,
				metadata = false,
				location = true,
				comments_disabled = false,
				favorites_disabled = true,
				_serverProvidedMetadata = {
					owner = {
						id = ownerId
					},
					this_space_user = {
						id = spaceUserDocId2,
						user = {
							id = spaceFeedbackUserId2
						}
					}
				}
			},
			{
				_id = spaceId,
				type = 'space',
				subtype = 'shared',
				download = true,
				metadata = false,
				location = true,
				comments_disabled = false,
				favorites_disabled = true,
				_serverProvidedMetadata = {
					owner = {
						id = ownerId
					},
					this_space_user = {
						id = spaceUserDocId,
						user = {
							id = spaceFeedbackUserId
						}
					}
				}
			},
		}), ':ignore:' )

	rxTest:expectSingleValue(
		sortAlbumList( LrmCatAlbum.getAlbumsList( wfCatalog, false ) ):take( 1 ),
		{
			{
				albumId = albumId1,
				assetCount = 0,
				coverId = "",
				globalSpaceId = spaceId,
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 1",
				nameLC = "test 1",
				parentId = "root",
				ownerInfo = {
					id = ownerId,
					name = ownerName,
					outOfQuota = false,
					subscriptionLapsed = false,
				},
				spaceFeedbackUserId = spaceFeedbackUserId,
				permissions = {
					role = "viewer",
				},
				shareSettings = {
					allowComments = true,
					allowDownloads = true,
					allowFavorites = false,
					showLocation = true,
					showMetadata = false,
				},
				spaceId = spaceId,
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
			},
			{
				albumId = albumId2,
				assetCount = 0,
				coverId = "",
				globalSpaceId = spaceId,
				hideInOrganize = 0,
				isPrivate = false,
				isShared = false,
				lastActivityTimestamp = "",
				localTimestamp = "",
				name = "test 2",
				nameLC = "test 2",
				parentId = "root",
				ownerInfo = {
					id = ownerId,
					name = ownerName,
					outOfQuota = false,
					subscriptionLapsed = false,
				},
				spaceFeedbackUserId = spaceFeedbackUserId,
				permissions = {
					role = "viewer",
				},
				shareSettings = {
					allowComments = true,
					allowDownloads = true,
					allowFavorites = false,
					showLocation = true,
					showMetadata = false,
				},
				spaceId = spaceId,
				subType = "collection",
				updateTimestamp = "",
				useOffline = 0,
			},
		}
	)

end

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

function LrmCatAlbumTest.rxTestIsValidAlbum( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	local albumId = 'notFooBar'

	rxTest:expectSingleValue(
	wfCatalog:docStore():bulkSetDocValue( {
		{
			_id = albumId,
			_spaceId = 'notspacebar',
			type = 'album',
			subtype = 'collection',
			name = 'test 1',
		}

	}), ':ignore:')

	-- fails with bad id
	rxTest:expectSingleValue(
		LrmCatAlbum.isValidAlbum( wfCatalog, 'foobar'):take(1),
		false)

	-- succeeds with valid id
	rxTest:expectSingleValue(
		LrmCatAlbum.isValidAlbum( wfCatalog, albumId):take(1),
		true)
end

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

function LrmCatAlbumTest.rxTestTouchAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSpecificError("LrmCatAlbum.touchAlbum: The albumId must be a non-empty string",
								function() LrmCatAlbum.touchAlbum(wfCatalog, "") end)


	-- Create a basic album with a timestamp

	local albumId = 'notFooBar'
	local testNowTimestamp = KSDate.timeInIso8601Format( nil, false )

	rxTest:expectSingleValue(
	wfCatalog:docStore():bulkSetDocValue( {
		{
			_id = albumId,
			_spaceId = 'notspacebar',
			type = 'album',
			subtype = 'collection',
			name = 'test 1',
			_localOnly = {
				updateTimeStamp = testNowTimestamp
			}
		}

	}), ':ignore:')


	-- Touch

	rxTest:waitFor(LrmCatAlbum.touchAlbum( wfCatalog, albumId ))


	rxTest:expectSingleValue(
		wfCatalog:docStore():docValue( albumId )
			:map( function( updatedAlbumDocValue )
				local updateTimeStamp = KSTableUtils.derefOpt( updatedAlbumDocValue, '_localOnly', 'updateTimeStamp' )
				local userUpdated = KSTableUtils.derefOpt( updatedAlbumDocValue, 'userUpdated' )
				return updateTimeStamp and userUpdated and testNowTimestamp < updateTimeStamp and testNowTimestamp < userUpdated
			end ), true
	)

end

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

function LrmCatAlbumTest.rxTestConvertAlbumToAdHoc( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSpecificError("LrmCatAlbum:convertAlbumToAdhoc: The albumId must be a non-empty string",
								function() LrmCatAlbum.convertAlbumToAdhoc(wfCatalog, "") end)

	local albumId = 'notFooBar'
	local testNowTimestamp = KSDate.timeInIso8601Format( nil, false )

	rxTest:expectSingleValue(
	wfCatalog:docStore():bulkSetDocValue( {
		{
			_id = albumId,
			_spaceId = 'notspacebar',
			type = 'album',
			subtype = 'collection',
			name = 'test 1',
			hideInOrganize = false,
			parent = {
				_id ='asdf',
				_delete = false --nonsense, adding presence for explicitness
			},
			_localOnly = {
				updateTimeStamp = testNowTimestamp
			}
		}

	}), ':ignore:')

	rxTest:waitFor( LrmCatAlbum.convertAlbumToAdhoc( wfCatalog, albumId ).value )

	rxTest:expectSingleValue(
		wfCatalog:docStore():docValue( albumId )
			:map( function( convertedAlbum )
				local isAdhoc = KSTableUtils.derefOpt( convertedAlbum, 'hideInOrganize' )
				local updateTimestamp = KSTableUtils.derefOpt( convertedAlbum, '_localOnly', 'updateTimeStamp' )

				--	SPAM: 2021-08-02 from OZ API docs: DEPRECATION NOTE In 2018, we will be
				--	converting all adhoc albums (which were a web-only feature)
				--	into collection albums with the payload.hideInOrganize field set to true.

				-- Test that original timestamp is less than updatedAlbumTimestamp
				-- relies on sortability of iso8601 format
				return isAdhoc and updateTimestamp and testNowTimestamp < updateTimestamp
			end ), true
	)

end

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

function LrmCatAlbumTest.rxTestConvertAdHocToAlbum( rxTest )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	rxTest:expectSpecificError("LrmCatAlbum:convertAdhocToAlbum: The albumId must be a non-empty string",
								function() LrmCatAlbum.convertAdhocToAlbum(wfCatalog, "") end)

	local albumId = 'notFooBar'
	local testNowTimestamp = KSDate.timeInIso8601Format( nil, false )

	rxTest:expectSingleValue(
	wfCatalog:docStore():bulkSetDocValue( {
		{
			_id = albumId,
			_spaceId = 'notspacebar',
			type = 'album',
			subtype = 'collection',
			name = 'test 1',
			hideInOrganize = true,
			_localOnly = {
				updateTimeStamp = testNowTimestamp
			}
		}

	}), ':ignore:')

	rxTest:waitFor(LrmCatAlbum.convertAdhocToAlbum( wfCatalog, albumId ).value)

	rxTest:expectSingleValue(
		wfCatalog:docStore():docValue( albumId )
			:map( function( convertedAlbum )
				-- NOTE: An album is an album if hideInOrganize is false

				local isAlbum = not KSTableUtils.derefOpt( convertedAlbum, 'hideInOrganize' )
				local updateTimestamp = KSTableUtils.derefOpt( convertedAlbum, '_localOnly', 'updateTimeStamp' )

				--	SPAM: 2021-08-02 from OZ API docs: DEPRECATION NOTE In 2018, we will be
				--	converting all adhoc albums (which were a web-only feature)
				--	into collection albums with the payload.hideInOrganize field set to true.

				-- Test that original timestamp is less than updatedAlbumTimestamp
				-- relies on sortability of iso8601 format
				return isAlbum and updateTimestamp and testNowTimestamp < updateTimestamp
			end ), true
	)

end

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

if WFPlatformInfo.isDesktop then
	return {}
else
	return WFRxTestCase.wrapSuite( 'LrmCatAlbumTest',  LrmCatAlbumTest )
end
