// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
#include "HyperTwistCore/HyperTwistCoreLibrary.h"
#include "JsonObjectConverter.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistMelindaBound1CoreTestInternal
{
	template <typename TStruct>
	FString SerializeStructToJson(const TStruct& Value)
	{
		FString Json;
		FJsonObjectConverter::UStructToJsonObjectString(TStruct::StaticStruct(), &Value, Json, 0, 0);
		return Json;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMelindaBound1CoreTest,
	"HyperTwist.CleanRoom.HactarCE.Bound1.CoreStateAndTransform",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMelindaBound1CoreTest::RunTest(const FString& Parameters)
{
	const FHyperTwistPuzzleState SolvedState = UHyperTwistContractLibrary::MakeSampleHyperPuzzleState();

	TestEqual(TEXT("Bound 1 keeps the Melinda puzzle id."), SolvedState.Definition.PuzzleId, FString(TEXT("hypercube/2x2x2x2")));
	TestEqual(TEXT("Bound 1 uses the Melinda state profile."), SolvedState.StateEncoding.EncodingProfile, FString(TEXT("melinda-2x2x2x2-state-v1")));
	TestTrue(TEXT("Sample Melinda state must stay structurally valid."), SolvedState.IsStructurallyValid());
	TestTrue(TEXT("Sample Melinda state must be structurally solved."), UHyperTwistCoreLibrary::IsSolved(SolvedState));

	const FString SerializedSolvedState = UHyperTwistContractLibrary::SerializePuzzleStateToJson(SolvedState);
	FHyperTwistPuzzleState RoundTrippedState;
	TestTrue(TEXT("Sample Melinda state must deserialize after JSON round-trip."), UHyperTwistContractLibrary::DeserializePuzzleStateFromJson(SerializedSolvedState, RoundTrippedState));
	TestTrue(TEXT("Round-tripped Melinda state must remain solved."), UHyperTwistCoreLibrary::IsSolved(RoundTrippedState));

	FHyperTwistMelinda2x2x2x2TransformEncoding MoveEncoding;
	MoveEncoding.MoveId = TEXT("bound1.xy-demo");
	MoveEncoding.PositionPullMap = {3, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
	MoveEncoding.OrientationDeltaPerNewPosition = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

	FHyperTwistTransformation Move;
	Move.Definition = SolvedState.Definition;
	Move.TransformKind = EHyperTwistTransformKind::SingleMove;
	Move.Notation = TEXT("XY");
	Move.TransformEncoding.EncodingProfile = MoveEncoding.EncodingProfile;
	Move.TransformEncoding.PayloadJson = HyperTwistMelindaBound1CoreTestInternal::SerializeStructToJson(MoveEncoding);
	Move.OriginalNotation = Move.Notation;

	const FHyperTwistApplyTransformationResult Applied = UHyperTwistCoreLibrary::ApplyTransformation(SolvedState, Move);
	TestTrue(TEXT("Melinda transform should apply."), Applied.bApplied);
	TestTrue(TEXT("Melinda transform should produce an exact state update."), Applied.bExactStateUpdate);
	TestFalse(TEXT("Exact Melinda transforms should not emit the placeholder warning."), Applied.Warnings.Contains(TEXT("exact-piece-update-pending")));
	TestFalse(TEXT("A non-identity Melinda transform should disturb the solved state."), UHyperTwistCoreLibrary::IsSolved(Applied.State));
	TestFalse(TEXT("A non-identity Melinda transform should clear bIsSolved."), Applied.State.bIsSolved);
	TestTrue(TEXT("Exact Melinda transforms should clear placeholder notes."), Applied.State.Notes.IsEmpty());

	FHyperTwistMelinda2x2x2x2StateEncoding AppliedPayload;
	TestTrue(TEXT("Applied Melinda state payload must deserialize."), FJsonObjectConverter::JsonObjectStringToUStruct(Applied.State.StateEncoding.PayloadJson, &AppliedPayload, 0, 0));
	TestEqual(TEXT("Move should rotate position 0 from old position 3."), AppliedPayload.PositionToPiece[0], 3);
	TestEqual(TEXT("Move should rotate position 1 from old position 0."), AppliedPayload.PositionToPiece[1], 0);
	TestEqual(TEXT("Move should rotate position 2 from old position 1."), AppliedPayload.PositionToPiece[2], 1);
	TestEqual(TEXT("Move should rotate position 3 from old position 2."), AppliedPayload.PositionToPiece[3], 2);
	TestTrue(TEXT("Bound 1 keeps identity orientations for the demo transform."), AppliedPayload.PieceOrientation == TArray<int32>({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));

	return true;
}

#endif // WITH_AUTOMATION_TESTS
