// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistMagicTilePhase7TestInternal
{
	FHyperTwistTrainingTilingStateEnvelope MakeSampleStateEnvelope()
	{
		FHyperTwistTrainingTilingStateEnvelope Envelope;
		Envelope.SessionId = TEXT("tiling-session-001");
		Envelope.SharedScrambleNotation = TEXT("R, U\nm(3,4)\u2019 ; reflect-x");

		FHyperTwistPuzzleState& PuzzleState = Envelope.PuzzleState;
		PuzzleState.Definition.PuzzleId = TEXT("tiling/{3,7}/sample");
		PuzzleState.Definition.PuzzleFamily = EHyperTwistPuzzleFamily::Tiling;
		PuzzleState.Definition.Dimension = 2;
		PuzzleState.Definition.DefinitionVersion = TEXT("2026.06");
		PuzzleState.Definition.NotationProfile = TEXT("magictile-shared");
		PuzzleState.Definition.SizeVector = {7, 3};
		PuzzleState.StateEncoding.EncodingProfile = TEXT("tiling-cells/v1");
		PuzzleState.StateEncoding.PayloadJson =
			TEXT("{\"cells\":[\"c0\",\"c1\"],\"selection\":\"face-0\"}");
		PuzzleState.CapturedAtUtc = TEXT("2026-06-19T00:15:00Z");
		PuzzleState.SourceSessionId = Envelope.SessionId;

		FHyperTwistTrainingLiveTimerState& TimerState = Envelope.TimerState;
		TimerState.Phase = EHyperTwistTrainingLiveTimerPhase::Solving;
		TimerState.SolveElapsedMs = 12345;
		TimerState.DisplayedElapsedMs = 0;
		TimerState.InspectionElapsedMs = 1500;
		TimerState.TotalPausedMs = 250;

		return Envelope;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMagicTilePhase7AHostDecisionTest,
	"HyperTwist.FirstParty.MagicTile.Phase7A.HostDecision",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMagicTilePhase7AHostDecisionTest::RunTest(const FString& Parameters)
{
	const FHyperTwistTrainingTilingReferenceBundle Bundle =
		UHyperTwistTrainingRuntimeLibrary::GetBundledTilingReferenceBundle();

	TestTrue(
		TEXT("The tiling bundle must stay structurally valid after the Phase 7 host decision lands."),
		Bundle.IsStructurallyValid()
	);

	FHyperTwistTrainingTilingHostDecision Decision;
	TestTrue(
		TEXT("The primary tiling host decision must resolve from the runtime library."),
		UHyperTwistTrainingRuntimeLibrary::TryGetBundledTilingHostDecision(
			Bundle.PrimaryHostDecisionId,
			Decision
		)
	);
	TestEqual(
		TEXT("The tiling host decision id must stay stable."),
		Decision.DecisionId,
		TEXT("tiling-embedded-browser-host")
	);
	TestEqual(
		TEXT("The tiling host decision must choose the embedded browser lane."),
		Decision.HostKind,
		EHyperTwistTrainingTilingHostKind::EmbeddedBrowserCef
	);
	TestTrue(
		TEXT("The host decision must require the retained three.js substrate."),
		Decision.RequiredPackageIds.Contains(TEXT("package/three-js"))
	);
	TestTrue(
		TEXT("The host decision must require the retained react-three-fiber substrate."),
		Decision.RequiredPackageIds.Contains(TEXT("package/react-three-fiber"))
	);
	TestTrue(
		TEXT("The host decision must preserve browser bridge object transport as a live browser surface."),
		Decision.BrowserSurfaceTags.Contains(TEXT("browser bridge object transport"))
	);
	TestTrue(
		TEXT("The host decision must explicitly defer a native non-Euclidean renderer port."),
		Decision.DeferredPortReasons.Contains(TEXT("native spherical and hyperbolic renderer ownership remains behavior-unproven relative to the landed browser runtime substrate"))
	);
	TestTrue(
		TEXT("The host decision must keep the old WinForms/OpenTK host shell excluded."),
		Decision.ExplicitExclusions.Contains(TEXT("whole WinForms/OpenTK host shell ownership"))
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMagicTilePhase7BStateBridgeContractTest,
	"HyperTwist.FirstParty.MagicTile.Phase7B.StateBridgeContract",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMagicTilePhase7BStateBridgeContractTest::RunTest(const FString& Parameters)
{
	const FHyperTwistTrainingTilingReferenceBundle Bundle =
		UHyperTwistTrainingRuntimeLibrary::GetBundledTilingReferenceBundle();

	FHyperTwistTrainingTilingStateBridgeContract Contract;
	TestTrue(
		TEXT("The tiling state bridge contract must resolve from the runtime library."),
		UHyperTwistTrainingRuntimeLibrary::TryGetBundledTilingStateBridgeContract(
			Bundle.PrimaryStateBridgeContractId,
			Contract
		)
	);
	TestEqual(
		TEXT("The tiling state bridge contract id must stay stable."),
		Contract.ContractId,
		TEXT("tiling-browser-state-bridge")
	);
	TestEqual(
		TEXT("The tiling state bridge contract must stay anchored to the browser host decision."),
		Contract.HostDecisionId,
		Bundle.PrimaryHostDecisionId
	);
	TestTrue(
		TEXT("The state bridge contract must keep MagicTile loader support as a required input."),
		Contract.RequiredPackageIds.Contains(TEXT("package/magictile-loader"))
	);
	TestTrue(
		TEXT("The state bridge contract must preserve postMessage fallback transport."),
		Contract.TransportSurfaceTags.Contains(TEXT("postMessage fallback transport"))
	);
	TestTrue(
		TEXT("The state bridge contract must preserve the shared scramble parser surface."),
		Contract.ScrambleSurfaceTags.Contains(TEXT("token-preserving scramble parser"))
	);
	TestTrue(
		TEXT("The state bridge contract must preserve the shared training timer surface."),
		Contract.TimerSurfaceTags.Contains(TEXT("shared training live timer state"))
	);
	TestTrue(
		TEXT("The state bridge contract must keep generic replay ownership excluded."),
		Contract.ExplicitExclusions.Contains(TEXT("generic replay ownership"))
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMagicTilePhase7BSharedScrambleParserTest,
	"HyperTwist.FirstParty.MagicTile.Phase7B.SharedScrambleParser",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMagicTilePhase7BSharedScrambleParserTest::RunTest(const FString& Parameters)
{
	const TArray<FString> Tokens =
		UHyperTwistTrainingRuntimeLibrary::ParseBundledTilingSharedScrambleNotation(
			TEXT("  R,  U\nm(3,4)\u2019 ; reflect-x  ")
		);

	TestEqual(
		TEXT("The shared scramble parser must preserve four normalized tokens."),
		Tokens.Num(),
		4
	);
	if (Tokens.Num() != 4)
	{
		return false;
	}
	TestEqual(TEXT("The first token must trim trailing punctuation."), Tokens[0], TEXT("R"));
	TestEqual(TEXT("The second token must survive whitespace normalization."), Tokens[1], TEXT("U"));
	TestEqual(TEXT("The third token must preserve interior punctuation and normalize the prime mark."), Tokens[2], TEXT("m(3,4)'"));
	TestEqual(TEXT("The fourth token must preserve hyphenated shell commands."), Tokens[3], TEXT("reflect-x"));
	TestEqual(
		TEXT("The rebuilt shared scramble notation must join normalized tokens with spaces."),
		UHyperTwistTrainingRuntimeLibrary::BuildBundledTilingSharedScrambleNotation(Tokens),
		TEXT("R U m(3,4)' reflect-x")
	);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMagicTilePhase7BStateEnvelopeSerializationTest,
	"HyperTwist.FirstParty.MagicTile.Phase7B.StateEnvelopeSerialization",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMagicTilePhase7BStateEnvelopeSerializationTest::RunTest(const FString& Parameters)
{
	const FHyperTwistTrainingTilingStateEnvelope SampleEnvelope =
		HyperTwistMagicTilePhase7TestInternal::MakeSampleStateEnvelope();

	FString Json;
	TestTrue(
		TEXT("The tiling state bridge must serialize a normalized state envelope to JSON."),
		UHyperTwistTrainingRuntimeLibrary::TrySerializeBundledTilingStateEnvelopeToJson(
			SampleEnvelope,
			Json
		)
	);
	TestFalse(TEXT("The serialized tiling state bridge JSON must not be empty."), Json.IsEmpty());

	FHyperTwistTrainingTilingStateEnvelope RoundTrippedEnvelope;
	TestTrue(
		TEXT("The tiling state bridge must deserialize the normalized state envelope from JSON."),
		UHyperTwistTrainingRuntimeLibrary::TryDeserializeBundledTilingStateEnvelopeFromJson(
			Json,
			RoundTrippedEnvelope
		)
	);
	TestEqual(
		TEXT("State-envelope normalization must restore the embedded browser host decision id."),
		RoundTrippedEnvelope.HostDecisionId,
		TEXT("tiling-embedded-browser-host")
	);
	TestEqual(
		TEXT("State-envelope normalization must restore the browser state bridge id."),
		RoundTrippedEnvelope.BridgeContractId,
		TEXT("tiling-browser-state-bridge")
	);
	TestEqual(
		TEXT("State-envelope normalization must derive the puzzle definition id from the puzzle state."),
		RoundTrippedEnvelope.PuzzleDefinitionId,
		TEXT("tiling/{3,7}/sample")
	);
	TestEqual(
		TEXT("State-envelope normalization must derive the shared scramble notation from normalized tokens."),
		RoundTrippedEnvelope.SharedScrambleNotation,
		TEXT("R U m(3,4)' reflect-x")
	);
	TestEqual(
		TEXT("State-envelope normalization must repair the timer training-session id from the envelope session id."),
		RoundTrippedEnvelope.TimerState.TrainingSessionId,
		RoundTrippedEnvelope.SessionId
	);
	TestEqual(
		TEXT("State-envelope normalization must keep displayed elapsed time aligned with the larger captured timer phase."),
		RoundTrippedEnvelope.TimerState.DisplayedElapsedMs,
		12345
	);
	return true;
}

#endif
