// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistMemoryPhase6RM1TestInternal
{
	const FHyperTwistMemoryLaneContract* FindLaneContract(
		const FHyperTwistMemoryLedgerState& LedgerState,
		const EHyperTwistMemoryLane Lane
	)
	{
		for (const FHyperTwistMemoryLaneContract& LaneContract : LedgerState.LaneContracts)
		{
			if (LaneContract.Lane == Lane)
			{
				return &LaneContract;
			}
		}

		return nullptr;
	}

	const FHyperTwistMemoryEntityDescriptor* FindEntityByRecordKind(
		const FHyperTwistMemoryLedgerState& LedgerState,
		const FString& RecordKind
	)
	{
		for (const FHyperTwistMemoryEntityDescriptor& Entity : LedgerState.Entities)
		{
			if (Entity.RecordKind == RecordKind)
			{
				return &Entity;
			}
		}

		return nullptr;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMemoryPhase6RM1LaneContractsTest,
	"HyperTwist.FirstParty.Memory.Phase6R.M1.LaneContracts",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMemoryPhase6RM1LaneContractsTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryLedgerState LedgerState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryLedgerState();

	TestTrue(TEXT("The Phase 6R-M1 memory ledger must be structurally valid."), LedgerState.IsStructurallyValid());
	TestEqual(TEXT("The Phase 6R-M1 ledger must expose all memory lane contracts."), LedgerState.LaneContracts.Num(), 12);
	TestEqual(TEXT("The Phase 6R-M1 ledger must expose one feature gate per lane."), LedgerState.FeatureGates.Num(), 12);
	TestEqual(TEXT("The economic retention profile id must stay canonical."), LedgerState.EconomicRetentionProfile.ProfileId, TEXT("memory-profile/economic-retention"));
	TestEqual(TEXT("The max retention profile id must stay canonical."), LedgerState.MaxRetentionProfile.ProfileId, TEXT("memory-profile/max-retention"));
	TestEqual(TEXT("The economic retention profile must stay canonical."), LedgerState.EconomicRetentionProfile.ContextAssemblyProfile, EHyperTwistMemoryContextAssemblyProfile::EconomicRetentionMode);
	TestEqual(TEXT("The max retention profile must stay canonical."), LedgerState.MaxRetentionProfile.ContextAssemblyProfile, EHyperTwistMemoryContextAssemblyProfile::MaxRetentionMode);

	const FHyperTwistMemoryLaneContract* ChronicleLane =
		HyperTwistMemoryPhase6RM1TestInternal::FindLaneContract(
			LedgerState,
			EHyperTwistMemoryLane::ChronicleCapture
		);
	TestNotNull(TEXT("The chronicle lane contract must exist."), ChronicleLane);
	if (ChronicleLane != nullptr)
	{
		TestEqual(TEXT("The chronicle lane id must stay canonical."), ChronicleLane->LaneId, TEXT("memory-lane/chronicle-capture"));
		TestEqual(TEXT("The chronicle lane must remain authoritative."), ChronicleLane->AuthorityKind, EHyperTwistMemoryAuthorityKind::Authoritative);
	}

	const FHyperTwistMemoryLaneContract* SharedCoordinationLane =
		HyperTwistMemoryPhase6RM1TestInternal::FindLaneContract(
			LedgerState,
			EHyperTwistMemoryLane::SharedCoordination
		);
	TestNotNull(TEXT("The shared coordination lane contract must exist."), SharedCoordinationLane);
	if (SharedCoordinationLane != nullptr)
	{
		TestEqual(TEXT("The shared coordination lane id must stay canonical."), SharedCoordinationLane->LaneId, TEXT("memory-lane/shared-coordination"));
		TestEqual(TEXT("The shared coordination lane must remain semi-authoritative."), SharedCoordinationLane->AuthorityKind, EHyperTwistMemoryAuthorityKind::SemiAuthoritative);
	}

	const FHyperTwistMemoryLaneContract* DerivedLane =
		HyperTwistMemoryPhase6RM1TestInternal::FindLaneContract(
			LedgerState,
			EHyperTwistMemoryLane::DerivedCompacted
		);
	TestNotNull(TEXT("The derived-compacted lane contract must exist."), DerivedLane);
	if (DerivedLane != nullptr)
	{
		TestEqual(TEXT("The derived-compacted lane id must stay canonical."), DerivedLane->LaneId, TEXT("memory-lane/derived-compacted"));
		TestEqual(TEXT("The derived-compacted lane must remain derived-only."), DerivedLane->AuthorityKind, EHyperTwistMemoryAuthorityKind::DerivedOnly);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistMemoryPhase6RM1DerivedProvenanceTest,
	"HyperTwist.FirstParty.Memory.Phase6R.M1.DerivedProvenance",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistMemoryPhase6RM1DerivedProvenanceTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryLedgerState LedgerState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryLedgerState();

	TestTrue(TEXT("The Phase 6R-M1 memory ledger must be structurally valid."), LedgerState.IsStructurallyValid());
	TestTrue(TEXT("The memory ledger must expose authoritative entities."), LedgerState.AuthoritativeEntityCount > 0);
	TestTrue(TEXT("The memory ledger must expose semi-authoritative entities."), LedgerState.SemiAuthoritativeEntityCount > 0);
	TestTrue(TEXT("The memory ledger must expose derived entities."), LedgerState.DerivedEntityCount > 0);

	const FHyperTwistMemoryEntityDescriptor* LearnerStateEntity =
		HyperTwistMemoryPhase6RM1TestInternal::FindEntityByRecordKind(
			LedgerState,
			TEXT("case-learner-state")
		);
	TestNotNull(TEXT("A semi-authoritative case learner-state entity must exist."), LearnerStateEntity);
	if (LearnerStateEntity != nullptr)
	{
		TestEqual(TEXT("The case learner-state entity must remain in the cognitive lane."), LearnerStateEntity->Lane, EHyperTwistMemoryLane::CognitiveConsolidated);
		TestEqual(TEXT("The case learner-state entity must remain semi-authoritative."), LearnerStateEntity->AuthorityKind, EHyperTwistMemoryAuthorityKind::SemiAuthoritative);
		TestTrue(TEXT("The case learner-state entity must remain derived from chronicle evidence."), LearnerStateEntity->bDerived);
		TestTrue(TEXT("The case learner-state entity must preserve provenance."), LearnerStateEntity->ProvenanceLinks.Num() > 0);
	}

	const FHyperTwistMemoryEntityDescriptor* ReviewProgramEntity =
		HyperTwistMemoryPhase6RM1TestInternal::FindEntityByRecordKind(
			LedgerState,
			TEXT("review-program-summary")
		);
	TestNotNull(TEXT("A review-program summary entity must exist."), ReviewProgramEntity);
	if (ReviewProgramEntity != nullptr)
	{
		TestEqual(TEXT("The review-program summary must remain in the recall lane."), ReviewProgramEntity->Lane, EHyperTwistMemoryLane::RecallRetrieval);
		TestEqual(TEXT("The review-program summary must remain semi-authoritative."), ReviewProgramEntity->AuthorityKind, EHyperTwistMemoryAuthorityKind::SemiAuthoritative);
		TestTrue(TEXT("The review-program summary must preserve provenance."), ReviewProgramEntity->ProvenanceLinks.Num() > 0);
	}

	const FHyperTwistMemoryEntityDescriptor* LearnerProfileEntity =
		HyperTwistMemoryPhase6RM1TestInternal::FindEntityByRecordKind(
			LedgerState,
			TEXT("learner-profile-summary")
		);
	TestNotNull(TEXT("A learner-profile summary entity must exist."), LearnerProfileEntity);
	if (LearnerProfileEntity != nullptr)
	{
		TestEqual(TEXT("The learner-profile summary must remain in the derived lane."), LearnerProfileEntity->Lane, EHyperTwistMemoryLane::DerivedCompacted);
		TestEqual(TEXT("The learner-profile summary must remain derived-only."), LearnerProfileEntity->AuthorityKind, EHyperTwistMemoryAuthorityKind::DerivedOnly);
		TestTrue(TEXT("The learner-profile summary must preserve provenance."), LearnerProfileEntity->ProvenanceLinks.Num() > 0);
	}

	return true;
}

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

bool FHyperTwistMemoryPhase6RM1SerializationRoundTripTest::RunTest(const FString& Parameters)
{
	const FHyperTwistMemoryLedgerState LedgerState =
		UHyperTwistContractLibrary::MakeSampleTrainingMemoryLedgerState();
	TestTrue(TEXT("The Phase 6R-M1 memory ledger must be structurally valid."), LedgerState.IsStructurallyValid());

	const FString Json = UHyperTwistContractLibrary::SerializeMemoryLedgerStateToJson(LedgerState);
	TestFalse(TEXT("The serialized memory ledger JSON must not be empty."), Json.IsEmpty());
	TestTrue(TEXT("The serialized memory ledger JSON must expose the chronicle lane id."), Json.Contains(TEXT("memory-lane/chronicle-capture")));

	FHyperTwistMemoryLedgerState RoundTrippedLedgerState;
	TestTrue(TEXT("The memory ledger JSON must deserialize cleanly."), UHyperTwistContractLibrary::DeserializeMemoryLedgerStateFromJson(Json, RoundTrippedLedgerState));
	TestTrue(TEXT("The round-tripped memory ledger must remain structurally valid."), RoundTrippedLedgerState.IsStructurallyValid());
	TestEqual(TEXT("The round-tripped memory ledger must preserve lane counts."), RoundTrippedLedgerState.LaneContracts.Num(), LedgerState.LaneContracts.Num());
	TestEqual(TEXT("The round-tripped memory ledger must preserve entity counts."), RoundTrippedLedgerState.Entities.Num(), LedgerState.Entities.Num());
	TestEqual(TEXT("The round-tripped memory ledger must preserve authoritative counts."), RoundTrippedLedgerState.AuthoritativeEntityCount, LedgerState.AuthoritativeEntityCount);
	TestEqual(TEXT("The round-tripped memory ledger must preserve semi-authoritative counts."), RoundTrippedLedgerState.SemiAuthoritativeEntityCount, LedgerState.SemiAuthoritativeEntityCount);
	TestEqual(TEXT("The round-tripped memory ledger must preserve derived counts."), RoundTrippedLedgerState.DerivedEntityCount, LedgerState.DerivedEntityCount);
	TestEqual(TEXT("The round-tripped memory ledger must preserve the economic profile id."), RoundTrippedLedgerState.EconomicRetentionProfile.ProfileId, LedgerState.EconomicRetentionProfile.ProfileId);
	TestEqual(TEXT("The round-tripped memory ledger must preserve the max profile id."), RoundTrippedLedgerState.MaxRetentionProfile.ProfileId, LedgerState.MaxRetentionProfile.ProfileId);
	return true;
}

#endif
