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

ADOBE CONFIDENTIAL
------------------
Copyright 2025 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
and its suppliers and are protected by all applicable intellectual
property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material
is strictly forbidden unless prior written permission is obtained
from Adobe.

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

	-- WF.core
local KSData = require 'KSData'
local KSIdGenerator = require 'KSIdGenerator'
local KSPathUtils = require 'KSPathUtils'
local KSTableUtils = require 'KSTableUtils'
local WFFileUtils = require 'WFFileUtils'
local WFPlatformInfo = require 'WFPlatformInfo'
local WFRxTestCase = require 'WFRxTestCase'

	-- WF.oz_client.managed_catalog
local WFCatTestSession = require 'WFCatTestSession'
local WFCatOrbit = require 'WFCatOrbit'

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

local WFCatOrbitTest = {}

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

-- Grab your user Auth Token from https://senseiui.cloud.adobe.io/workflows in
-- order to run the autotag tests. We could avoid this, and enable the autotag
-- tests permanently if our IMS clients had the appropriate scopes.

local kAuthToken = [[TOKEN GOES HERE]]

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

local testDataPath = KSPathUtils.child(
		KSPathUtils.getStandardFilePath( 'testdata' ), "import-samples" )

local testDataFolder1 = KSPathUtils.child( testDataPath, "folder-1" )

local fileName = "small.JPG"
local filePath = KSPathUtils.child( testDataFolder1, fileName )

local testMetaCAFDataPath = KSPathUtils.child(
		KSPathUtils.getStandardFilePath( 'testdata' ), "metacaf" )

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

local function extractSenseiEngineAndFirstResult( parts, headers )

	if headers and headers.status_code and
			tonumber( headers.status_code ) == 200 then

		local tag = KSTableUtils.derefOpt(
				parts, 2, 'data', 'classifications', 1, 'classification_entries', 1, 'class_label' ) or {}

		return tag

	end

end

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

