// 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 HyperTwistWhisperCppPhase6RAFTestInternal
{
	FHyperTwistTrainingDeck MakeSpeechDeck()
	{
		FHyperTwistTrainingDeck Deck;
		Deck.DeckId = TEXT("phase6r-af/whispercpp-device-permission-workflow");
		Deck.Title = TEXT("Phase 6R-AF Whisper Device Permission Workflow");
		Deck.DeliveryModes = {
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};

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

	void ForceSpeechClientKind(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ClientKind)
	{
		if (TrainingSubsystem == nullptr)
		{
			return;
		}

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

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

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

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

	FHyperTwistTrainingCompanionSpeechSessionState* ResolveActiveSpeechSessionState(
		UHyperTwistTrainingSubsystem* TrainingSubsystem
	)
	{
		if (TrainingSubsystem == nullptr)
		{
			return nullptr;
		}

		if (FStructProperty* SessionStateProperty = FindFProperty<FStructProperty>(
			UHyperTwistTrainingSubsystem::StaticClass(),
			TEXT("ActiveCompanionSpeechSessionState")
		))
		{
			return SessionStateProperty->ContainerPtrToValuePtr<FHyperTwistTrainingCompanionSpeechSessionState>(
				TrainingSubsystem
			);
		}

		return nullptr;
	}

	void ForcePermissionError(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ErrorText)
	{
		if (FHyperTwistTrainingCompanionSpeechSessionState* SessionState =
				ResolveActiveSpeechSessionState(TrainingSubsystem))
		{
			SessionState->LastError = ErrorText;
			SessionState->ServiceHealth.LastError = ErrorText;
		}
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowSessionConfigTest,
	"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionWorkflowSessionConfig",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

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

	FString OpenError;
	TestTrue(TEXT("The device-permission workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
	TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	const FHyperTwistSpeechDevicePermissionWorkflowProfile& Profile =
		SessionState.SessionConfig.DevicePermissionWorkflowProfileDefinition;

	TestTrue(TEXT("The device-permission workflow route must expose a structurally valid profile."), Profile.IsStructurallyValid());
	TestEqual(TEXT("The workflow profile must preserve the first-party profile id."), Profile.DevicePermissionWorkflowProfileId, TEXT("speech-device-permission-workflow-v1"));
	TestEqual(TEXT("The workflow profile must preserve the workflow kind."), Profile.WorkflowKind, TEXT("operator-device-permission-workflow"));
	TestEqual(TEXT("The workflow profile must preserve the permission contract id."), Profile.PermissionContractId, TEXT("device-permission-workflow-v1"));
	TestEqual(TEXT("The workflow profile must preserve the settings handoff contract id."), Profile.SettingsHandoffContractId, TEXT("device-permission-settings-handoff-v1"));
	TestEqual(TEXT("The workflow profile must preserve the recheck contract id."), Profile.PermissionRecheckContractId, TEXT("device-permission-recheck-v1"));
	TestEqual(TEXT("The workflow profile must preserve the request action id."), Profile.PermissionRequestActionId, TEXT("request-microphone-permission"));
	TestEqual(TEXT("The workflow profile must preserve the settings handoff action id."), Profile.OpenSettingsActionId, TEXT("open-microphone-privacy-settings"));
	TestEqual(TEXT("The workflow profile must preserve the recheck action id."), Profile.PermissionRecheckActionId, TEXT("recheck-microphone-permission"));
	TestTrue(TEXT("The speech service health must expose device-permission workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionWorkflow")));
	TestTrue(TEXT("The speech service health must expose settings handoff capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionSettingsHandoff")));
	TestTrue(TEXT("The speech service health must expose permission recheck capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionRecheck")));
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowTransitionTest,
	"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionWorkflowTransitions",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowTransitionTest::RunTest(
	const FString& Parameters
)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistWhisperCppPhase6RAFTestInternal::MakeSpeechSubsystem(
			TEXT("phase6r-af-transition-session"),
			TEXT("mock")
		);
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AF."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FString OpenError;
	TestTrue(TEXT("The device-permission workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
	TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());

	FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	FHyperTwistSpeechDevicePermissionWorkflowState WorkflowState =
		SessionState.DevicePermissionWorkflowState;

	TestTrue(TEXT("The granted workflow state must be structurally valid."), WorkflowState.IsStructurallyValid());
	TestEqual(TEXT("The granted workflow state must start cleared."), WorkflowState.WorkflowStateId, TEXT("permission-workflow-cleared"));
	TestEqual(TEXT("The granted workflow state must preserve permission-granted posture."), WorkflowState.PermissionStateId, TEXT("permission-granted"));
	TestTrue(TEXT("The granted workflow state must report permission granted."), WorkflowState.bPermissionGranted);
	TestTrue(TEXT("The granted workflow state must report workflow ready."), WorkflowState.bPermissionWorkflowReady);
	TestFalse(TEXT("The granted workflow state must keep permission request hidden."), WorkflowState.bPermissionRequestVisible);

	HyperTwistWhisperCppPhase6RAFTestInternal::ForcePermissionError(
		TrainingSubsystem,
		TEXT("microphone-permission-required")
	);
	TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();

	SessionState = TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	WorkflowState = SessionState.DevicePermissionWorkflowState;

	TestTrue(TEXT("The request-required workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
	TestEqual(TEXT("The request-required workflow state must preserve the request posture."), WorkflowState.WorkflowStateId, TEXT("permission-request-required"));
	TestEqual(TEXT("The request-required workflow state must preserve the permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required"));
	TestFalse(TEXT("The request-required workflow state must report permission withheld."), WorkflowState.bPermissionGranted);
	TestFalse(TEXT("The request-required workflow state must block workflow ready."), WorkflowState.bPermissionWorkflowReady);
	TestTrue(TEXT("The request-required workflow state must surface the permission request action."), WorkflowState.bPermissionRequestVisible);
	TestFalse(TEXT("The request-required workflow state must keep settings handoff hidden."), WorkflowState.bSettingsHandoffVisible);
	TestFalse(TEXT("The request-required workflow state must keep permission recheck hidden."), WorkflowState.bPermissionRecheckVisible);
	TestEqual(TEXT("The request-required workflow state must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
	TestEqual(TEXT("The request-required workflow state must retain one request entry."), WorkflowState.PermissionRequestEntryCount, 1);
	TestEqual(TEXT("The request-required workflow state must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-request-required"));
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistWhisperCppPhase6RAFDevicePermissionSettingsWorkflowTest,
	"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionSettingsWorkflow",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistWhisperCppPhase6RAFDevicePermissionSettingsWorkflowTest::RunTest(
	const FString& Parameters
)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistWhisperCppPhase6RAFTestInternal::MakeSpeechSubsystem(
			TEXT("phase6r-af-settings-session"),
			TEXT("mock")
		);
	TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AF."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FString OpenError;
	TestTrue(TEXT("The settings-review workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
	TestTrue(TEXT("Opening the settings-review speech session must not report an error."), OpenError.IsEmpty());

	HyperTwistWhisperCppPhase6RAFTestInternal::ForcePermissionError(
		TrainingSubsystem,
		TEXT("microphone-permission-denied-settings")
	);
	TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();

	const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
		TrainingSubsystem->GetActiveCompanionSpeechSessionState();
	const FHyperTwistSpeechDevicePermissionWorkflowState& WorkflowState =
		SessionState.DevicePermissionWorkflowState;

	TestTrue(TEXT("The settings-review workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
	TestEqual(TEXT("The settings-review workflow must preserve the settings-review posture."), WorkflowState.WorkflowStateId, TEXT("permission-settings-review-required"));
	TestEqual(TEXT("The settings-review workflow must preserve permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required"));
	TestEqual(TEXT("The settings-review workflow must preserve the settings-review source kind."), WorkflowState.LatestSourceKind, TEXT("permission-settings-review-required"));
	TestFalse(TEXT("The settings-review workflow must report permission withheld."), WorkflowState.bPermissionGranted);
	TestFalse(TEXT("The settings-review workflow must block workflow-ready posture."), WorkflowState.bPermissionWorkflowReady);
	TestTrue(TEXT("The settings-review workflow must recommend settings review."), WorkflowState.bSettingsReviewRecommended);
	TestTrue(TEXT("The settings-review workflow must surface settings handoff."), WorkflowState.bSettingsHandoffVisible);
	TestTrue(TEXT("The settings-review workflow must surface permission recheck."), WorkflowState.bPermissionRecheckVisible);
	TestEqual(TEXT("The settings-review workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
	TestEqual(TEXT("The settings-review workflow must retain one settings handoff entry."), WorkflowState.SettingsHandoffEntryCount, 1);
	TestEqual(TEXT("The settings-review workflow must retain one permission recheck entry."), WorkflowState.PermissionRecheckEntryCount, 1);
	TestEqual(TEXT("The settings-review workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-settings-review-required"));
	TestTrue(TEXT("The health surface must still advertise device-permission workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionWorkflow")));
	TestTrue(TEXT("The health surface must still advertise settings handoff capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionSettingsHandoff")));
	TestTrue(TEXT("The health surface must still advertise permission recheck capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionRecheck")));
	return true;
}

#endif
