// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistMemoryPhase6RM3TestInternal
{
	const FHyperTwistMemoryRecallMatch* FindRecallMatchByRecordKind(
		const FHyperTwistMemoryRecallSharedContextState& RecallState,
		const FString& SourceRecordKind
	)
	{
		for (const FHyperTwistMemoryRecallMatch& RecallMatch : RecallState.RecallMatches)
		{
			if (RecallMatch.SourceRecordKind == SourceRecordKind)
			{
				return &RecallMatch;
			}
		}

		return nullptr;
	}

	const FHyperTwistMemoryRecallMatch* FindRecallMatchByKind(
		const FHyperTwistMemoryRecallSharedContextState& RecallState,
		const FString& MatchKind
	)
	{
		for (const FHyperTwistMemoryRecallMatch& RecallMatch : RecallState.RecallMatches)
		{
			if (RecallMatch.MatchKind == MatchKind)
			{
				return &RecallMatch;
			}
		}

		return nullptr;
	}

	const FHyperTwistMemorySharedContextProjection* FindProjectionByContextId(
		const FHyperTwistMemoryRecallSharedContextState& RecallState,
		const FString& ContextId
	)
	{
		for (const FHyperTwistMemorySharedContextProjection& Projection :
			RecallState.SharedContextProjections)
		{
			if (Projection.ContextId == ContextId)
			{
				return &Projection;
			}
		}

		return nullptr;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMemoryPhase6RM3RecallSearchTest,
	"HyperTwist.FirstParty.Memory.Phase6R.M3.RecallSearch",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMemoryPhase6RM3RecallSearchTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryRecallSharedContextState RecallState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryRecallSharedContextState();

	TestTrue(
		TEXT("The Phase 6R-M3 recall/shared-context state must be structurally valid."),
		RecallState.IsStructurallyValid()
	);
	TestTrue(
		TEXT("The M3 state must preserve a non-empty explicit recall query."),
		!RecallState.QueryText.IsEmpty()
	);
	TestTrue(
		TEXT("The M3 state must preserve non-empty query tokens."),
		RecallState.QueryTokens.Num() > 0
	);
	TestTrue(
		TEXT("The M3 state must expose bounded recall results."),
		RecallState.RecallMatches.Num() >= 4
	);
	TestTrue(
		TEXT("The M3 state must expose semi-authoritative recall results."),
		RecallState.SemiAuthoritativeRecallCount > 0
	);

	const FHyperTwistMemoryRecallMatch* ReviewProgramMatch =
		HyperTwistMemoryPhase6RM3TestInternal::FindRecallMatchByRecordKind(
			RecallState,
			TEXT("review-program-summary")
		);
	TestNotNull(
		TEXT("A review-program summary recall match must exist."),
		ReviewProgramMatch
	);
	if (ReviewProgramMatch != nullptr)
	{
		TestEqual(
			TEXT("The review-program summary must stay in the recall lane."),
			ReviewProgramMatch->Lane,
			EHyperTwistMemoryLane::RecallRetrieval
		);
		TestTrue(
			TEXT("The review-program summary must preserve provenance links."),
			ReviewProgramMatch->ProvenanceLinks.Num() > 0
		);
	}

	const FHyperTwistMemoryRecallMatch* ResumeMatch =
		HyperTwistMemoryPhase6RM3TestInternal::FindRecallMatchByKind(
			RecallState,
			TEXT("remember-focus")
		);
	TestNotNull(TEXT("A bounded remember-focus recall match must exist."), ResumeMatch);
	if (ResumeMatch != nullptr)
	{
		TestTrue(
			TEXT("The bounded remember-focus match must preserve recall reasons."),
			ResumeMatch->MatchReasonLines.Num() > 0
		);
		TestTrue(
			TEXT("The remember-focus match must keep a positive score."),
			ResumeMatch->MatchScore > 0
		);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMemoryPhase6RM3SharedContextRulesTest,
	"HyperTwist.FirstParty.Memory.Phase6R.M3.SharedContextRules",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMemoryPhase6RM3SharedContextRulesTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryRecallSharedContextState RecallState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryRecallSharedContextState();

	TestTrue(
		TEXT("The Phase 6R-M3 recall/shared-context state must be structurally valid."),
		RecallState.IsStructurallyValid()
	);
	TestTrue(
		TEXT("The M3 state must expose bounded shared-context rules."),
		RecallState.SharedContextRules.Num() >= 3
	);
	TestTrue(
		TEXT("The M3 state must expose scoped shared-context projections."),
		RecallState.SharedContextProjections.Num() >= 2
	);
	TestTrue(
		TEXT("The M3 state must explicitly report scoped shared-context."),
		RecallState.bHasScopedSharedContext
	);

	const FHyperTwistMemorySharedContextProjection* CoachProjection =
		HyperTwistMemoryPhase6RM3TestInternal::FindProjectionByContextId(
			RecallState,
			TEXT("coach-follow-up")
		);
	TestNotNull(TEXT("A coach follow-up shared-context projection must exist."), CoachProjection);
	if (CoachProjection != nullptr)
	{
		TestTrue(
			TEXT("Coach follow-up context must stay revocable."),
			CoachProjection->bRevocable
		);
		TestTrue(
			TEXT("Coach follow-up context must remain session-local."),
			CoachProjection->bSessionLocalOnly
		);
		TestTrue(
			TEXT("Coach follow-up context must preserve related recall matches."),
			CoachProjection->RelatedMatchIds.Num() > 0
		);
		TestTrue(
			TEXT("Coach follow-up context must preserve related entity ids."),
			CoachProjection->RelatedEntityIds.Num() > 0
		);
	}

	const FHyperTwistMemorySharedContextProjection* SidecarProjection =
		HyperTwistMemoryPhase6RM3TestInternal::FindProjectionByContextId(
			RecallState,
			TEXT("sidecar-brief")
		);
	TestNotNull(TEXT("A sidecar brief shared-context projection must exist."), SidecarProjection);
	if (SidecarProjection != nullptr)
	{
		TestTrue(
			TEXT("The sidecar brief must require provenance-qualified inputs."),
			SidecarProjection->bRequiresAuthoritativeProvenance
		);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMemoryPhase6RM3SerializationRoundTripTest,
	"HyperTwist.FirstParty.Memory.Phase6R.M3.SerializationRoundTrip",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMemoryPhase6RM3SerializationRoundTripTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryRecallSharedContextState RecallState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryRecallSharedContextState();
	TestTrue(
		TEXT("The Phase 6R-M3 recall/shared-context state must be structurally valid."),
		RecallState.IsStructurallyValid()
	);

	const FString Json =
		UHyperTwistContractLibrary::SerializeMemoryRecallSharedContextStateToJson(
			RecallState
		);
	TestFalse(TEXT("The serialized M3 JSON must not be empty."), Json.IsEmpty());
	TestTrue(
		TEXT("The serialized M3 JSON must expose the coach follow-up projection."),
		Json.Contains(TEXT("coach-follow-up"))
	);

	FHyperTwistMemoryRecallSharedContextState RoundTrippedState;
	TestTrue(
		TEXT("The M3 JSON must deserialize cleanly."),
		UHyperTwistContractLibrary::DeserializeMemoryRecallSharedContextStateFromJson(
			Json,
			RoundTrippedState
		)
	);
	TestTrue(
		TEXT("The round-tripped M3 state must remain structurally valid."),
		RoundTrippedState.IsStructurallyValid()
	);
	TestEqual(
		TEXT("The round-tripped M3 state must preserve recall-match counts."),
		RoundTrippedState.RecallMatches.Num(),
		RecallState.RecallMatches.Num()
	);
	TestEqual(
		TEXT("The round-tripped M3 state must preserve provenance drill-down counts."),
		RoundTrippedState.ProvenanceDrillDownEntries.Num(),
		RecallState.ProvenanceDrillDownEntries.Num()
	);
	TestEqual(
		TEXT("The round-tripped M3 state must preserve shared-context projection counts."),
		RoundTrippedState.SharedContextProjections.Num(),
		RecallState.SharedContextProjections.Num()
	);
	TestEqual(
		TEXT("The round-tripped M3 state must preserve scoped shared-context flags."),
		RoundTrippedState.bHasScopedSharedContext,
		RecallState.bHasScopedSharedContext
	);

	return true;
}

#endif
