// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistSkillPhaseS1ATestInternal
{
	const FHyperTwistSkillManifestEntry* FindSkillById(
		const FHyperTwistSkillRegistryState& RegistryState,
		const FString& SkillId
	)
	{
		for (const FHyperTwistSkillManifestEntry& Entry : RegistryState.Entries)
		{
			if (Entry.SkillId == SkillId)
			{
				return &Entry;
			}
		}

		return nullptr;
	}

	int32 FindStatusCount(
		const FHyperTwistSkillRegistryState& RegistryState,
		const EHyperTwistSkillStatus Status
	)
	{
		for (const FHyperTwistSkillStatusCount& StatusCount : RegistryState.StatusCounts)
		{
			if (StatusCount.Status == Status)
			{
				return StatusCount.Count;
			}
		}

		return 0;
	}

	int32 FindFamilyCount(
		const FHyperTwistSkillRegistryState& RegistryState,
		const EHyperTwistSkillFamily Family
	)
	{
		for (const FHyperTwistSkillFamilyCount& FamilyCount : RegistryState.FamilyCounts)
		{
			if (FamilyCount.Family == Family)
			{
				return FamilyCount.Count;
			}
		}

		return 0;
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistSkillPhaseS1ARegistryManifestTest,
	"HyperTwist.FirstParty.Skill.PhaseS1A.RegistryManifest",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistSkillPhaseS1ARegistryManifestTest::RunTest(const FString& Parameters)
{
	const FHyperTwistSkillRegistryState RegistryState =
		UHyperTwistContractLibrary::MakeSampleSkillRegistryState();

	TestTrue(
		TEXT("The Phase S1-A skill registry must be structurally valid."),
		RegistryState.IsStructurallyValid()
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose forty declared skills after S7-C lands."),
		RegistryState.SkillCount,
		40
	);
	TestTrue(
		TEXT("The S1-A registry must avoid ad hoc metadata."),
		RegistryState.bNoAdHocMetadataRequired
	);
	TestTrue(
		TEXT("The S1-A registry must keep the whole family optional-assistive."),
		RegistryState.bAllSkillsOptionalAssistive
	);
	TestEqual(
		TEXT("The sample S1-A registry must keep thirty-seven implemented skill entries after S7-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindStatusCount(
			RegistryState,
			EHyperTwistSkillStatus::ImplementedNow
		),
		37
	);
	TestEqual(
		TEXT("The sample S1-A registry must keep one command-contract-pending entry for note capture."),
		HyperTwistSkillPhaseS1ATestInternal::FindStatusCount(
			RegistryState,
			EHyperTwistSkillStatus::CommandContractPending
		),
		1
	);
	TestEqual(
		TEXT("The sample S1-A registry must keep one clean-room skill-spec-specified entry after S2-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindStatusCount(
			RegistryState,
			EHyperTwistSkillStatus::CleanRoomSkillSpecSpecified
		),
		1
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose one governance-family entry."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::SubstrateGovernance
		),
		1
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose eight analysis/review-family entries after S5-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::AnalysisReview
		),
		8
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose eight memory-continuity-family entries after S3-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::MemoryContinuity
		),
		8
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose six browser/docs/design-family entries after S4-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::BrowserDocsDesign
		),
		6
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose seven provider/operations-family entries after S6-B lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::ProviderOperations
		),
		7
	);
	TestEqual(
		TEXT("The sample S1-A registry must expose nine domain/creative-family entries after S7-C lands."),
		HyperTwistSkillPhaseS1ATestInternal::FindFamilyCount(
			RegistryState,
			EHyperTwistSkillFamily::DomainCreative
		),
		9
	);

	const FHyperTwistSkillManifestEntry* ReplayExplainerSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/compose-replay-explainer-pack")
		);
	TestNotNull(
		TEXT("The compose-replay-explainer-pack skill entry must exist."),
		ReplayExplainerSkill
	);
	if (ReplayExplainerSkill != nullptr)
	{
		TestEqual(
			TEXT("The compose-replay-explainer-pack skill must be implemented now."),
			ReplayExplainerSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The compose-replay-explainer-pack skill must bind to the first-party replay-explainer pack service."),
			ReplayExplainerSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/compose-replay-explainer-pack"))
		);
	}

	const FHyperTwistSkillManifestEntry* GovernanceSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/skillization-governance")
		);
	TestNotNull(
		TEXT("The governance skill entry must exist."),
		GovernanceSkill
	);
	if (GovernanceSkill != nullptr)
	{
		TestEqual(
			TEXT("The governance skill must be implemented now."),
			GovernanceSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestTrue(
			TEXT("The governance skill must expose a command binding."),
			GovernanceSkill->CommandBindings.Num() > 0
		);
		TestEqual(
			TEXT("The governance skill must bind to the first-party registry service."),
			GovernanceSkill->CommandBindings[0].ServiceBindingId,
			TEXT("service/skills/registry-state")
		);
	}

	const FHyperTwistSkillManifestEntry* RecallMemorySkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/recall-memory")
		);
	TestNotNull(
		TEXT("The recall-memory skill entry must exist."),
		RecallMemorySkill
	);
	if (RecallMemorySkill != nullptr)
	{
		TestEqual(
			TEXT("The recall-memory skill must be implemented now."),
			RecallMemorySkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The recall-memory skill must bind to the first-party recall-memory service."),
			RecallMemorySkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/recall-memory"))
		);
	}

	const FHyperTwistSkillManifestEntry* WorkflowCaptureSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/capture-session-template")
		);
	TestNotNull(
		TEXT("The capture-session-template skill entry must exist."),
		WorkflowCaptureSkill
	);
	if (WorkflowCaptureSkill != nullptr)
	{
		TestEqual(
			TEXT("The capture-session-template skill must be implemented now."),
			WorkflowCaptureSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The capture-session-template skill must bind to the first-party workflow capture service."),
			WorkflowCaptureSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/capture-session-template"))
		);
	}

	const FHyperTwistSkillManifestEntry* ImplementationReviewSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/review-implementation")
		);
	TestNotNull(
		TEXT("The review-implementation skill entry must exist."),
		ImplementationReviewSkill
	);
	if (ImplementationReviewSkill != nullptr)
	{
		TestEqual(
			TEXT("The review-implementation skill must be implemented now."),
			ImplementationReviewSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The review-implementation skill must bind to the first-party implementation-review service."),
			ImplementationReviewSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/specs/review-implementation"))
		);
	}

	const FHyperTwistSkillManifestEntry* PlanSynthesisSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/synthesize-plan")
		);
	TestNotNull(
		TEXT("The synthesize-plan skill entry must exist."),
		PlanSynthesisSkill
	);
	if (PlanSynthesisSkill != nullptr)
	{
		TestEqual(
			TEXT("The synthesize-plan skill must be implemented now."),
			PlanSynthesisSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The synthesize-plan skill must bind to the first-party plan-synthesis service."),
			PlanSynthesisSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/synthesize-plan"))
		);
	}

	const FHyperTwistSkillManifestEntry* ProviderProfileSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/inspect-provider-profile")
		);
	TestNotNull(
		TEXT("The inspect-provider-profile skill entry must exist."),
		ProviderProfileSkill
	);
	if (ProviderProfileSkill != nullptr)
	{
		TestEqual(
			TEXT("The inspect-provider-profile skill must be implemented now."),
			ProviderProfileSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The inspect-provider-profile skill must bind to the first-party provider-profile inspection service."),
			ProviderProfileSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/inspect-provider-profile"))
		);
	}

	const FHyperTwistSkillManifestEntry* UsageAuditSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/audit-provider-usage")
		);
	TestNotNull(
		TEXT("The audit-provider-usage skill entry must exist."),
		UsageAuditSkill
	);
	if (UsageAuditSkill != nullptr)
	{
		TestEqual(
			TEXT("The audit-provider-usage skill must be implemented now."),
			UsageAuditSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The audit-provider-usage skill must bind to the first-party provider-usage audit service."),
			UsageAuditSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/audit-provider-usage"))
		);
	}

	const FHyperTwistSkillManifestEntry* DomainPackagingSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/specify-domain-packaging")
		);
	TestNotNull(
		TEXT("The specify-domain-packaging skill entry must exist."),
		DomainPackagingSkill
	);
	if (DomainPackagingSkill != nullptr)
	{
		TestEqual(
			TEXT("The specify-domain-packaging skill must be implemented now."),
			DomainPackagingSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The specify-domain-packaging skill must bind to the first-party domain-packaging service."),
			DomainPackagingSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/specify-domain-packaging"))
		);
	}

	const FHyperTwistSkillManifestEntry* TrainingReplayPackSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/review-training-replay-pack")
		);
	TestNotNull(
		TEXT("The review-training-replay-pack skill entry must exist."),
		TrainingReplayPackSkill
	);
	if (TrainingReplayPackSkill != nullptr)
	{
		TestEqual(
			TEXT("The review-training-replay-pack skill must be implemented now."),
			TrainingReplayPackSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The review-training-replay-pack skill must bind to the first-party training-replay review pack service."),
			TrainingReplayPackSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/review-training-replay-pack"))
		);
	}

	const FHyperTwistSkillManifestEntry* DocsExtractionSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/extract-docs")
		);
	TestNotNull(
		TEXT("The extract-docs skill entry must exist."),
		DocsExtractionSkill
	);
	if (DocsExtractionSkill != nullptr)
	{
		TestEqual(
			TEXT("The extract-docs skill must be implemented now."),
			DocsExtractionSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The extract-docs skill must bind to the first-party docs extraction service."),
			DocsExtractionSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/extract-docs"))
		);
	}

	const FHyperTwistSkillManifestEntry* BrowserTraceSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/capture-browser-trace")
		);
	TestNotNull(
		TEXT("The capture-browser-trace skill entry must exist."),
		BrowserTraceSkill
	);
	if (BrowserTraceSkill != nullptr)
	{
		TestEqual(
			TEXT("The capture-browser-trace skill must be implemented now."),
			BrowserTraceSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The capture-browser-trace skill must bind to the first-party browser-trace service."),
			BrowserTraceSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/capture-browser-trace"))
		);
	}

	const FHyperTwistSkillManifestEntry* DesignShellSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/translate-design-shell")
		);
	TestNotNull(
		TEXT("The translate-design-shell skill entry must exist."),
		DesignShellSkill
	);
	if (DesignShellSkill != nullptr)
	{
		TestEqual(
			TEXT("The translate-design-shell skill must be implemented now."),
			DesignShellSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The translate-design-shell skill must bind to the first-party design-shell translation service."),
			DesignShellSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/translate-design-shell"))
		);
	}

	const FHyperTwistSkillManifestEntry* WorkflowReviewSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/review-proposal")
		);
	TestNotNull(
		TEXT("The review-proposal skill entry must exist."),
		WorkflowReviewSkill
	);
	if (WorkflowReviewSkill != nullptr)
	{
		TestEqual(
			TEXT("The review-proposal skill must be implemented now."),
			WorkflowReviewSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The review-proposal skill must bind to the first-party proposal-review service."),
			WorkflowReviewSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/review-proposal"))
		);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistSkillPhaseS1ADisabledSkillsRemainInstalledTest,
	"HyperTwist.FirstParty.Skill.PhaseS1A.DisabledSkillsRemainInstalled",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistSkillPhaseS1ADisabledSkillsRemainInstalledTest::RunTest(
	const FString& Parameters
)
{
	const FHyperTwistSkillRegistryState RegistryState =
		UHyperTwistContractLibrary::MakeSampleSkillRegistryState();

	TestTrue(
		TEXT("The S1-A registry must confirm disabled skills remain installed."),
		RegistryState.bDisabledSkillsRemainInstalled
	);
	TestTrue(
		TEXT("The S1-A registry must confirm disabled skills are inert."),
		RegistryState.bDisabledSkillsAreInert
	);
	TestTrue(
		TEXT("The sample S1-A registry must keep disabled-by-default entries."),
		RegistryState.DisabledByDefaultCount > 0
	);

	const FHyperTwistSkillManifestEntry* ProviderSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/provider-routing-inspector")
		);
	TestNotNull(
		TEXT("A disabled provider-routing-inspector skill must exist."),
		ProviderSkill
	);
	if (ProviderSkill != nullptr)
	{
		TestFalse(
			TEXT("The provider skill must start disabled by default."),
			ProviderSkill->bEnabledByDefault
		);
		TestTrue(
			TEXT("The provider skill must remain installed when disabled."),
			ProviderSkill->bInstalledWhenDisabled
		);
		TestTrue(
			TEXT("The provider skill must stay inert when disabled."),
			ProviderSkill->bInertWhenDisabled
		);
		TestEqual(
			TEXT("The provider skill must now be implemented while remaining opt-in."),
			ProviderSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
	}

	const FHyperTwistSkillManifestEntry* RouxSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/roux-session-review")
		);
	TestNotNull(
		TEXT("A Roux session review skill entry must exist."),
		RouxSkill
	);
	if (RouxSkill != nullptr)
	{
		TestEqual(
			TEXT("The Roux skill should now carry a clean-room skill-spec-specified status."),
			RouxSkill->Status,
			EHyperTwistSkillStatus::CleanRoomSkillSpecSpecified
		);
		TestEqual(
			TEXT("The Roux skill should now point to the first-party clean-room contract service id."),
			RouxSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/specs/training-review/roux-session-review"))
		);
	}

	const FHyperTwistSkillManifestEntry* CaptureNoteSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/capture-note")
		);
	TestNotNull(
		TEXT("A capture-note skill entry must exist."),
		CaptureNoteSkill
	);
	if (CaptureNoteSkill != nullptr)
	{
		TestEqual(
			TEXT("The capture-note skill should remain command-contract pending while note ownership is unresolved."),
			CaptureNoteSkill->Status,
			EHyperTwistSkillStatus::CommandContractPending
		);
		TestEqual(
			TEXT("The capture-note skill should point to the deferred first-party note-capture service id."),
			CaptureNoteSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/deferred/capture-note"))
		);
	}

	const FHyperTwistSkillManifestEntry* CompactViewSkill =
		HyperTwistSkillPhaseS1ATestInternal::FindSkillById(
			RegistryState,
			TEXT("skill/generate-compact-view")
		);
	TestNotNull(
		TEXT("A generate-compact-view skill entry must exist."),
		CompactViewSkill
	);
	if (CompactViewSkill != nullptr)
	{
		TestEqual(
			TEXT("The generate-compact-view skill must be implemented now."),
			CompactViewSkill->Status,
			EHyperTwistSkillStatus::ImplementedNow
		);
		TestEqual(
			TEXT("The generate-compact-view skill should point to the first-party compact-view service id."),
			CompactViewSkill->CommandBindings[0].ServiceBindingId,
			FString(TEXT("service/skills/generate-compact-view"))
		);
	}

	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistSkillPhaseS1ASerializationRoundTripTest,
	"HyperTwist.FirstParty.Skill.PhaseS1A.SerializationRoundTrip",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistSkillPhaseS1ASerializationRoundTripTest::RunTest(
	const FString& Parameters
)
{
	const FHyperTwistSkillRegistryState RegistryState =
		UHyperTwistContractLibrary::MakeSampleSkillRegistryState();
	TestTrue(
		TEXT("The Phase S1-A registry must be structurally valid before serialization."),
		RegistryState.IsStructurallyValid()
	);

	const FString Json =
		UHyperTwistContractLibrary::SerializeSkillRegistryStateToJson(RegistryState);
	TestFalse(TEXT("The serialized S1-A JSON must not be empty."), Json.IsEmpty());
	TestTrue(
		TEXT("The serialized S1-A JSON must expose manifest entries."),
		Json.Contains(TEXT("Entries"))
	);
	TestTrue(
		TEXT("The serialized S1-A JSON must expose the inert-off-state contract."),
		Json.Contains(TEXT("bDisabledSkillsRemainInstalled"))
	);

	FHyperTwistSkillRegistryState RoundTrippedState;
	TestTrue(
		TEXT("The S1-A JSON must deserialize cleanly."),
		UHyperTwistContractLibrary::DeserializeSkillRegistryStateFromJson(
			Json,
			RoundTrippedState
		)
	);
	TestTrue(
		TEXT("The round-tripped S1-A registry must remain structurally valid."),
		RoundTrippedState.IsStructurallyValid()
	);
	TestEqual(
		TEXT("The round-tripped S1-A registry must preserve skill counts."),
		RoundTrippedState.SkillCount,
		RegistryState.SkillCount
	);
	TestEqual(
		TEXT("The round-tripped S1-A registry must preserve disabled-by-default counts."),
		RoundTrippedState.DisabledByDefaultCount,
		RegistryState.DisabledByDefaultCount
	);
	TestEqual(
		TEXT("The round-tripped S1-A registry must preserve family-count cardinality."),
		RoundTrippedState.FamilyCounts.Num(),
		RegistryState.FamilyCounts.Num()
	);

	return true;
}

#endif
