// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "Engine/GameInstance.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "UObject/UnrealType.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistFasterWhisperPhase6RAPTestInternal
{
	FHyperTwistTrainingDeck MakeSpeechDeck()
	{
		FHyperTwistTrainingDeck Deck;
		Deck.DeckId = TEXT("phase6r-ap/fasterwhisper-service-lane-tuning");
		Deck.Title = TEXT("Phase 6R-AP Faster Whisper Service Lane Tuning");
		Deck.DeliveryModes = {
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};

		FHyperTwistTrainingCase TrainingCase;
		TrainingCase.CaseId = TEXT("phase6r-ap-case");
		TrainingCase.PuzzleId = TEXT("cube/3x3x3");
		TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence;
		TrainingCase.PromptLabel = TEXT("Phase 6R-AP Coach Speech");
		TrainingCase.AllowedDeliveryModes = {
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};
		Deck.Cases = {TrainingCase};
		return Deck;
	}

	void ForceMockSpeechClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
	{
		if (TrainingSubsystem == nullptr)
		{
			return;
		}

		if (FStrProperty* SpeechClientKindProperty = FindFProperty<FStrProperty>(
			UHyperTwistTrainingSubsystem::StaticClass(),
			TEXT("CompanionSpeechClientKind")
		))
		{
			SpeechClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, TEXT("mock"));
		}
	}

	UHyperTwistTrainingSubsystem* MakeSpeechSubsystem(const FString& SessionId)
	{
		UGameInstance* GameInstance = NewObject<UGameInstance>();
		if (GameInstance == nullptr)
		{
			return nullptr;
		}

		UHyperTwistTrainingSubsystem* TrainingSubsystem =
			NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
		if (TrainingSubsystem == nullptr)
		{
			return nullptr;
		}

		ForceMockSpeechClient(TrainingSubsystem);
		const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
			MakeSpeechDeck(),
			TEXT("phase6r-ap-user"),
			SessionId,
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		);
		return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
	}

	bool HasCapability(const TArray<FString>& Capabilities, const FString& ExpectedCapability)
	{
		return Capabilities.Contains(ExpectedCapability);
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistFasterWhisperPhase6RAPSessionConfigTest,
	"HyperTwist.Permissive.FasterWhisper.Phase6R.AP.SessionConfig",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistFasterWhisperPhase6RAPSessionConfigTest::RunTest(const FString& Parameters)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistFasterWhisperPhase6RAPTestInternal::MakeSpeechSubsystem(
			TEXT("phase6r-ap-config-session")
		);
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AP."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FString OpenError;
	TestTrue(
		TEXT("The speech session must open for the Phase 6R-AP tuning profile."),
		TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)
	);
	TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	const FHyperTwistSpeechTranscriptionOrchestrationProfile& Profile =
		SessionState.SessionConfig.OrchestrationProfile;

	TestEqual(
		TEXT("The tuning profile must preserve the Python orchestration profile id."),
		Profile.OrchestrationProfileId,
		TEXT("python-batch-transcribe-v1")
	);
	TestEqual(
		TEXT("The bounded tuning profile must preserve the batch collection window."),
		Profile.BatchCollectionWindowMs,
		150
	);
	TestEqual(
		TEXT("The bounded tuning profile must preserve the prompt-routing mode."),
		Profile.PromptRoutingModeId,
		TEXT("prefix-hotword-session-hints-v1")
	);
	TestEqual(
		TEXT("The bounded tuning profile must preserve the retrieval context lane."),
		Profile.RetrievalContextLaneId,
		TEXT("speech-command-hint-session-memory")
	);
	TestEqual(
		TEXT("The bounded tuning profile must preserve the retrieved-hint cap."),
		Profile.MaxRetrievedHintCount,
		4
	);
	TestTrue(
		TEXT("The bounded tuning profile must keep retrieved-hint augmentation enabled."),
		Profile.bEnableRetrievedHintAugmentation
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistFasterWhisperPhase6RAPTranscriptTuningTest,
	"HyperTwist.Permissive.FasterWhisper.Phase6R.AP.TranscriptTuningMetadata",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistFasterWhisperPhase6RAPTranscriptTuningTest::RunTest(const FString& Parameters)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistFasterWhisperPhase6RAPTestInternal::MakeSpeechSubsystem(
			TEXT("phase6r-ap-transcript-session")
		);
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AP."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FHyperTwistSpeechUtteranceEnvelope Utterance;
	Utterance.AudioRef = TEXT("mock://coach/next-case");
	Utterance.SpeechStartMs = 100;
	Utterance.SpeechEndMs = 900;
	Utterance.SilenceGapMs = 180;
	Utterance.SampleCount = 12800;
	const FHyperTwistSpeechTranscriptResult Result =
		TrainingSubsystem->SubmitActiveCompanionSpeechUtterance(Utterance);

	TestTrue(TEXT("The transcript result must remain structurally valid."), Result.IsStructurallyValid());
	TestEqual(
		TEXT("The batch collection window must be carried onto the transcript result."),
		Result.AppliedBatchCollectionWindowMs,
		150
	);
	TestEqual(
		TEXT("The prompt-routing mode must be carried onto the transcript result."),
		Result.AppliedPromptRoutingModeId,
		TEXT("prefix-hotword-session-hints-v1")
	);
	TestEqual(
		TEXT("The retrieval context lane must be carried onto the transcript result."),
		Result.AppliedRetrievalContextLaneId,
		TEXT("speech-command-hint-session-memory")
	);
	TestTrue(
		TEXT("The bounded tuning profile must record retrieved-hint augmentation."),
		Result.bUsedRetrievedHintAugmentation
	);
	TestEqual(
		TEXT("The bounded tuning profile must cap retrieved command hints to four."),
		Result.RetrievedHintCount,
		4
	);
	TestEqual(
		TEXT("The bounded tuning profile must retain the first command hint."),
		Result.AppliedRetrievedHints.Num() > 0 ? Result.AppliedRetrievedHints[0] : FString(),
		TEXT("ready coach")
	);
	TestTrue(
		TEXT("The bounded tuning profile must still surface prompt-hint usage."),
		Result.bUsedPromptHints
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistFasterWhisperPhase6RAPServiceHealthTest,
	"HyperTwist.Permissive.FasterWhisper.Phase6R.AP.ServiceHealth",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistFasterWhisperPhase6RAPServiceHealthTest::RunTest(const FString& Parameters)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistFasterWhisperPhase6RAPTestInternal::MakeSpeechSubsystem(
			TEXT("phase6r-ap-health-session")
		);
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AP."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	TestTrue(
		TEXT("Speech health must expose the batch-window tuning capability."),
		HyperTwistFasterWhisperPhase6RAPTestInternal::HasCapability(
			SessionState.ServiceHealth.Capabilities,
			TEXT("batchWindowTuning")
		)
	);
	TestTrue(
		TEXT("Speech health must expose the prompt-routing profile capability."),
		HyperTwistFasterWhisperPhase6RAPTestInternal::HasCapability(
			SessionState.ServiceHealth.Capabilities,
			TEXT("promptRoutingProfiles")
		)
	);
	TestTrue(
		TEXT("Speech health must expose the retrieval-hint augmentation capability."),
		HyperTwistFasterWhisperPhase6RAPTestInternal::HasCapability(
			SessionState.ServiceHealth.Capabilities,
			TEXT("retrievalHintAugmentation")
		)
	);
	return true;
}

#endif
