// 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 HyperTwistFasterWhisperPhase6RFTestInternal
{
	FHyperTwistTrainingDeck MakeSpeechDeck()
	{
		FHyperTwistTrainingDeck Deck;
		Deck.DeckId = TEXT("phase6r-f/fasterwhisper-speech");
		Deck.Title = TEXT("Phase 6R-F Faster Whisper Speech");
		Deck.DeliveryModes = {
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};

		FHyperTwistTrainingCase TrainingCase;
		TrainingCase.CaseId = TEXT("phase6r-f-case");
		TrainingCase.PuzzleId = TEXT("cube/3x3x3");
		TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence;
		TrainingCase.PromptLabel = TEXT("Phase 6R-F 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-f-user"),
			SessionId,
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		);
		return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
	}
}

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

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

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

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	TestEqual(
		TEXT("The orchestration profile id must anchor on the bounded Python orchestration slice."),
		SessionState.SessionConfig.OrchestrationProfile.OrchestrationProfileId,
		TEXT("python-batch-transcribe-v1")
	);
	TestEqual(
		TEXT("The service lane id must stay inside the speech Python service lane."),
		SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId,
		TEXT("speech/python-service")
	);
	TestEqual(
		TEXT("The preferred batch size must preserve the bounded batched inference profile."),
		SessionState.SessionConfig.OrchestrationProfile.PreferredBatchSize,
		8
	);
	TestTrue(
		TEXT("The orchestration profile must request word timestamps."),
		SessionState.SessionConfig.OrchestrationProfile.bEnableWordTimestamps
	);
	TestFalse(
		TEXT("The orchestration profile must keep condition-on-previous-text disabled in the first slice."),
		SessionState.SessionConfig.OrchestrationProfile.bConditionOnPreviousText
	);
	TestTrue(
		TEXT("The orchestration profile must expose the HyperTwist hotword hint."),
		SessionState.SessionConfig.OrchestrationProfile.HotwordHints.Contains(TEXT("HyperTwist"))
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistFasterWhisperPhase6RFTranscriptMetadataTest,
	"HyperTwist.Permissive.FasterWhisper.Phase6R.F.TranscriptMetadata",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistFasterWhisperPhase6RFTranscriptMetadataTest::RunTest(const FString& Parameters)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistFasterWhisperPhase6RFTestInternal::MakeSpeechSubsystem(TEXT("phase6r-f-transcript-session"));
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-F."), 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 bounded mock route must resolve the next-case transcript."), Result.TranscriptText, TEXT("next case"));
	TestEqual(TEXT("The orchestration profile id must be carried on the transcript result."), Result.OrchestrationProfileId, TEXT("python-batch-transcribe-v1"));
	TestEqual(TEXT("The service lane id must be carried on the transcript result."), Result.ServiceLaneId, TEXT("speech/python-service"));
	TestEqual(TEXT("The requested batch size must stay on the first bounded profile."), Result.RequestedBatchSize, 8);
	TestEqual(TEXT("The first bounded profile should report one processed clip."), Result.ProcessedClipCount, 1);
	TestTrue(TEXT("The first bounded profile should report batched inference usage."), Result.bUsedBatchedInference);
	TestTrue(TEXT("The first bounded profile should request word timestamps."), Result.bWordTimestampsRequested);
	TestFalse(TEXT("The first bounded profile should keep previous-text conditioning disabled."), Result.bConditionedOnPreviousText);
	TestTrue(TEXT("The first bounded profile should record prompt-hint usage."), Result.bUsedPromptHints);
	TestEqual(TEXT("The bounded profile should keep the prefix hint."), Result.AppliedPrefixHint, TEXT("Coach command:"));
	TestTrue(TEXT("The bounded profile should carry the HyperTwist hotword hint."), Result.AppliedHotwordHints.Contains(TEXT("HyperTwist")));
	TestTrue(TEXT("The bounded profile should report language confidence."), Result.LanguageProbability > 0.9f);
	return true;
}

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

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

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	TestTrue(TEXT("The mock speech health must expose batched transcription capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("batchedTranscribe")));
	TestTrue(TEXT("The mock speech health must expose word timestamp capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("wordTimestamps")));
	TestTrue(TEXT("The mock speech health must expose language detection capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("languageDetection")));
	TestTrue(TEXT("The mock speech health must expose prompt hint capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("promptHints")));
	TestTrue(
		TEXT("The mock speech health must expose the supported orchestration profile."),
		SessionState.ServiceHealth.SupportedOrchestrationProfiles.Contains(TEXT("python-batch-transcribe-v1"))
	);
	return true;
}

#endif
