// 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 HyperTwistRubixCubeSolverPhase6RATTestInternal
{
	FHyperTwistTrainingDeck MakeRecognitionDeck()
	{
		FHyperTwistTrainingDeck Deck;
		Deck.DeckId = TEXT("phase6r-at/rubix-cube-solver-browser-asset-review");
		Deck.Title = TEXT("Phase 6R-AT Rubix Cube Solver Browser Asset Review");
		Deck.DeliveryModes = {
			EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};

		FHyperTwistTrainingCase TrainingCase;
		TrainingCase.CaseId = TEXT("phase6r-at-case");
		TrainingCase.PuzzleId = TEXT("cube/3x3x3");
		TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
		TrainingCase.PromptLabel = TEXT("Phase 6R-AT Recognition");
		TrainingCase.AllowedDeliveryModes = {
			EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
			EHyperTwistTrainingDeliveryMode::CoachReviewed
		};
		Deck.Cases = {TrainingCase};
		return Deck;
	}

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

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

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

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

		ForceMockRecognitionClient(TrainingSubsystem);
		const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
			MakeRecognitionDeck(),
			TEXT("phase6r-at-user"),
			SessionId,
			EHyperTwistTrainingDeliveryMode::RecognitionAssisted
		);
		return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
	}

	const FHyperTwistVisionBrowserAssetReviewDescriptor* FindBrowserAssetReviewDescriptor(
		const TArray<FHyperTwistVisionBrowserAssetReviewDescriptor>& Descriptors,
		const FString& ExpectedDescriptorId
	)
	{
		return Descriptors.FindByPredicate(
			[&ExpectedDescriptorId](const FHyperTwistVisionBrowserAssetReviewDescriptor& Candidate)
			{
				return Candidate.BrowserAssetReviewDescriptorId == ExpectedDescriptorId;
			}
		);
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistRubixCubeSolverPhase6RATBrowserAssetReviewSessionConfigTest,
	"HyperTwist.Permissive.RubixCubeSolver.Phase6R.AT.BrowserAssetReviewSessionConfig",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistRubixCubeSolverPhase6RATBrowserAssetReviewSessionConfigTest::RunTest(
	const FString& Parameters
)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistRubixCubeSolverPhase6RATTestInternal::MakeRecognitionSubsystem(
			TEXT("phase6r-at-config-session")
		);
	TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-AT."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FString OpenError;
	TestTrue(
		TEXT("The recognition session must open for the browser-asset review route."),
		TrainingSubsystem->OpenActiveRecognitionSession(OpenError)
	);
	TestTrue(TEXT("Opening the recognition session must not report an error."), OpenError.IsEmpty());

	const FHyperTwistTrainingRecognitionSessionState SessionState =
		TrainingSubsystem->GetActiveRecognitionSessionState();

	TestEqual(
		TEXT("The browser shell must expose the bounded browser asset review profile id."),
		SessionState.SessionConfig.BrowserShellProfileDefinition.BrowserAssetReviewProfileId,
		TEXT("rubix-browser-asset-review-v1")
	);
	TestTrue(
		TEXT("The browser asset review profile must stay structurally valid."),
		SessionState.SessionConfig.BrowserShellProfileDefinition.BrowserAssetReviewProfileDefinition
			.IsStructurallyValid()
	);
	TestEqual(
		TEXT("The browser shell must expose one bounded visualization asset review descriptor."),
		SessionState.SessionConfig.BrowserShellProfileDefinition.SupportedBrowserAssetReviews.Num(),
		1
	);
	TestEqual(
		TEXT("The browser shell must preserve the bounded default asset review descriptor id."),
		SessionState.SessionConfig.BrowserShellProfileDefinition.DefaultBrowserAssetReviewDescriptorId,
		TEXT("browser-asset-review/rubix-cube-solver/twistysim")
	);

	const FHyperTwistVisionBrowserAssetReviewDescriptor* Descriptor =
		HyperTwistRubixCubeSolverPhase6RATTestInternal::FindBrowserAssetReviewDescriptor(
			SessionState.SessionConfig.BrowserShellProfileDefinition.SupportedBrowserAssetReviews,
			TEXT("browser-asset-review/rubix-cube-solver/twistysim")
		);
	TestNotNull(TEXT("The twistysim review descriptor must be present."), Descriptor);
	if (Descriptor != nullptr)
	{
		TestEqual(
			TEXT("The twistysim review descriptor must preserve the upstream asset path."),
			Descriptor->AssetPathHint,
			TEXT("frontend/lib/twistysim.min.js")
		);
		TestFalse(
			TEXT("The twistysim review descriptor must not imply bundled redistribution."),
			Descriptor->bBundledWithCodeDistribution
		);
		TestTrue(
			TEXT("The twistysim review descriptor must keep playback visualization ownership deferred."),
			Descriptor->bPlaybackVisualizationDeferred
		);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistRubixCubeSolverPhase6RATBrowserAssetReviewPreviewStateTest,
	"HyperTwist.Permissive.RubixCubeSolver.Phase6R.AT.BrowserAssetReviewPreviewState",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistRubixCubeSolverPhase6RATBrowserAssetReviewPreviewStateTest::RunTest(
	const FString& Parameters
)
{
	UHyperTwistTrainingSubsystem* TrainingSubsystem =
		HyperTwistRubixCubeSolverPhase6RATTestInternal::MakeRecognitionSubsystem(
			TEXT("phase6r-at-preview-session")
		);
	TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-AT."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	FHyperTwistVisionFrameEnvelope Frame;
	Frame.TargetFaceHint = TEXT("F");
	const FHyperTwistVisionPreviewResult Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);

	TestTrue(
		TEXT("The preview result must carry a structurally valid browser shell state."),
		Result.BrowserShellState.IsStructurallyValid()
	);
	TestEqual(
		TEXT("The preview browser shell must preserve the bounded browser asset review profile id."),
		Result.BrowserShellState.BrowserAssetReviewProfileId,
		TEXT("rubix-browser-asset-review-v1")
	);
	TestEqual(
		TEXT("The preview browser shell must preserve the bounded active asset review descriptor id."),
		Result.BrowserShellState.ActiveBrowserAssetReviewDescriptorId,
		TEXT("browser-asset-review/rubix-cube-solver/twistysim")
	);
	TestTrue(
		TEXT("The preview browser shell must keep bundled browser asset shipping deferred."),
		Result.BrowserShellState.bBundledBrowserAssetDeferred
	);
	TestTrue(
		TEXT("The preview browser shell must still keep playback deferred in this packet."),
		Result.BrowserShellState.bPlaybackDeferred
	);
	return true;
}

#endif
