#include "HyperTwistTraining/HyperTwistTrainingTilingLibrary.h"

#include "JsonObjectConverter.h"

namespace HyperTwistTrainingTilingLibraryInternal
{
	const TCHAR* MagicTileLicense = TEXT("MIT");

	FHyperTwistTrainingSourceAttribution MakeAttribution(
		const FString& TrackId,
		const FString& Notes
	)
	{
		FHyperTwistTrainingSourceAttribution Attribution;
		Attribution.SourceMode = EHyperTwistTrainingSourceMode::DirectDonor;
		Attribution.SourceRepo = TEXT("roice3/MagicTile");
		Attribution.SourceLicense = MagicTileLicense;
		Attribution.SourceTrackId = TrackId;
		Attribution.SourceNotes = Notes;
		return Attribution;
	}

	FHyperTwistTrainingSourceAttribution MakeFirstPartyAttribution(
		const FString& TrackId,
		const FString& Notes
	)
	{
		FHyperTwistTrainingSourceAttribution Attribution;
		Attribution.SourceMode = EHyperTwistTrainingSourceMode::FirstParty;
		Attribution.SourceTrackId = TrackId;
		Attribution.SourceNotes = Notes;
		return Attribution;
	}

	FHyperTwistTrainingTilingPackageReference MakePackageReference(
		const FString& PackageId,
		const FString& Label,
		const FString& ExportPath,
		const FString& OwnershipRole,
		const FString& LicensePosture,
		const FString& Summary,
		const TArray<FString>& RetainedSurfaceTags,
		const TArray<FString>& RequiredNoticeActions,
		const TArray<FString>& ExplicitExclusions,
		const FHyperTwistTrainingSourceAttribution& SourceAttribution
	)
	{
		FHyperTwistTrainingTilingPackageReference Reference;
		Reference.PackageId = PackageId;
		Reference.Label = Label;
		Reference.ExportPath = ExportPath;
		Reference.OwnershipRole = OwnershipRole;
		Reference.LicensePosture = LicensePosture;
		Reference.Summary = Summary;
		Reference.RetainedSurfaceTags = RetainedSurfaceTags;
		Reference.RequiredNoticeActions = RequiredNoticeActions;
		Reference.ExplicitExclusions = ExplicitExclusions;
		Reference.SourceAttribution = SourceAttribution;
		return Reference;
	}

	void AppendJoined(FString& OutText, const TArray<FString>& Values)
	{
		for (int32 Index = 0; Index < Values.Num(); ++Index)
		{
			if (Index > 0)
			{
				OutText.Append(TEXT(", "));
			}

			OutText.Append(Values[Index]);
		}
	}

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

	template <typename TStruct>
	bool DeserializeStructFromJson(const FString& Json, TStruct& OutValue)
	{
		return !Json.IsEmpty()
			&& FJsonObjectConverter::JsonObjectStringToUStruct(Json, &OutValue, 0, 0);
	}

	FString NormalizeScrambleToken(const FString& RawToken)
	{
		FString Token = RawToken.TrimStartAndEnd();
		Token.ReplaceInline(TEXT("\u2019"), TEXT("'"));
		Token.ReplaceInline(TEXT("\u2032"), TEXT("'"));
		Token.ReplaceInline(TEXT("\u00B4"), TEXT("'"));

		while (Token.StartsWith(TEXT(",")) || Token.StartsWith(TEXT(";")))
		{
			Token.RightChopInline(1, EAllowShrinking::No);
		}

		while (Token.EndsWith(TEXT(",")) || Token.EndsWith(TEXT(";")))
		{
			Token.LeftChopInline(1, EAllowShrinking::No);
		}

		return Token.TrimStartAndEnd();
	}

	TArray<FString> NormalizeScrambleTokens(const TArray<FString>& Tokens)
	{
		TArray<FString> Result;
		Result.Reserve(Tokens.Num());
		for (const FString& Token : Tokens)
		{
			const FString NormalizedToken = NormalizeScrambleToken(Token);
			if (!NormalizedToken.IsEmpty())
			{
				Result.Add(NormalizedToken);
			}
		}

		return Result;
	}

	TArray<FString> NormalizeUniqueNonEmptyStrings(const TArray<FString>& Values)
	{
		TArray<FString> Result;
		Result.Reserve(Values.Num());
		for (const FString& Value : Values)
		{
			const FString NormalizedValue = Value.TrimStartAndEnd();
			if (NormalizedValue.IsEmpty())
			{
				continue;
			}

			bool bAlreadyPresent = false;
			for (const FString& ExistingValue : Result)
			{
				if (ExistingValue.Equals(NormalizedValue, ESearchCase::CaseSensitive))
				{
					bAlreadyPresent = true;
					break;
				}
			}

			if (!bAlreadyPresent)
			{
				Result.Add(NormalizedValue);
			}
		}

		return Result;
	}
}