function WFCatOrbitTest.rxTestGroupInputs( rxTest )

	local function verifyGrouping( inputs, maxInputsPerGroup, expectedGroups )

		local groups = WFCatOrbit._DEBUG_groupInputs( inputs, maxInputsPerGroup )
		rxTest:expectEqual( #expectedGroups, #groups )

		for i, group in ipairs( groups ) do

			rxTest:expectEqual( #expectedGroups[ i ], #group )

			for j, item in ipairs( group ) do
				rxTest:expectEqual( expectedGroups[ i ][ j ], item )
			end

		end

	end

	-- Test case 1: Empty input
	verifyGrouping( {}, 5, {} )

	-- Test case 2: Input size less than max group size
	verifyGrouping( { 1, 2, 3 }, 5, { { 1, 2, 3 } } )

	-- Test case 3: Input size equal to max group size
	verifyGrouping( { 1, 2, 3, 4, 5 }, 5, { { 1, 2, 3, 4, 5 } } )

	-- Test case 4: Input size greater than max group size
	verifyGrouping( { 1, 2, 3, 4, 5, 6 }, 5, { { 1, 2, 3, 4, 5 }, { 6 } } )

	-- Test case 5: Input size multiple of max group size
	verifyGrouping( { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 5, { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 } } )

	-- Test case 6: Input size not multiple of max group size
	verifyGrouping( { 1, 2, 3, 4, 5, 6, 7 }, 5, { { 1, 2, 3, 4, 5 }, { 6, 7 } } )

end

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

function WFCatOrbitTest.DISABLED_rxTestRequestAutotagsForImage_testNightly( rxTest )

	-- To run this test, set kAuthToken and remove the "DISABLED_" prefix.

	rxTest:setTimeLimit( 10 )

	rxTest:expectSingleValue( WFCatOrbit.requestAutotagsForImage_experimental(
			kAuthToken, filePath, 10 )

		:map( extractSenseiEngineAndFirstResult ), 'sea' )

end

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

function WFCatOrbitTest.DISABLED_rxTestRequestCategoryAutotagsForImage_testNightly( rxTest )

	-- To run this test, set kAuthToken and remove the "DISABLED_" prefix.

	rxTest:setTimeLimit( 10 )

	rxTest:expectSingleValue( WFCatOrbit.requestCategoryAutotagsForImage_experimental(
			kAuthToken, filePath, 10 )

		:map( extractSenseiEngineAndFirstResult ), 'Landscapes' )

end

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

local function setUpTestCatalog( rxTest )

	local maybeIndexDefinitionsFile = WFPlatformInfo.isMobile() and 'LrmCatalogIndexDefinitions.lua' or nil
	local catalogTestSession = WFCatTestSession( rxTest, nil, maybeIndexDefinitionsFile )
	local catalog = rxTest:getFirstValue( catalogTestSession:catalog() )

	rxTest:finally( function()

		catalogTestSession:close()

	end )

	return catalog

end

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

local function testRequestExpand( rxTest, manuallyVerify )

	rxTest:setTimeLimit( 30 )

	local wfCatalog = setUpTestCatalog( rxTest )

	local size = {
		height = 1024,
		width = 2048
	}

	local optArgs = {
		shift = -1,
		dilate = 0.1,
		prompt = "A futuristic city with a busy port. Photorealistic."
	}

	rxTest:expectSingleValue( WFCatOrbit.requestExpand( wfCatalog, filePath, size, optArgs )

		:map( function( parts, headers )

			local statusCode = KSTableUtils.derefOpt( headers, 'status_code' )
			local hasPartWithImageContent = false

			if statusCode == '200' then

				local testRunResultsPath

				if manuallyVerify then

					local resultsPath = KSPathUtils.child(
							KSPathUtils.getStandardFilePath( 'desktop' ), "expand" )

					testRunResultsPath = KSPathUtils.child(
							resultsPath, KSIdGenerator.uuidInLowercaseHex() )

					WFFileUtils.mkdir( testRunResultsPath )

				end

				for i, part in ipairs( parts ) do

					local partHeadersContentType = KSTableUtils.derefOpt( part, 'headers', 'Content-Type' )

					if partHeadersContentType == 'image/jpeg' then

						hasPartWithImageContent = true

						if manuallyVerify then

							local resultImagePath = KSPathUtils.child( testRunResultsPath, string.format( "expand_%d.jpg", i ) )

							local imgData = KSData.make( part.data )
							KSData.saveToFile( imgData, resultImagePath )
							imgData:close()

						end

					end

				end

			end

			return statusCode, hasPartWithImageContent

		end ), '200', true )

end

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

-- FIXME [jandrew 2025-03-11]: Disabled due to instability.

function WFCatOrbitTest.DISABLED_rxTestRequestExpand_testNightly( rxTest )

	testRequestExpand( rxTest )

end

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

function WFCatOrbitTest.DISABLED_rxTestRequestExpand_ManuallyVerifyResults_testNightly( rxTest )

	-- When enabled, this test case will write generated output images to the Desktop.
	-- This is useful for manually verifying the results, but not much else.

	local verifyManually = true

	testRequestExpand( rxTest, verifyManually )

end

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

local function requestFillHoleWithMetaCAF( rxTest, inputImage, inputMask, manuallyVerify )

	rxTest:setTimeLimit( 30 )

	local wfCatalog = setUpTestCatalog( rxTest )

	local _, _, extn = string.find( inputImage, ".+%.([^.]+)" )
	extn = string.lower( extn )

	local optArgs = {
		stage = true
	}

	rxTest:expectSingleValue( WFCatOrbit.requestFillHoleWithMetaCAF_experimental(
			wfCatalog, inputImage, inputMask, optArgs )

		:map( function( fileData )

			if manuallyVerify then

				local resultsPath = KSPathUtils.child(
						KSPathUtils.getStandardFilePath( 'desktop' ), "metacaf" )

				local testRunResultsPath = KSPathUtils.child(
						resultsPath, KSIdGenerator.uuidInLowercaseHex() )

				WFFileUtils.mkdir( testRunResultsPath )

				local resultImagePath = KSPathUtils.child( testRunResultsPath,
						string.format( "expand_%s.%s", KSIdGenerator.uuidInLowercaseHex(), extn ) )

				local imgData = KSData.make( fileData )
				KSData.saveToFile( imgData, resultImagePath )
				imgData:close()

			end

			return not not fileData

		end ), true )

end

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

function WFCatOrbitTest.rxTestRequestFillHoleWithMetaCAF_jxl_testNightly( rxTest )

	local fileName = "source.jxl"
	local inputImage = KSPathUtils.child( testMetaCAFDataPath, fileName )

	local maskFileName = "mask.jxl"
	local inputMask = KSPathUtils.child( testMetaCAFDataPath, maskFileName )

	requestFillHoleWithMetaCAF( rxTest, inputImage, inputMask )

end

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

function WFCatOrbitTest.DISABLED_rxTestRequestFillHoleWithMetaCAF_ManuallyVerifyResults_testNightly( rxTest )

	local verifyManually = true

	local fileName = "source.jxl"
	local inputImage = KSPathUtils.child( testMetaCAFDataPath, fileName )

	local maskFileName = "mask.jxl"
	local inputMask = KSPathUtils.child( testMetaCAFDataPath, maskFileName )

	requestFillHoleWithMetaCAF( rxTest, inputImage, inputMask, verifyManually )

end

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

local function requestServerSideMasking( rxTest, inputImage )

	rxTest:setTimeLimit( 30 )

	local wfCatalog = setUpTestCatalog( rxTest )

	local optArgs = {
		stage = true
	}

	rxTest:expectSingleValue( WFCatOrbit.requestServerSideMasking_experimental(
			wfCatalog, inputImage, optArgs )

		:map( function( filePaths )

			return #filePaths > 0

		end ), true )

end

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

function WFCatOrbitTest.rxTestRequestServerSideMasking_testNightly( rxTest )

	requestServerSideMasking( rxTest, filePath )

end

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

-- TODO [jandrew 2025-04-09]: In order to run this test, you must use an access
-- token generated from a client such as Lightroom Desktop, that has been
-- appropriately provisioned to work with the storage service. Unfortunatley,
-- for now, this is not the case for WF. We should address this.

function WFCatOrbitTest.DISABLED_rxTestUploadInputsToStorageService_testNightly( rxTest )

	rxTest:setTimeLimit( 30 )

	local wfCatalog = setUpTestCatalog( rxTest )

	local inputs = {
		{
			path = filePath,
			length = 1024,
			id = "test1"
		}
	}

	rxTest:expectSingleValue( WFCatOrbit.uploadInputsToStorageService( wfCatalog, inputs )

		:map( function( urls )
			return #urls == #inputs
		end ), true )

end

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

function WFCatOrbitTest.rxTestAskChatGPT_testNightly( rxTest )

	rxTest:setTimeLimit( 60 )

	local wfCatalog = setUpTestCatalog( rxTest )

	-- Test with default model (GPT-4o mini)
	local query1 = "What are the best camera settings for landscape photography?"

	rxTest:expectSingleValue( WFCatOrbit.askChatGPT( wfCatalog, query1 )
		:map( function( response )
			-- The response should contain content
			return response and response ~= ""
		end ), true )

end

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

function WFCatOrbitTest.rxTestSampleRenditionUrls( rxTest )

	local albumRenditionInfo = {
		{
			s3Url = "https://example.com/image1.jpg",
			people = { "John", "Jane" }
		},
		{
			s3Url = "https://example.com/image2.jpg",
			people = { "John" }
		},
		{
			s3Url = "https://example.com/image3.jpg",
			people = {}
		},
		{
			s3Url = "https://example.com/image4.jpg",
			people = { "Alice", "Bob", "Charlie" }
		}
	}

	local imageUrlsStr, imageUrlsSubset1Str, imageUrlsSubset2Str, metadataStr = 
		WFCatOrbit._DEBUG_sampleRenditionUrls( albumRenditionInfo )

	-- Test full image URLs string
	local expectedImageUrlsStr = [[
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image1.jpg",
			"detail": "low"
		}
	}
	,
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image2.jpg",
			"detail": "low"
		}
	}
	,
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image3.jpg",
			"detail": "low"
		}
	}
	,
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image4.jpg",
			"detail": "low"
		}
	}
	]]

	rxTest:expectEqual( expectedImageUrlsStr, imageUrlsStr )

	-- Test subset 1 (odd indices)

	local expectedSubset1Str = [[
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image1.jpg",
			"detail": "low"
		}
	}
	,
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image3.jpg",
			"detail": "low"
		}
	}
	]]

	rxTest:expectEqual( expectedSubset1Str, imageUrlsSubset1Str )

	-- Test subset 2 (even indices)

	local expectedSubset2Str = [[
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image2.jpg",
			"detail": "low"
		}
	}
	,
	{
		"type": "image_url",
		"image_url": {
			"url": "https://example.com/image4.jpg",
			"detail": "low"
		}
	}
	]]

	rxTest:expectEqual( expectedSubset2Str, imageUrlsSubset2Str )

	-- Test metadata string

	local expectedMetadataStr = "[Image 1 URL: https://example.com/image1.jpg, People in Image 1: John, Jane], [Image 2 URL: https://example.com/image2.jpg, People in Image 2: John], [Image 3 URL: https://example.com/image3.jpg, People in Image 3: None], [Image 4 URL: https://example.com/image4.jpg, People in Image 4: Alice, Bob, Charlie]"
	rxTest:expectEqual( expectedMetadataStr, metadataStr )

	-- Test empty input

	local emptyUrlsStr, emptySubset1Str, emptySubset2Str, emptyMetadataStr = 
		WFCatOrbit._DEBUG_sampleRenditionUrls( {} )

	rxTest:expectEqual( emptyUrlsStr, "" )
	rxTest:expectEqual( emptySubset1Str, "" )
	rxTest:expectEqual( emptySubset2Str, "" )
	rxTest:expectEqual( emptyMetadataStr, "" )

