// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistRecognition/HyperTwistVoiceClient.h"
#include "UObject/Object.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistPiperPhase6RAQTestInternal
{
	UHyperTwistMockVoiceClient* MakeMockVoiceClient()
	{
		return NewObject<UHyperTwistMockVoiceClient>();
	}

	UHyperTwistHttpVoiceClient* MakeHttpVoiceClient()
	{
		return NewObject<UHyperTwistHttpVoiceClient>();
	}

	const FHyperTwistVoiceProfileSummary* FindProfileById(
		const TArray<FHyperTwistVoiceProfileSummary>& Profiles,
		const FString& VoiceProfileId
	)
	{
		return Profiles.FindByPredicate(
			[&VoiceProfileId](const FHyperTwistVoiceProfileSummary& Profile)
			{
				return Profile.VoiceProfileId == VoiceProfileId;
			}
		);
	}

	const FHyperTwistVoiceAssetDescriptor* FindVoiceAssetById(
		const TArray<FHyperTwistVoiceAssetDescriptor>& VoiceAssets,
		const FString& VoiceAssetId
	)
	{
		return VoiceAssets.FindByPredicate(
			[&VoiceAssetId](const FHyperTwistVoiceAssetDescriptor& VoiceAsset)
			{
				return VoiceAsset.VoiceAssetId == VoiceAssetId;
			}
		);
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistPiperPhase6RAQVoiceAssetCatalogAlignmentTest,
	"HyperTwist.Permissive.Piper.Phase6R.AQ.VoiceAssetCatalogAlignment",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistPiperPhase6RAQVoiceAssetCatalogAlignmentTest::RunTest(const FString& Parameters)
{
	UHyperTwistMockVoiceClient* VoiceClient = HyperTwistPiperPhase6RAQTestInternal::MakeMockVoiceClient();
	TestNotNull(TEXT("The mock voice client must be constructed for Phase 6R-AQ."), VoiceClient);
	if (VoiceClient == nullptr)
	{
		return false;
	}

	const TArray<FHyperTwistVoiceProfileSummary> Profiles = VoiceClient->ListVoiceProfiles();
	const FHyperTwistVoiceServiceHealth Health = VoiceClient->GetVoiceServiceHealth();

	TestTrue(TEXT("The local voice catalog must remain non-empty."), Profiles.Num() >= 3);
	TestTrue(TEXT("The voice review health must remain structurally valid."), Health.IsStructurallyValid());
	TestEqual(TEXT("The bounded review lane must expose three local voice assets."), Health.SupportedVoiceAssetIds.Num(), 3);
	TestEqual(TEXT("The bounded review lane must expose three local voice descriptors."), Health.SupportedVoiceAssets.Num(), 3);
	TestFalse(
		TEXT("The bounded piper review lane must not implicitly widen into the coqui premium profile."),
		Health.SupportedVoiceAssetIds.Contains(TEXT("voice-asset/coqui/multilingual-guide-premium"))
	);

	for (const FHyperTwistVoiceAssetDescriptor& VoiceAsset : Health.SupportedVoiceAssets)
	{
		const FHyperTwistVoiceProfileSummary* Profile =
			HyperTwistPiperPhase6RAQTestInternal::FindProfileById(Profiles, VoiceAsset.VoiceProfileId);
		TestNotNull(TEXT("Every reviewable local voice asset must map back to a voice profile."), Profile);
		if (Profile != nullptr)
		{
			TestEqual(
				TEXT("The reviewable local voice asset must preserve the model-card review path."),
				VoiceAsset.ReviewDocumentPath,
				Profile->ModelCardRelativePath
			);
		}
	}

	const FHyperTwistVoiceAssetDescriptor* LessacAsset =
		HyperTwistPiperPhase6RAQTestInternal::FindVoiceAssetById(
			Health.SupportedVoiceAssets,
			TEXT("voice-asset/piper/en_US-lessac-medium")
		);
	TestNotNull(TEXT("The bounded review lane must preserve the Lessac voice asset descriptor."), LessacAsset);
	if (LessacAsset != nullptr)
	{
		TestEqual(
			TEXT("The Lessac descriptor must preserve the ONNX artifact name."),
			LessacAsset->ArtifactName,
			TEXT("en_US-lessac-medium.onnx")
		);
		TestEqual(
			TEXT("The Lessac descriptor must preserve the donor-grounded relative path hint."),
			LessacAsset->RelativePathHint,
			TEXT("en/en_US/lessac/medium/en_US-lessac-medium.onnx")
		);
		TestTrue(TEXT("The Lessac descriptor must remain user-supplied in this packet."), LessacAsset->bUserSuppliedOnly);
		TestFalse(TEXT("The Lessac descriptor must not be bundled by this packet."), LessacAsset->bBundledWithCodeDistribution);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistPiperPhase6RAQVoiceAssetReviewHealthTest,
	"HyperTwist.Permissive.Piper.Phase6R.AQ.VoiceAssetReviewHealth",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistPiperPhase6RAQVoiceAssetReviewHealthTest::RunTest(const FString& Parameters)
{
	UHyperTwistMockVoiceClient* VoiceClient = HyperTwistPiperPhase6RAQTestInternal::MakeMockVoiceClient();
	TestNotNull(TEXT("The mock voice client must be constructed for Phase 6R-AQ."), VoiceClient);
	if (VoiceClient == nullptr)
	{
		return false;
	}

	const FHyperTwistVoiceServiceHealth Health = VoiceClient->GetVoiceServiceHealth();
	TestTrue(TEXT("The voice review health must remain structurally valid."), Health.IsStructurallyValid());
	TestEqual(
		TEXT("The voice review lane must preserve the review profile id."),
		Health.VoiceAssetReviewProfileId,
		TEXT("downloadable-voice-asset-review-v1")
	);
	TestTrue(
		TEXT("The voice review profile must remain structurally valid."),
		Health.VoiceAssetReviewProfileDefinition.IsStructurallyValid()
	);
	TestTrue(
		TEXT("The voice service must advertise downloadable voice-asset review capability."),
		Health.Capabilities.Contains(TEXT("downloadableVoiceAssetReview"))
	);
	TestTrue(
		TEXT("The voice service must advertise model-card review capability."),
		Health.Capabilities.Contains(TEXT("voiceModelCardReview"))
	);
	TestFalse(
		TEXT("The review profile must keep runtime voice download deferred."),
		Health.VoiceAssetReviewProfileDefinition.bAllowsRuntimeVoiceDownloadInCurrentPacket
	);
	TestFalse(
		TEXT("The review profile must keep bundled redistribution disabled."),
		Health.VoiceAssetReviewProfileDefinition.bAllowsBundledVoiceAssetRedistribution
	);
	TestTrue(
		TEXT("The review profile must preserve user-managed voice support."),
		Health.VoiceAssetReviewProfileDefinition.bSupportsUserManagedVoiceAssets
	);
	TestTrue(
		TEXT("The review profile must preserve sidecar-managed voice support."),
		Health.VoiceAssetReviewProfileDefinition.bSupportsSidecarManagedVoiceAssets
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistPiperPhase6RAQProviderBackedVoiceAssetReviewFallbackTest,
	"HyperTwist.Permissive.Piper.Phase6R.AQ.ProviderBackedVoiceAssetReviewFallback",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistPiperPhase6RAQProviderBackedVoiceAssetReviewFallbackTest::RunTest(const FString& Parameters)
{
	UHyperTwistHttpVoiceClient* VoiceClient = HyperTwistPiperPhase6RAQTestInternal::MakeHttpVoiceClient();
	TestNotNull(TEXT("The provider-backed voice client must be constructed for Phase 6R-AQ."), VoiceClient);
	if (VoiceClient == nullptr)
	{
		return false;
	}

	VoiceClient->ServiceBaseUrl.Reset();
	const FHyperTwistVoiceServiceHealth Health = VoiceClient->GetVoiceServiceHealth();

	TestTrue(TEXT("The provider-backed review fallback must remain structurally valid."), Health.IsStructurallyValid());
	TestFalse(TEXT("The provider-backed review fallback must report not ready."), Health.bReady);
	TestEqual(
		TEXT("The provider-backed review fallback must preserve the missing-endpoint error."),
		Health.LastError,
		TEXT("service-base-url-missing")
	);
	TestTrue(
		TEXT("The provider-backed review fallback must preserve downloadable voice-asset review capability."),
		Health.Capabilities.Contains(TEXT("downloadableVoiceAssetReview"))
	);
	TestTrue(
		TEXT("The provider-backed review fallback must preserve provider-backed voice capability."),
		Health.Capabilities.Contains(TEXT("provider-backed-voice"))
	);
	TestEqual(
		TEXT("The provider-backed review fallback must preserve three local voice asset ids."),
		Health.SupportedVoiceAssetIds.Num(),
		3
	);
	TestEqual(
		TEXT("The provider-backed review fallback must preserve three local voice asset descriptors."),
		Health.SupportedVoiceAssets.Num(),
		3
	);
	return true;
}

#endif