FHyperTwistTrainingTilingReferenceBundle UHyperTwistTrainingTilingLibrary::BuildBundledTilingReferenceBundle()
{
	using namespace HyperTwistTrainingTilingLibraryInternal;

	FHyperTwistTrainingTilingReferenceBundle Bundle;
	Bundle.ReferenceUtc = TEXT("2026-05-24T18:30:00Z");
	Bundle.PrimaryTopologyContractId = TEXT("tiling-topology-stack");
	Bundle.PrimaryLoaderBoundaryId = TEXT("tiling-loader-boundary");
	Bundle.PrimaryAttributionBoundaryId = TEXT("tiling-mit-attribution-boundary");
	Bundle.PrimaryMacroRemappingContractId = TEXT("tiling-macro-remapping-stack");
	Bundle.PrimaryMacroPersistenceBoundaryId = TEXT("tiling-macro-persistence-boundary");
	Bundle.PrimaryHostDecisionId = TEXT("tiling-embedded-browser-host");
	Bundle.PrimaryStateBridgeContractId = TEXT("tiling-browser-state-bridge");
	Bundle.PrimaryNativeBehaviorProofContractId = TEXT("tiling-native-behavior-proof");

	const TArray<FString> CommonNoticeActions =
	{
		TEXT("preserve the upstream MIT license text in third-party notices or equivalent release/legal materials"),
		TEXT("preserve the upstream copyright notice for Roice Nelson")
	};

	Bundle.PackageReferences =
	{
		MakePackageReference(
			TEXT("package/magictile-topology-analyzer"),
			TEXT("MagicTile/Puzzle/TopologyAnalyzer"),
			TEXT("./MagicTile/Puzzle/TopologyAnalyzer.cs"),
			TEXT("tiling-topology-anchor"),
			TEXT("direct permissive donor"),
			TEXT("Logical face-edge-vertex identification, Euler-characteristic calculation, and merged topology analysis for tiling-driven puzzle spaces."),
			{
				TEXT("logical element indexing"),
				TEXT("face edge vertex counts"),
				TEXT("Euler characteristic"),
				TEXT("master/slave identification merging")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("WinForms/OpenTK shell ownership"),
				TEXT("macro transport ownership")
			},
			MakeAttribution(
				TEXT("tiling/topology-analyzer"),
				TEXT("Preserves MagicTile logical-element indexing, identification merging, and Euler-characteristic analysis beneath first-party HyperTwist tiling topology ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-loader"),
			TEXT("MagicTile/Puzzle/Loader"),
			TEXT("./MagicTile/Puzzle/Loader.cs"),
			TEXT("tiling-loader-support"),
			TEXT("direct permissive donor"),
			TEXT("Generalized puzzle-construction loading, master/slave cell preparation, and topology-derived construction support for tiling-driven puzzle families."),
			{
				TEXT("puzzle loading"),
				TEXT("master slave cell setup"),
				TEXT("state-calculation cell preparation"),
				TEXT("topology-derived construction")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("whole legacy persistence shell"),
				TEXT("direct WinForms host transplant")
			},
			MakeAttribution(
				TEXT("tiling/loader"),
				TEXT("Preserves MagicTile generalized puzzle-loading and topology-derived construction seams without displacing the Hyperspeedcube runtime anchor.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-puzzle-core"),
			TEXT("MagicTile/Puzzle/Puzzle"),
			TEXT("./MagicTile/Puzzle/Puzzle.cs"),
			TEXT("tiling-construction-support"),
			TEXT("direct permissive donor"),
			TEXT("Tiling-driven puzzle construction support including master/slave cells, sticker slicing, and twist-data registration beneath first-party runtime ownership."),
			{
				TEXT("master cells"),
				TEXT("slave cells"),
				TEXT("sticker slicing"),
				TEXT("twist-data registration")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("whole renderer ownership"),
				TEXT("legacy host-shell ownership")
			},
			MakeAttribution(
				TEXT("tiling/puzzle-core"),
				TEXT("Preserves MagicTile master/slave construction, sticker slicing, and twist-data setup seams beneath first-party HyperTwist topology ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-tiling-geometry"),
			TEXT("R3.Core/Geometry/Tiling"),
			TEXT("./R3/R3.Core/Geometry/Tiling.cs"),
			TEXT("tiling-geometry-anchor"),
			TEXT("direct permissive donor"),
			TEXT("Regular-tiling, dual-tiling, and centered-projection geometry substrate for spherical, Euclidean, and hyperbolic tiling families."),
			{
				TEXT("regular tiling generation"),
				TEXT("dual tiling support"),
				TEXT("face edge vertex centered projections"),
				TEXT("non-Euclidean tiling families")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("macro remapping ownership"),
				TEXT("legacy renderer shell")
			},
			MakeAttribution(
				TEXT("tiling/geometry-tiling"),
				TEXT("Preserves MagicTile regular-tiling, dual-tiling, and projection-family seams for first-party HyperTwist tiling geometry ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-polytope-geometry"),
			TEXT("R3.Core/Geometry/Polytope"),
			TEXT("./R3/R3.Core/Geometry/Polytope.cs"),
			TEXT("tiling-polytope-support"),
			TEXT("direct permissive donor"),
			TEXT("Polytope construction support for topology-derived puzzle families that widen beyond ordinary cube spaces."),
			{
				TEXT("polytope construction"),
				TEXT("cell and facet relationships"),
				TEXT("higher-order geometry support")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("whole viewer shell ownership"),
				TEXT("direct renderer transplant")
			},
			MakeAttribution(
				TEXT("tiling/geometry-polytope"),
				TEXT("Preserves MagicTile polytope-construction support for first-party HyperTwist topology and geometry-family ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-h3-honeycomb"),
			TEXT("R3.Core/Honeycombs/H3"),
			TEXT("./R3/R3.Core/Honeycombs/H3.cs"),
			TEXT("tiling-hyperbolic-family-support"),
			TEXT("direct permissive donor"),
			TEXT("Hyperbolic honeycomb-family support for later non-Euclidean tiling puzzle families beneath first-party HyperTwist ownership."),
			{
				TEXT("hyperbolic honeycomb families"),
				TEXT("non-Euclidean family tags"),
				TEXT("later topology widening support")
			},
			CommonNoticeActions,
			{
				TEXT("runtime-base ownership"),
				TEXT("whole app-shell ownership"),
				TEXT("macro remapping ownership")
			},
			MakeAttribution(
				TEXT("tiling/h3-honeycomb"),
				TEXT("Preserves MagicTile hyperbolic honeycomb-family seams for later first-party HyperTwist tiling and non-Euclidean topology widening.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-macro"),
			TEXT("MagicTile/Puzzle/Twisting/Macro"),
			TEXT("./MagicTile/Puzzle/Twisting/Macro.cs"),
			TEXT("tiling-macro-remapping-anchor"),
			TEXT("direct permissive donor"),
			TEXT("Reference Mobius orientation, transformed twist lookup, reverse or reflection handling, and macro XML persistence for topology-aware advanced puzzle macros."),
			{
				TEXT("reference mobius transform"),
				TEXT("clicked-cell canonical orientation"),
				TEXT("view reflection parity"),
				TEXT("transformed twist lookup"),
				TEXT("macro XML persistence")
			},
			CommonNoticeActions,
			{
				TEXT("whole macro editor shell"),
				TEXT("broad non-Euclidean interaction shell ownership"),
				TEXT("WinForms/OpenTK host-shell ownership")
			},
			MakeAttribution(
				TEXT("tiling/macro"),
				TEXT("Preserves MagicTile reference Mobius setup, transformed twist lookup, reversal handling, and XML macro persistence beneath first-party HyperTwist macro-remapping ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-twist-handler"),
			TEXT("MagicTile/Puzzle/Twisting/TwistHandler"),
			TEXT("./MagicTile/Puzzle/Twisting/TwistHandler.cs"),
			TEXT("tiling-macro-playback-support"),
			TEXT("direct permissive donor"),
			TEXT("Macro playback, reverse execution, setup-move unwind, commutator playback, and grouped undo or redo handling for advanced tiling puzzles."),
			{
				TEXT("forward macro execution"),
				TEXT("reverse macro execution"),
				TEXT("setup-move unwind"),
				TEXT("commutator playback"),
				TEXT("grouped macro undo redo")
			},
			CommonNoticeActions,
			{
				TEXT("whole click-input shell"),
				TEXT("animation timing shell ownership"),
				TEXT("legacy status or menu ownership")
			},
			MakeAttribution(
				TEXT("tiling/twist-handler"),
				TEXT("Preserves MagicTile macro playback, reversal, setup-move, and grouped undo behavior without promoting the old desktop interaction shell into first-party HyperTwist ownership.")
			)
		),
		MakePackageReference(
			TEXT("package/magictile-setup-moves"),
			TEXT("MagicTile/Puzzle/Twisting/SetupMoves"),
			TEXT("./MagicTile/Puzzle/Twisting/SetupMoves.cs"),
			TEXT("tiling-setup-move-support"),
			TEXT("direct permissive donor"),
			TEXT("Setup and commutator move recording that complements transform-aware macro remapping without owning the broader puzzle shell."),
			{
				TEXT("setup move recording"),
				TEXT("commutator derivation"),
				TEXT("macro reversal reuse")
			},
			CommonNoticeActions,
			{
				TEXT("whole macro-pane shell"),
				TEXT("legacy accelerator ownership"),
				TEXT("runtime-base ownership")
			},
			MakeAttribution(
				TEXT("tiling/setup-moves"),
				TEXT("Preserves MagicTile setup-move and commutator recording seams beneath first-party HyperTwist macro-remapping ownership.")
			)
		)
	};

	FHyperTwistTrainingTilingTopologyContract TopologyContract;
	TopologyContract.ContractId = TEXT("tiling-topology-stack");
	TopologyContract.Title = TEXT("Tiling topology and geometry-family contract");
	TopologyContract.Summary = TEXT("First-party HyperTwist contract for tiling-driven puzzle-family descriptors, logical topology analysis, and non-Euclidean geometry-family tags beneath the Hyperspeedcube runtime anchor.");
	TopologyContract.RequiredPackageIds =
	{
		TEXT("package/magictile-topology-analyzer"),
		TEXT("package/magictile-tiling-geometry"),
		TEXT("package/magictile-polytope-geometry"),
		TEXT("package/magictile-h3-honeycomb")
	};
	TopologyContract.PuzzleFamilies =
	{
		TEXT("spherical tilings"),
		TEXT("Euclidean tilings"),
		TEXT("hyperbolic tilings"),
		TEXT("tiling-driven non-Euclidean puzzle families")
	};
	TopologyContract.GeometrySurfaceTags =
	{
		TEXT("regular tiling generation"),
		TEXT("dual-tiling support"),
		TEXT("polytope geometry support"),
		TEXT("hyperbolic honeycomb family tags"),
		TEXT("face edge vertex centered projections")
	};
	TopologyContract.TopologySurfaceTags =
	{
		TEXT("logical face edge vertex counts"),
		TEXT("Euler characteristic"),
		TEXT("master slave identification merging"),
		TEXT("topology-derived indexing"),
		TEXT("state-calculation cell topology")
	};
	TopologyContract.ExplicitExclusions =
	{
		TEXT("Hyperspeedcube runtime-base replacement"),
		TEXT("WinForms/OpenTK shell ownership"),
		TEXT("transform-aware macro remapping ownership")
	};
	TopologyContract.SourceAttribution = MakeAttribution(
		TEXT("tiling/topology-contract"),
		TEXT("Preserves MagicTile tiling-family descriptors, topology analysis, and non-Euclidean geometry-family seams while keeping HyperTwist's runtime anchor and product shell first-party.")
	);
	Bundle.TopologyContracts = {TopologyContract};

	FHyperTwistTrainingTilingLoaderBoundary LoaderBoundary;
	LoaderBoundary.BoundaryId = TEXT("tiling-loader-boundary");
	LoaderBoundary.Title = TEXT("Tiling loader and runtime-anchor boundary");
	LoaderBoundary.Summary = TEXT("First-party HyperTwist boundary for generalized tiling-derived puzzle loading beneath the Hyperspeedcube runtime anchor and outside the legacy host shell.");
	LoaderBoundary.RequiredPackageIds =
	{
		TEXT("package/magictile-loader"),
		TEXT("package/magictile-puzzle-core"),
		TEXT("package/magictile-topology-analyzer")
	};
	LoaderBoundary.LoaderSurfaceTags =
	{
		TEXT("generalized puzzle loading"),
		TEXT("master slave cell preparation"),
		TEXT("sticker slicing"),
		TEXT("twist-data registration"),
		TEXT("topology-derived construction")
	};
	LoaderBoundary.RuntimeBoundaryTags =
	{
		TEXT("Hyperspeedcube runtime anchor remains primary for runtime catalog notation replay and verification"),
		TEXT("MagicTile widens only the tiling-derived topology and geometry-family lane"),
		TEXT("loader enrichments stay beneath first-party runtime ownership")
	};
	LoaderBoundary.ExplicitExclusions =
	{
		TEXT("Hyperspeedcube runtime-base replacement"),
		TEXT("whole WinForms/OpenTK shell ownership"),
		TEXT("legacy persistence-dialog ownership"),
		TEXT("transform-aware macro remapping ownership")
	};
	LoaderBoundary.SourceAttribution = MakeAttribution(
		TEXT("tiling/loader-boundary"),
		TEXT("Preserves MagicTile generalized puzzle-loading and topology-derived construction seams while keeping runtime, notation, replay, and verification ownership with the Hyperspeedcube anchor and first-party HyperTwist code.")
	);
	Bundle.LoaderBoundaries = {LoaderBoundary};

	FHyperTwistTrainingTilingMacroRemappingContract MacroContract;
	MacroContract.ContractId = TEXT("tiling-macro-remapping-stack");
	MacroContract.Title = TEXT("Tiling transform-aware macro remapping contract");
	MacroContract.Summary = TEXT("First-party HyperTwist contract for reference-cell macro orientation, transformed twist lookup, reflection-aware reversal, and bounded macro playback semantics for non-Euclidean tiling families.");
	MacroContract.RequiredPackageIds =
	{
		TEXT("package/magictile-macro"),
		TEXT("package/magictile-twist-handler"),
		TEXT("package/magictile-setup-moves"),
		TEXT("package/magictile-puzzle-core"),
		TEXT("package/magictile-topology-analyzer")
	};
	MacroContract.ReferenceSurfaceTags =
	{
		TEXT("reference mobius transform"),
		TEXT("clicked-cell canonical orientation"),
		TEXT("view reflection parity"),
		TEXT("puzzle-specific macro ownership")
	};
	MacroContract.TransformSurfaceTags =
	{
		TEXT("transformed twist lookup by closest center"),
		TEXT("state-calc twist selection nearest origin after transform"),
		TEXT("reverse XOR view-reflection handling"),
		TEXT("identified twist-data remapping")
	};
	MacroContract.PlaybackSurfaceTags =
	{
		TEXT("forward macro execution"),
		TEXT("reverse macro execution"),
		TEXT("setup-move unwind"),
		TEXT("commutator second-half playback"),
		TEXT("grouped macro undo redo")
	};
	MacroContract.ExplicitExclusions =
	{
		TEXT("whole macro editor shell"),
		TEXT("broad non-Euclidean interaction shell ownership"),
		TEXT("WinForms/OpenTK host-shell ownership"),
		TEXT("Hyperspeedcube runtime-base replacement")
	};
	MacroContract.SourceAttribution = MakeAttribution(
		TEXT("tiling/macro-remapping-contract"),
		TEXT("Preserves MagicTile macro reference orientation, transformed twist remapping, and reversal semantics while keeping HyperTwist first-party for the broader macro UX, runtime, and product shell.")
	);
	Bundle.MacroRemappingContracts = {MacroContract};

	FHyperTwistTrainingTilingMacroPersistenceBoundary MacroPersistenceBoundary;
	MacroPersistenceBoundary.BoundaryId = TEXT("tiling-macro-persistence-boundary");
	MacroPersistenceBoundary.Title = TEXT("Tiling macro persistence and compatibility boundary");
	MacroPersistenceBoundary.Summary = TEXT("First-party HyperTwist boundary for MagicTile-style macro XML persistence, puzzle compatibility checks, and macro block serialization beneath first-party macro ownership.");
	MacroPersistenceBoundary.RequiredPackageIds =
	{
		TEXT("package/magictile-macro"),
		TEXT("package/magictile-loader"),
		TEXT("package/magictile-puzzle-core")
	};
	MacroPersistenceBoundary.PersistenceSurfaceTags =
	{
		TEXT("MagicTileMacros XML root"),
		TEXT("macro display name persistence"),
		TEXT("Mobius persistence"),
		TEXT("view-reflected persistence"),
		TEXT("tab-delimited macro block serialization"),
		TEXT("macro start and end markers")
	};
	MacroPersistenceBoundary.CompatibilitySurfaceTags =
	{
		TEXT("active puzzle id compatibility"),
		TEXT("puzzle display-name compatibility messaging"),
		TEXT("edge or vertex twisting version compatibility"),
		TEXT("saved version 2.0 and 2.1 handling"),
		TEXT("load macros against all twist data")
	};
	MacroPersistenceBoundary.ProvenanceCaveats =
	{
		TEXT("macro files are puzzle-specific and are not intended to travel across differently configured puzzles"),
		TEXT("macro persistence uses XML blocks rather than the first-party canonical replay schema"),
		TEXT("save and load dialog filter strings remain legacy host-shell details rather than first-party product contract")
	};
	MacroPersistenceBoundary.ExplicitExclusions =
	{
		TEXT("generic replay or canonical notation ownership"),
		TEXT("host file-dialog ownership"),
		TEXT("whole macro-management pane shell"),
		TEXT("legacy message-box choreography")
	};
	MacroPersistenceBoundary.SourceAttribution = MakeAttribution(
		TEXT("tiling/macro-persistence-boundary"),
		TEXT("Preserves MagicTile macro XML persistence and compatibility checks while keeping HyperTwist first-party for canonical replay, notation, and broader persistence ownership.")
	);
	Bundle.MacroPersistenceBoundaries = {MacroPersistenceBoundary};

	FHyperTwistTrainingTilingAttributionBoundary AttributionBoundary;
	AttributionBoundary.BoundaryId = TEXT("tiling-mit-attribution-boundary");
	AttributionBoundary.Title = TEXT("Tiling donor attribution boundary");
	AttributionBoundary.Summary = TEXT("First-party HyperTwist compliance boundary for using MagicTile as a direct permissive donor for tiling topology, loader, and bounded macro-remapping contracts.");
	AttributionBoundary.AllowedUseTags =
	{
		TEXT("first-party reauthored subsystem implementation"),
		TEXT("direct permissive donor reference"),
		TEXT("bounded tiling topology, loader, and macro-remapping contract use")
	};
	AttributionBoundary.RequiredNoticeActions = CommonNoticeActions;
	AttributionBoundary.ExplicitExclusions =
	{
		TEXT("silent attribution removal"),
		TEXT("whole legacy app-shell adoption"),
		TEXT("pretending broad interaction-shell or runtime-base ownership landed in this packet")
	};
	AttributionBoundary.SourceAttribution = MakeAttribution(
		TEXT("tiling/attribution-boundary"),
		TEXT("Records MIT attribution and packet bounds for MagicTile while keeping non-owned shell surfaces excluded from the bounded tiling topology and macro-remapping slices.")
	);
	Bundle.AttributionBoundaries = {AttributionBoundary};

	FHyperTwistTrainingTilingHostDecision HostDecision;
	HostDecision.DecisionId = TEXT("tiling-embedded-browser-host");
	HostDecision.HostKind = EHyperTwistTrainingTilingHostKind::EmbeddedBrowserCef;
	HostDecision.Title = TEXT("Embedded browser host decision for non-Euclidean tiling");
	HostDecision.Summary = TEXT("First-party HyperTwist host decision that keeps MagicTile on the retained topology and macro lane while routing the live non-Euclidean interaction shell through the already-landed embedded browser runtime, react-three-fiber, and three.js substrate.");
	HostDecision.RequiredPackageIds =
	{
		TEXT("package/magictile-loader"),
		TEXT("package/magictile-puzzle-core"),
		TEXT("package/magictile-tiling-geometry"),
		TEXT("package/magictile-macro"),
		TEXT("package/three-js"),
		TEXT("package/react-three-fiber"),
		TEXT("package/pmndrs-three-stdlib"),
		TEXT("package/pmndrs-zustand"),
		TEXT("package/pmndrs-use-gesture")
	};
	HostDecision.BrowserSurfaceTags =
	{
		TEXT("embedded CEF browser shell"),
		TEXT("authoritative local browser shell"),
		TEXT("three.js scene renderer camera lifecycle"),
		TEXT("react-three-fiber canvas roots"),
		TEXT("browser bridge object transport"),
		TEXT("postMessage fallback transport")
	};
	HostDecision.DeferredPortReasons =
	{
		TEXT("native spherical and hyperbolic renderer ownership remains behavior-unproven relative to the landed browser runtime substrate"),
		TEXT("the retained MagicTile lane already packetized topology loader and macro remapping without a runtime-base transplant"),
		TEXT("legacy WinForms and OpenTK shell code stays excluded from first-party ownership")
	};
	HostDecision.ExplicitExclusions =
	{
		TEXT("whole WinForms/OpenTK host shell ownership"),
		TEXT("native spherical or hyperbolic renderer port before first-party behavior proof"),
		TEXT("generic non-Euclidean runtime-base replacement")
	};
	HostDecision.SourceAttribution = MakeFirstPartyAttribution(
		TEXT("tiling/browser-host-decision"),
		TEXT("Chooses the already-landed embedded browser runtime above the retained MagicTile topology lane, avoiding a broad legacy host transplant while keeping future native renderer work explicitly deferred.")
	);
	Bundle.HostDecisions = {HostDecision};

	FHyperTwistTrainingTilingStateBridgeContract StateBridgeContract;
	StateBridgeContract.ContractId = TEXT("tiling-browser-state-bridge");
	StateBridgeContract.HostDecisionId = HostDecision.DecisionId;
	StateBridgeContract.Title = TEXT("Tiling browser state bridge contract");
	StateBridgeContract.Summary = TEXT("First-party HyperTwist bridge contract for serializing non-Euclidean tiling puzzle state, shared scramble notation, and unified training timer state across Unreal and the embedded browser host chosen for MagicTile.");
	StateBridgeContract.RequiredPackageIds =
	{
		TEXT("package/magictile-loader"),
		TEXT("package/magictile-macro"),
		TEXT("package/three-js"),
		TEXT("package/react-three-fiber"),
		TEXT("package/pmndrs-zustand")
	};
	StateBridgeContract.StateSurfaceTags =
	{
		TEXT("json tiling state envelope"),
		TEXT("puzzle-state pass-through"),
		TEXT("projection payload pass-through"),
		TEXT("browser shell synchronization")
	};
	StateBridgeContract.TransportSurfaceTags =
	{
		TEXT("browser bridge object transport"),
		TEXT("postMessage fallback transport"),
		TEXT("transport-neutral state envelope")
	};
	StateBridgeContract.ScrambleSurfaceTags =
	{
		TEXT("token-preserving scramble parser"),
		TEXT("whitespace-delimited scramble normalization"),
		TEXT("puzzle-family-neutral scramble notation")
	};
	StateBridgeContract.TimerSurfaceTags =
	{
		TEXT("shared training live timer state"),
		TEXT("inspection solving paused phases"),
		TEXT("cross-family displayed elapsed synchronization")
	};
	StateBridgeContract.ExplicitExclusions =
	{
		TEXT("native non-Euclidean simulation ownership"),
		TEXT("whole browser shell ownership"),
		TEXT("generic replay ownership")
	};
	StateBridgeContract.SourceAttribution = MakeFirstPartyAttribution(
		TEXT("tiling/browser-state-bridge"),
		TEXT("Maps the retained MagicTile tiling lane onto the landed browser bridge and shared training timer substrate without claiming a native non-Euclidean runtime transplant.")
	);
	Bundle.StateBridgeContracts = {StateBridgeContract};

	FHyperTwistTrainingTilingNativeBehaviorProofContract NativeBehaviorProofContract;
	NativeBehaviorProofContract.ContractId = TEXT("tiling-native-behavior-proof");
	NativeBehaviorProofContract.HostDecisionId = HostDecision.DecisionId;
	NativeBehaviorProofContract.StateBridgeContractId = StateBridgeContract.ContractId;
	NativeBehaviorProofContract.Title = TEXT("Tiling native behavior proof contract");
	NativeBehaviorProofContract.Summary = TEXT("First-party HyperTwist proof contract for modeling selected-cell focus, candidate-twist disambiguation, preview, commit or cancel, and timer continuity natively before any non-Euclidean renderer port is justified.");
	NativeBehaviorProofContract.RequiredPackageIds =
	{
		TEXT("package/magictile-puzzle-core"),
		TEXT("package/magictile-tiling-geometry"),
		TEXT("package/magictile-macro"),
		TEXT("package/magictile-twist-handler")
	};
	NativeBehaviorProofContract.NativeInputSurfaceTags =
	{
		TEXT("selected cell focus"),
		TEXT("candidate twist disambiguation"),
		TEXT("drag-to-twist intent"),
		TEXT("commit or cancel interaction")
	};
	NativeBehaviorProofContract.NativeStateSurfaceTags =
	{
		TEXT("native preview transform token"),
		TEXT("native twist commit token"),
		TEXT("timer continuity during native interaction"),
		TEXT("shared scramble token reuse")
	};
	NativeBehaviorProofContract.ReusedBridgeSurfaceTags =
	{
		TEXT("transport-neutral state envelope"),
		TEXT("puzzle-state pass-through"),
		TEXT("projection payload pass-through"),
		TEXT("shared training live timer state")
	};
	NativeBehaviorProofContract.ExplicitExclusions =
	{
		TEXT("whole renderer port ownership"),
		TEXT("whole WinForms/OpenTK host shell ownership"),
		TEXT("generic non-Euclidean runtime-base replacement")
	};
	NativeBehaviorProofContract.SourceAttribution = MakeFirstPartyAttribution(
		TEXT("tiling/native-behavior-proof"),
		TEXT("Defines the smallest first-party native MagicTile behavior proof above the landed browser bridge so later renderer-port work can stay decision-gated rather than assumption-driven.")
	);
	Bundle.NativeBehaviorProofContracts = {NativeBehaviorProofContract};

	return Bundle;
}

bool UHyperTwistTrainingTilingLibrary::TryGetTopologyContractById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& ContractId,
	FHyperTwistTrainingTilingTopologyContract& OutContract
)
{
	for (const FHyperTwistTrainingTilingTopologyContract& Contract : Bundle.TopologyContracts)
	{
		if (Contract.ContractId == ContractId)
		{
			OutContract = Contract;
			return true;
		}
	}

	OutContract = FHyperTwistTrainingTilingTopologyContract();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetLoaderBoundaryById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& BoundaryId,
	FHyperTwistTrainingTilingLoaderBoundary& OutBoundary
)
{
	for (const FHyperTwistTrainingTilingLoaderBoundary& Boundary : Bundle.LoaderBoundaries)
	{
		if (Boundary.BoundaryId == BoundaryId)
		{
			OutBoundary = Boundary;
			return true;
		}
	}

	OutBoundary = FHyperTwistTrainingTilingLoaderBoundary();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetAttributionBoundaryById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& BoundaryId,
	FHyperTwistTrainingTilingAttributionBoundary& OutBoundary
)
{
	for (const FHyperTwistTrainingTilingAttributionBoundary& Boundary : Bundle.AttributionBoundaries)
	{
		if (Boundary.BoundaryId == BoundaryId)
		{
			OutBoundary = Boundary;
			return true;
		}
	}

	OutBoundary = FHyperTwistTrainingTilingAttributionBoundary();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetMacroRemappingContractById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& ContractId,
	FHyperTwistTrainingTilingMacroRemappingContract& OutContract
)
{
	for (const FHyperTwistTrainingTilingMacroRemappingContract& Contract : Bundle.MacroRemappingContracts)
	{
		if (Contract.ContractId == ContractId)
		{
			OutContract = Contract;
			return true;
		}
	}

	OutContract = FHyperTwistTrainingTilingMacroRemappingContract();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetMacroPersistenceBoundaryById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& BoundaryId,
	FHyperTwistTrainingTilingMacroPersistenceBoundary& OutBoundary
)
{
	for (const FHyperTwistTrainingTilingMacroPersistenceBoundary& Boundary : Bundle.MacroPersistenceBoundaries)
	{
		if (Boundary.BoundaryId == BoundaryId)
		{
			OutBoundary = Boundary;
			return true;
		}
	}

	OutBoundary = FHyperTwistTrainingTilingMacroPersistenceBoundary();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetHostDecisionById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& DecisionId,
	FHyperTwistTrainingTilingHostDecision& OutDecision
)
{
	for (const FHyperTwistTrainingTilingHostDecision& Decision : Bundle.HostDecisions)
	{
		if (Decision.DecisionId == DecisionId)
		{
			OutDecision = Decision;
			return true;
		}
	}

	OutDecision = FHyperTwistTrainingTilingHostDecision();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetStateBridgeContractById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& ContractId,
	FHyperTwistTrainingTilingStateBridgeContract& OutContract
)
{
	for (const FHyperTwistTrainingTilingStateBridgeContract& Contract : Bundle.StateBridgeContracts)
	{
		if (Contract.ContractId == ContractId)
		{
			OutContract = Contract;
			return true;
		}
	}

	OutContract = FHyperTwistTrainingTilingStateBridgeContract();
	return false;
}

bool UHyperTwistTrainingTilingLibrary::TryGetNativeBehaviorProofContractById(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle,
	const FString& ContractId,
	FHyperTwistTrainingTilingNativeBehaviorProofContract& OutContract
)
{
	for (const FHyperTwistTrainingTilingNativeBehaviorProofContract& Contract :
		Bundle.NativeBehaviorProofContracts)
	{
		if (Contract.ContractId == ContractId)
		{
			OutContract = Contract;
			return true;
		}
	}

	OutContract = FHyperTwistTrainingTilingNativeBehaviorProofContract();
	return false;
}

TArray<FString> UHyperTwistTrainingTilingLibrary::ParseSharedScrambleNotation(
	const FString& SharedScrambleNotation
)
{
	TArray<FString> RawTokens;
	SharedScrambleNotation.ParseIntoArrayWS(RawTokens);
	return HyperTwistTrainingTilingLibraryInternal::NormalizeScrambleTokens(RawTokens);
}

FString UHyperTwistTrainingTilingLibrary::BuildSharedScrambleNotation(
	const TArray<FString>& SharedScrambleTokens
)
{
	return FString::Join(
		HyperTwistTrainingTilingLibraryInternal::NormalizeScrambleTokens(
			SharedScrambleTokens
		),
		TEXT(" ")
	);
}

FHyperTwistTrainingTilingStateEnvelope UHyperTwistTrainingTilingLibrary::NormalizeStateEnvelope(
	const FHyperTwistTrainingTilingStateEnvelope& StateEnvelope
)
{
	FHyperTwistTrainingTilingStateEnvelope NormalizedEnvelope = StateEnvelope;

	if (NormalizedEnvelope.SchemaVersion.IsEmpty())
	{
		NormalizedEnvelope.SchemaVersion = TEXT("ht-tiling-state-bridge/v1");
	}

	if (NormalizedEnvelope.HostDecisionId.IsEmpty())
	{
		NormalizedEnvelope.HostDecisionId = TEXT("tiling-embedded-browser-host");
	}

	if (NormalizedEnvelope.BridgeContractId.IsEmpty())
	{
		NormalizedEnvelope.BridgeContractId = TEXT("tiling-browser-state-bridge");
	}

	if (NormalizedEnvelope.PuzzleFamilyId.IsEmpty())
	{
		NormalizedEnvelope.PuzzleFamilyId = TEXT("Tiling");
	}

	if (NormalizedEnvelope.PuzzleDefinitionId.IsEmpty()
		&& NormalizedEnvelope.PuzzleState.Definition.IsStructurallyValid())
	{
		NormalizedEnvelope.PuzzleDefinitionId =
			NormalizedEnvelope.PuzzleState.Definition.PuzzleId;
	}

	if (NormalizedEnvelope.TransportKind.IsEmpty())
	{
		NormalizedEnvelope.TransportKind = TEXT("browser-bridge-object");
	}

	if (NormalizedEnvelope.SharedScrambleTokens.Num() == 0
		&& !NormalizedEnvelope.SharedScrambleNotation.IsEmpty())
	{
		NormalizedEnvelope.SharedScrambleTokens =
			ParseSharedScrambleNotation(NormalizedEnvelope.SharedScrambleNotation);
	}
	else
	{
		NormalizedEnvelope.SharedScrambleTokens =
			HyperTwistTrainingTilingLibraryInternal::NormalizeScrambleTokens(
				NormalizedEnvelope.SharedScrambleTokens
			);
	}

	NormalizedEnvelope.SharedScrambleNotation =
		BuildSharedScrambleNotation(NormalizedEnvelope.SharedScrambleTokens);

	if (NormalizedEnvelope.SourceRuntime.IsEmpty())
	{
		NormalizedEnvelope.SourceRuntime = TEXT("embedded-browser-cef");
	}

	if (NormalizedEnvelope.CapturedAtUtc.IsEmpty())
	{
		if (!NormalizedEnvelope.PuzzleState.CapturedAtUtc.IsEmpty())
		{
			NormalizedEnvelope.CapturedAtUtc = NormalizedEnvelope.PuzzleState.CapturedAtUtc;
		}
		else if (!NormalizedEnvelope.TimerState.LastUpdatedAtUtc.IsEmpty())
		{
			NormalizedEnvelope.CapturedAtUtc = NormalizedEnvelope.TimerState.LastUpdatedAtUtc;
		}
		else if (!NormalizedEnvelope.TimerState.StartedAtUtc.IsEmpty())
		{
			NormalizedEnvelope.CapturedAtUtc = NormalizedEnvelope.TimerState.StartedAtUtc;
		}
	}

	if (NormalizedEnvelope.TimerState.TrainingSessionId.IsEmpty())
	{
		NormalizedEnvelope.TimerState.TrainingSessionId = NormalizedEnvelope.SessionId;
	}

	if (NormalizedEnvelope.TimerState.DeckId.IsEmpty())
	{
		NormalizedEnvelope.TimerState.DeckId = NormalizedEnvelope.PuzzleFamilyId;
	}

	if (NormalizedEnvelope.TimerState.CaseId.IsEmpty())
	{
		NormalizedEnvelope.TimerState.CaseId = NormalizedEnvelope.PuzzleDefinitionId;
	}

	if (NormalizedEnvelope.TimerState.LastUpdatedAtUtc.IsEmpty())
	{
		NormalizedEnvelope.TimerState.LastUpdatedAtUtc = NormalizedEnvelope.CapturedAtUtc;
	}

	NormalizedEnvelope.TimerState.InspectionLimitMs =
		FMath::Max(NormalizedEnvelope.TimerState.InspectionLimitMs, 0);
	NormalizedEnvelope.TimerState.InspectionElapsedMs =
		FMath::Max(NormalizedEnvelope.TimerState.InspectionElapsedMs, 0);
	NormalizedEnvelope.TimerState.SolveElapsedMs =
		FMath::Max(NormalizedEnvelope.TimerState.SolveElapsedMs, 0);
	NormalizedEnvelope.TimerState.DisplayedElapsedMs = FMath::Max(
		NormalizedEnvelope.TimerState.DisplayedElapsedMs,
		FMath::Max(
			NormalizedEnvelope.TimerState.InspectionElapsedMs,
			NormalizedEnvelope.TimerState.SolveElapsedMs
		)
	);
	NormalizedEnvelope.TimerState.TotalPausedMs =
		FMath::Max(NormalizedEnvelope.TimerState.TotalPausedMs, 0);

	return NormalizedEnvelope;
}

bool UHyperTwistTrainingTilingLibrary::TrySerializeStateEnvelopeToJson(
	const FHyperTwistTrainingTilingStateEnvelope& StateEnvelope,
	FString& OutJson
)
{
	const FHyperTwistTrainingTilingStateEnvelope NormalizedEnvelope =
		NormalizeStateEnvelope(StateEnvelope);
	if (!NormalizedEnvelope.IsStructurallyValid())
	{
		OutJson.Reset();
		return false;
	}

	return HyperTwistTrainingTilingLibraryInternal::SerializeStructToJson(
		NormalizedEnvelope,
		OutJson
	);
}

bool UHyperTwistTrainingTilingLibrary::TryDeserializeStateEnvelopeFromJson(
	const FString& Json,
	FHyperTwistTrainingTilingStateEnvelope& OutStateEnvelope
)
{
	OutStateEnvelope = FHyperTwistTrainingTilingStateEnvelope();

	FHyperTwistTrainingTilingStateEnvelope ParsedEnvelope;
	if (!HyperTwistTrainingTilingLibraryInternal::DeserializeStructFromJson(
			Json,
			ParsedEnvelope))
	{
		return false;
	}

	OutStateEnvelope = NormalizeStateEnvelope(ParsedEnvelope);
	return OutStateEnvelope.IsStructurallyValid();
}

FHyperTwistTrainingTilingNativeBehaviorProofProbe
UHyperTwistTrainingTilingLibrary::CreateNativeBehaviorProofProbe(
	const FHyperTwistTrainingTilingStateEnvelope& StateEnvelope,
	const FString& SelectedCellId,
	const TArray<FString>& CandidateTwistIds,
	const FString& CommitTwistId,
	const FString& PreviewTransformToken
)
{
	const FHyperTwistTrainingTilingStateEnvelope NormalizedStateEnvelope =
		NormalizeStateEnvelope(StateEnvelope);

	FHyperTwistTrainingTilingNativeBehaviorProofProbe Probe;
	Probe.ProofContractId = TEXT("tiling-native-behavior-proof");
	Probe.HostDecisionId = NormalizedStateEnvelope.HostDecisionId;
	Probe.StateBridgeContractId = NormalizedStateEnvelope.BridgeContractId;
	Probe.SessionId = NormalizedStateEnvelope.SessionId;
	Probe.PuzzleDefinitionId = NormalizedStateEnvelope.PuzzleDefinitionId;
	Probe.SelectedCellId = SelectedCellId;
	Probe.CandidateTwistIds = CandidateTwistIds;
	Probe.PreviewTransformToken = PreviewTransformToken;
	Probe.CommitTwistId = CommitTwistId;
	Probe.SharedScrambleTokens = NormalizedStateEnvelope.SharedScrambleTokens;
	Probe.SharedScrambleNotation = NormalizedStateEnvelope.SharedScrambleNotation;
	Probe.PuzzleState = NormalizedStateEnvelope.PuzzleState;
	Probe.TimerState = NormalizedStateEnvelope.TimerState;
	Probe.CapturedAtUtc = NormalizedStateEnvelope.CapturedAtUtc;
	return NormalizeNativeBehaviorProofProbe(Probe);
}

FHyperTwistTrainingTilingNativeBehaviorProofProbe
UHyperTwistTrainingTilingLibrary::NormalizeNativeBehaviorProofProbe(
	const FHyperTwistTrainingTilingNativeBehaviorProofProbe& Probe
)
{
	FHyperTwistTrainingTilingNativeBehaviorProofProbe NormalizedProbe = Probe;

	if (NormalizedProbe.SchemaVersion.IsEmpty())
	{
		NormalizedProbe.SchemaVersion = TEXT("ht-tiling-native-behavior-proof/v1");
	}

	if (NormalizedProbe.ProofContractId.IsEmpty())
	{
		NormalizedProbe.ProofContractId = TEXT("tiling-native-behavior-proof");
	}

	if (NormalizedProbe.HostDecisionId.IsEmpty())
	{
		NormalizedProbe.HostDecisionId = TEXT("tiling-embedded-browser-host");
	}

	if (NormalizedProbe.StateBridgeContractId.IsEmpty())
	{
		NormalizedProbe.StateBridgeContractId = TEXT("tiling-browser-state-bridge");
	}

	if (NormalizedProbe.PuzzleDefinitionId.IsEmpty()
		&& NormalizedProbe.PuzzleState.Definition.IsStructurallyValid())
	{
		NormalizedProbe.PuzzleDefinitionId =
			NormalizedProbe.PuzzleState.Definition.PuzzleId;
	}

	if (NormalizedProbe.SessionId.IsEmpty()
		&& !NormalizedProbe.PuzzleState.SourceSessionId.IsEmpty())
	{
		NormalizedProbe.SessionId = NormalizedProbe.PuzzleState.SourceSessionId;
	}

	NormalizedProbe.SelectedCellId = NormalizedProbe.SelectedCellId.TrimStartAndEnd();
	NormalizedProbe.CandidateTwistIds =
		HyperTwistTrainingTilingLibraryInternal::NormalizeUniqueNonEmptyStrings(
			NormalizedProbe.CandidateTwistIds
		);
	NormalizedProbe.CommitTwistId = NormalizedProbe.CommitTwistId.TrimStartAndEnd();

	if (NormalizedProbe.CommitTwistId.IsEmpty()
		&& NormalizedProbe.CandidateTwistIds.Num() > 0)
	{
		NormalizedProbe.CommitTwistId = NormalizedProbe.CandidateTwistIds[0];
	}

	NormalizedProbe.PreviewTransformToken =
		NormalizedProbe.PreviewTransformToken.TrimStartAndEnd();
	if (NormalizedProbe.PreviewTransformToken.IsEmpty()
		&& !NormalizedProbe.SelectedCellId.IsEmpty()
		&& !NormalizedProbe.CommitTwistId.IsEmpty())
	{
		NormalizedProbe.PreviewTransformToken = FString::Printf(
			TEXT("native-preview::%s::%s"),
			*NormalizedProbe.SelectedCellId,
			*NormalizedProbe.CommitTwistId
		);
	}

	if (NormalizedProbe.SharedScrambleTokens.Num() == 0
		&& !NormalizedProbe.SharedScrambleNotation.IsEmpty())
	{
		NormalizedProbe.SharedScrambleTokens =
			ParseSharedScrambleNotation(NormalizedProbe.SharedScrambleNotation);
	}
	else
	{
		NormalizedProbe.SharedScrambleTokens =
			HyperTwistTrainingTilingLibraryInternal::NormalizeScrambleTokens(
				NormalizedProbe.SharedScrambleTokens
			);
	}

	NormalizedProbe.SharedScrambleNotation =
		BuildSharedScrambleNotation(NormalizedProbe.SharedScrambleTokens);

	if (NormalizedProbe.SourceRuntime.IsEmpty())
	{
		NormalizedProbe.SourceRuntime = TEXT("native-unreal-proof");
	}

	if (NormalizedProbe.CapturedAtUtc.IsEmpty())
	{
		if (!NormalizedProbe.PuzzleState.CapturedAtUtc.IsEmpty())
		{
			NormalizedProbe.CapturedAtUtc = NormalizedProbe.PuzzleState.CapturedAtUtc;
		}
		else if (!NormalizedProbe.TimerState.LastUpdatedAtUtc.IsEmpty())
		{
			NormalizedProbe.CapturedAtUtc = NormalizedProbe.TimerState.LastUpdatedAtUtc;
		}
		else if (!NormalizedProbe.TimerState.StartedAtUtc.IsEmpty())
		{
			NormalizedProbe.CapturedAtUtc = NormalizedProbe.TimerState.StartedAtUtc;
		}
	}

	if (NormalizedProbe.TimerState.TrainingSessionId.IsEmpty()
		&& !NormalizedProbe.SessionId.IsEmpty())
	{
		NormalizedProbe.TimerState.TrainingSessionId = NormalizedProbe.SessionId;
	}

	if (NormalizedProbe.TimerState.DeckId.IsEmpty())
	{
		NormalizedProbe.TimerState.DeckId = TEXT("Tiling");
	}

	if (NormalizedProbe.TimerState.CaseId.IsEmpty())
	{
		NormalizedProbe.TimerState.CaseId = NormalizedProbe.PuzzleDefinitionId;
	}

	if (NormalizedProbe.TimerState.LastUpdatedAtUtc.IsEmpty())
	{
		NormalizedProbe.TimerState.LastUpdatedAtUtc = NormalizedProbe.CapturedAtUtc;
	}

	if (NormalizedProbe.TimerState.DisplayedElapsedMs < NormalizedProbe.TimerState.SolveElapsedMs)
	{
		NormalizedProbe.TimerState.DisplayedElapsedMs =
			NormalizedProbe.TimerState.SolveElapsedMs;
	}

	if (NormalizedProbe.PuzzleState.SourceSessionId.IsEmpty()
		&& !NormalizedProbe.SessionId.IsEmpty())
	{
		NormalizedProbe.PuzzleState.SourceSessionId = NormalizedProbe.SessionId;
	}

	if (NormalizedProbe.PuzzleState.CapturedAtUtc.IsEmpty())
	{
		NormalizedProbe.PuzzleState.CapturedAtUtc = NormalizedProbe.CapturedAtUtc;
	}

	return NormalizedProbe;
}

bool UHyperTwistTrainingTilingLibrary::TrySerializeNativeBehaviorProofProbeToJson(
	const FHyperTwistTrainingTilingNativeBehaviorProofProbe& Probe,
	FString& OutJson
)
{
	const FHyperTwistTrainingTilingNativeBehaviorProofProbe NormalizedProbe =
		NormalizeNativeBehaviorProofProbe(Probe);
	return NormalizedProbe.IsStructurallyValid()
		&& HyperTwistTrainingTilingLibraryInternal::SerializeStructToJson(
			NormalizedProbe,
			OutJson
		);
}

bool UHyperTwistTrainingTilingLibrary::TryDeserializeNativeBehaviorProofProbeFromJson(
	const FString& Json,
	FHyperTwistTrainingTilingNativeBehaviorProofProbe& OutProbe
)
{
	OutProbe = FHyperTwistTrainingTilingNativeBehaviorProofProbe();

	FHyperTwistTrainingTilingNativeBehaviorProofProbe ParsedProbe;
	if (!HyperTwistTrainingTilingLibraryInternal::DeserializeStructFromJson(
			Json,
			ParsedProbe))
	{
		return false;
	}

	OutProbe = NormalizeNativeBehaviorProofProbe(ParsedProbe);
	return OutProbe.IsStructurallyValid();
}

FString UHyperTwistTrainingTilingLibrary::BuildPackageChecklistTsv(
	const FHyperTwistTrainingTilingReferenceBundle& Bundle
)
{
	using namespace HyperTwistTrainingTilingLibraryInternal;

	FString Output = TEXT("packageId\tlabel\texportPath\townershipRole\tlicensePosture\tretainedSurfaceTags\texplicitExclusions\n");
	for (const FHyperTwistTrainingTilingPackageReference& Reference : Bundle.PackageReferences)
	{
		FString RetainedTags;
		AppendJoined(RetainedTags, Reference.RetainedSurfaceTags);

		FString ExplicitExclusions;
		AppendJoined(ExplicitExclusions, Reference.ExplicitExclusions);

		Output += FString::Printf(
			TEXT("%s\t%s\t%s\t%s\t%s\t%s\t%s\n"),
			*Reference.PackageId,
			*Reference.Label,
			*Reference.ExportPath,
			*Reference.OwnershipRole,
			*Reference.LicensePosture,
			*RetainedTags,
			*ExplicitExclusions
		);
	}

	return Output;
}