end

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

function WFCatOrbitTest.rxTestConvertGPSCoordinatesToString( rxTest )

	-- Test with valid GPS coordinates
	local gpsCoordinates = {
		{ 37.7749, -122.4194 },  -- San Francisco
		{ 34.0522, -118.2437 }   -- Los Angeles
	}

	local gpsStr = WFCatOrbit._DEBUG_convertGPSCoordinatesToString( gpsCoordinates )
	rxTest:expectEqual( gpsStr, "[37.7749, -122.4194], [34.0522, -118.2437]" )

	-- Test with nil input
	local nilStr = WFCatOrbit._DEBUG_convertGPSCoordinatesToString( nil )
	rxTest:expectEqual( nilStr, "" )

	-- Test with empty table
	local emptyStr = WFCatOrbit._DEBUG_convertGPSCoordinatesToString( {} )
	rxTest:expectEqual( emptyStr, "" )

	-- Test with single coordinate
	local singleCoord = {
		{ 37.7749, -122.4194 }
	}
	local singleStr = WFCatOrbit._DEBUG_convertGPSCoordinatesToString( singleCoord )
	rxTest:expectEqual( singleStr, "[37.7749, -122.4194]" )

end

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

return WFRxTestCase.wrapSuite( 'WFCatOrbitTest', WFCatOrbitTest )
