#include "Misc/AutomationTest.h"

#include "HyperTwistSimulation/HyperTwistSimulationLibrary.h"
#include "HyperTwistSimulation/HyperTwistVirtual3333ProjectionLibrary.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistVirtual3333Phase6BContractTestInternal
{
	FString SerializeState(const FHyperTwistVirtual3333RuntimeState& State)
	{
		return UHyperTwistVirtual3333ProjectionLibrary::SerializeRuntimeStateToJson(State);
	}

	EHyperTwistVirtual3333TurnDirection InvertDirection(
		const EHyperTwistVirtual3333TurnDirection Direction
	)
	{
		return Direction == EHyperTwistVirtual3333TurnDirection::Clockwise
			? EHyperTwistVirtual3333TurnDirection::CounterClockwise
			: EHyperTwistVirtual3333TurnDirection::Clockwise;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistVirtual3333Phase6BProjectionContractTest,
	"HyperTwist.CleanRoom.HactarCE.Phase6B.VisibleProjectionContract",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistVirtual3333Phase6BProjectionContractTest::RunTest(const FString& Parameters)
{
	static_cast<void>(Parameters);

	const FHyperTwistVirtual3333RuntimeState SolvedState =
		UHyperTwistVirtual3333ProjectionLibrary::BuildSolvedState();
	TestTrue(TEXT("The Phase 6B solved state must remain structurally valid."), SolvedState.IsStructurallyValid());
	TestEqual(
		TEXT("The Phase 6B solved state must keep the canonical 3x3x3x3 puzzle id."),
		SolvedState.Definition.PuzzleId,
		FString(TEXT("hypercube/3x3x3x3"))
	);
	TestEqual(
		TEXT("The Phase 6B state must track all 81 tesseracts."),
		SolvedState.PositionToPiece.Num(),
		81
	);
	TestEqual(
		TEXT("The Phase 6B state must track all 81 piece orientations."),
		SolvedState.PieceOrientations.Num(),
		81
	);

	static const EHyperTwistVirtual3333Axis VisibleAxes[] = {
		EHyperTwistVirtual3333Axis::X,
		EHyperTwistVirtual3333Axis::Y,
		EHyperTwistVirtual3333Axis::Z,
		EHyperTwistVirtual3333Axis::W
	};
	static const int32 SliceCoordinates[] = {-1, 0, 1};

	for (const EHyperTwistVirtual3333Axis VisibleAxis : VisibleAxes)
	{
		for (const int32 SliceCoordinate : SliceCoordinates)
		{
			const FHyperTwistVirtual3333ProjectionBuildResult ProjectionResult =
				UHyperTwistVirtual3333ProjectionLibrary::BuildVisibleProjection(
					SolvedState,
					VisibleAxis,
					SliceCoordinate
				);

			TestTrue(TEXT("Every canonical Phase 6B slice should project."), ProjectionResult.bProjected);
			TestTrue(TEXT("Every canonical Phase 6B slice projection should remain exact."), ProjectionResult.bExactProjection);
			TestTrue(
				TEXT("Every canonical Phase 6B slice projection should be structurally valid."),
				ProjectionResult.Projection.IsStructurallyValid()
			);
			TestEqual(
				TEXT("Each Phase 6B projection should expose 27 projected tesseracts."),
				ProjectionResult.Projection.Tesseracts.Num(),
				27
			);

			int32 VisibleCellCount = 0;
			for (const FHyperTwistVirtual3333ProjectedTesseract& Tesseract :
				 ProjectionResult.Projection.Tesseracts)
			{
				TestEqual(
					TEXT("Each Phase 6B projected tesseract should expose exactly three visible cells."),
					Tesseract.VisibleCells.Num(),
					3
				);
				VisibleCellCount += Tesseract.VisibleCells.Num();
			}

			TestEqual(
				TEXT("Each Phase 6B projection should expose exactly 81 visible cells."),
				VisibleCellCount,
				81
			);
		}
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistVirtual3333Phase6BSliceTurnContractTest,
	"HyperTwist.CleanRoom.HactarCE.Phase6B.SliceTurnContract",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistVirtual3333Phase6BSliceTurnContractTest::RunTest(const FString& Parameters)
{
	static_cast<void>(Parameters);

	const FHyperTwistVirtual3333RuntimeState SolvedState =
		UHyperTwistVirtual3333ProjectionLibrary::BuildSolvedState();
	const FString SerializedSolvedState =
		HyperTwistVirtual3333Phase6BContractTestInternal::SerializeState(SolvedState);

	static const EHyperTwistVirtual3333Axis SliceAxes[] = {
		EHyperTwistVirtual3333Axis::X,
		EHyperTwistVirtual3333Axis::Y,
		EHyperTwistVirtual3333Axis::Z,
		EHyperTwistVirtual3333Axis::W
	};
	static const int32 SliceCoordinates[] = {-1, 0, 1};
	static const EHyperTwistVirtual3333TurnDirection Directions[] = {
		EHyperTwistVirtual3333TurnDirection::Clockwise,
		EHyperTwistVirtual3333TurnDirection::CounterClockwise
	};

	for (const EHyperTwistVirtual3333Axis SliceAxis : SliceAxes)
	{
		const TArray<EHyperTwistVirtual3333Axis> RotationAxes =
			UHyperTwistVirtual3333ProjectionLibrary::GetAvailableRotationAxesForSliceAxis(
				SliceAxis
			);
		TestEqual(
			TEXT("Each Phase 6B slice axis should expose exactly three rotation choices."),
			RotationAxes.Num(),
			3
		);

		for (const int32 SliceCoordinate : SliceCoordinates)
		{
			for (const EHyperTwistVirtual3333Axis RotationAxis : RotationAxes)
			{
				for (const EHyperTwistVirtual3333TurnDirection Direction : Directions)
				{
					FHyperTwistVirtual3333SliceTurnRequest Request;
					Request.SliceAxis = SliceAxis;
					Request.SliceCoordinate = SliceCoordinate;
					Request.RotationAxis = RotationAxis;
					Request.Direction = Direction;

					const FHyperTwistVirtual3333SliceTurnResult AppliedResult =
						UHyperTwistVirtual3333ProjectionLibrary::ApplySliceTurn(
							SolvedState,
							Request
						);
					TestTrue(TEXT("Every Phase 6B slice turn should apply."), AppliedResult.bApplied);
					TestTrue(
						TEXT("Every Phase 6B slice turn should remain exact."),
						AppliedResult.bExactStateUpdate
					);
					TestTrue(
						TEXT("Every Phase 6B slice turn result should remain structurally valid."),
						AppliedResult.State.IsStructurallyValid()
					);
					TestFalse(
						TEXT("Any Phase 6B quarter turn should disturb the solved state."),
						AppliedResult.State.bIsSolved
					);

					const FHyperTwistVirtual3333ProjectionBuildResult ProjectionResult =
						UHyperTwistVirtual3333ProjectionLibrary::BuildVisibleProjection(
							AppliedResult.State,
							SliceAxis,
							SliceCoordinate
						);
					TestTrue(
						TEXT("Post-turn Phase 6B states should still project."),
						ProjectionResult.bProjected && ProjectionResult.bExactProjection
					);

					FHyperTwistVirtual3333SliceTurnRequest InverseRequest = Request;
					InverseRequest.Direction =
						HyperTwistVirtual3333Phase6BContractTestInternal::InvertDirection(Direction);
					const FHyperTwistVirtual3333SliceTurnResult InverseResult =
						UHyperTwistVirtual3333ProjectionLibrary::ApplySliceTurn(
							AppliedResult.State,
							InverseRequest
						);
					TestTrue(TEXT("Every inverse Phase 6B slice turn should apply."), InverseResult.bApplied);
					TestTrue(
						TEXT("Every inverse Phase 6B slice turn should remain exact."),
						InverseResult.bExactStateUpdate
					);
					TestEqual(
						TEXT("A Phase 6B turn followed by its inverse must recover identity."),
						HyperTwistVirtual3333Phase6BContractTestInternal::SerializeState(InverseResult.State),
						SerializedSolvedState
					);

					FHyperTwistVirtual3333RuntimeState QuadTurnState = SolvedState;
					for (int32 TurnIndex = 0; TurnIndex < 4; ++TurnIndex)
					{
						const FHyperTwistVirtual3333SliceTurnResult QuadResult =
							UHyperTwistVirtual3333ProjectionLibrary::ApplySliceTurn(
								QuadTurnState,
								Request
							);
						TestTrue(TEXT("Every quarter-turn in the four-turn identity proof should apply."), QuadResult.bApplied);
						TestTrue(TEXT("Every quarter-turn in the four-turn identity proof should remain exact."), QuadResult.bExactStateUpdate);
						QuadTurnState = QuadResult.State;
					}

					TestEqual(
						TEXT("Four Phase 6B quarter turns must recover identity."),
						HyperTwistVirtual3333Phase6BContractTestInternal::SerializeState(QuadTurnState),
						SerializedSolvedState
					);
				}
			}
		}
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistVirtual3333Phase6BSceneContextContractTest,
	"HyperTwist.CleanRoom.HactarCE.Phase6B.SceneContextContract",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistVirtual3333Phase6BSceneContextContractTest::RunTest(const FString& Parameters)
{
	static_cast<void>(Parameters);

	const FHyperTwistSimulationSceneContext SceneContext =
		UHyperTwistSimulationLibrary::MakeVirtual3333VisibleSceneContext();

	TestTrue(TEXT("The Phase 6B scene context should be structurally valid."), SceneContext.IsStructurallyValid());
	TestTrue(TEXT("The Phase 6B scene context should be renderable."), UHyperTwistSimulationLibrary::CanRenderSceneContext(SceneContext));
	TestEqual(
		TEXT("The Phase 6B scene context should keep the canonical 3x3x3x3 puzzle id."),
		SceneContext.PuzzleState.Definition.PuzzleId,
		FString(TEXT("hypercube/3x3x3x3"))
	);
	TestEqual(
		TEXT("The Phase 6B scene context should use the visible-slice projection profile."),
		SceneContext.ProjectionSettings.ProjectionProfile,
		FString(TEXT("hypercube-3x3x3x3-visible-slice-projection-v1"))
	);
	TestEqual(
		TEXT("The Phase 6B scene context should advertise the owned procedural runtime."),
		SceneContext.RenderStateProfile,
		FString(TEXT("virtual-3x3x3x3-visible-slice-procedural-runtime-v1"))
	);

	return true;
}

#endif
