// Copyright HyperTwist, Inc. All Rights Reserved.

#include "Misc/AutomationTest.h"

#include "Engine/GameInstance.h"
#include "HyperTwistTraining/HyperTwistTrainingPersistenceLibrary.h"
#include "HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "UObject/Package.h"

#if WITH_AUTOMATION_TESTS

namespace HyperTwistCubeDeskBound5SocialChallengeTestInternal
{
	FHyperTwistTrainingDeck MakeDeck(const FString& DeckId)
	{
		FHyperTwistTrainingDeck Deck;
		Deck.DeckId = DeckId;
		Deck.Title = TEXT("Bound5 Social Deck");
		Deck.DeliveryModes = {EHyperTwistTrainingDeliveryMode::Timer};

		FHyperTwistTrainingCase TrainingCase;
		TrainingCase.CaseId = TEXT("bound5-case-1");
		TrainingCase.PuzzleId = TEXT("3x3x3x3");
		TrainingCase.PromptLabel = TEXT("Bound5 Case");
		TrainingCase.AllowedDeliveryModes = {EHyperTwistTrainingDeliveryMode::Timer};
		Deck.Cases.Add(TrainingCase);
		return Deck;
	}

	FHyperTwistTrainingEntitlementGateState MakeVisibleEntitledGate()
	{
		FHyperTwistTrainingEntitlementGateState GateState;
		GateState.GateId = TEXT("gate/local-social");
		GateState.FeatureId = TEXT("feature/local-social");
		GateState.Title = TEXT("Local Social");
		GateState.VisibilityLabel = TEXT("published");
		GateState.FeatureGovernanceContractId = TEXT("posthog-feature-governance-and-blast-radius");
		GateState.EarlyAccessContractId = TEXT("posthog-early-access-rollout");
		GateState.LifecycleStageId = TEXT("active");
		GateState.EnrollmentModeLabel = TEXT("automatic");
		GateState.ApprovedWorkflowTags = {TEXT("feature-governance"), TEXT("local-social")};
		GateState.bVisible = true;
		GateState.bEntitled = true;
		return GateState;
	}

	FHyperTwistTrainingFriendLinkRecord MakeFriendLinkRecord(
		const FString& UserId,
		const FString& FriendUserId,
		const FString& DisplayName,
		const EHyperTwistTrainingFriendLinkState State,
		const FString& ReferenceUtc
	)
	{
		FHyperTwistTrainingFriendLinkRecord FriendLinkRecord;
		FriendLinkRecord.LinkId = FString::Printf(TEXT("friend-link/%s/%s"), *UserId, *FriendUserId);
		FriendLinkRecord.UserId = UserId;
		FriendLinkRecord.FriendSnapshot.FriendUserId = FriendUserId;
		FriendLinkRecord.FriendSnapshot.DisplayName = DisplayName;
		FriendLinkRecord.FriendSnapshot.DeckId = TEXT("deck/hypercube-3333");
		FriendLinkRecord.FriendSnapshot.PublicationId =
			FString::Printf(TEXT("publication/%s/%s"), *FriendUserId, TEXT("deck/hypercube-3333"));
		FriendLinkRecord.FriendSnapshot.LastPublishedAtUtc = ReferenceUtc;
		FriendLinkRecord.Tags = {TEXT("local"), TEXT("bound5")};
		return UHyperTwistTrainingRepositoryLibrary::ResolveFriendLinkRecordState(
			FriendLinkRecord,
			State,
			ReferenceUtc
		);
	}

	FHyperTwistTrainingNotificationRecord MakeNotificationRecord(
		const FString& NotificationId,
		const FString& RecipientUserId,
		const FString& ActorUserId,
		const FString& FriendUserId,
		const FString& ChallengeId,
		const EHyperTwistTrainingNotificationKind Kind,
		const FString& CreatedAtUtc
	)
	{
		FHyperTwistTrainingNotificationRecord NotificationRecord;
		NotificationRecord.NotificationId = NotificationId;
		NotificationRecord.RecipientUserId = RecipientUserId;
		NotificationRecord.ActorUserId = ActorUserId;
		NotificationRecord.FriendUserId = FriendUserId;
		NotificationRecord.ChallengeId = ChallengeId;
		NotificationRecord.DeckId = TEXT("deck/hypercube-3333");
		NotificationRecord.EventId = TEXT("event/3x3x3x3/single");
		NotificationRecord.CreatedAtUtc = CreatedAtUtc;
		NotificationRecord.Title = TEXT("Bound5 notification");
		NotificationRecord.SummaryLine = TEXT("Local social notification");
		NotificationRecord.Kind = Kind;
		NotificationRecord.VisibilityState = EHyperTwistTrainingNotificationVisibilityState::Inbox;
		return NotificationRecord;
	}

	FHyperTwistTrainingLeaderboardEntry MakeLeaderboardEntry(
		const FString& EntryId,
		const FString& DisplayName,
		const FString& PrimaryValue,
		const FString& SecondaryValue,
		const FString& RecordedAtUtc
	)
	{
		FHyperTwistTrainingLeaderboardEntry Entry;
		Entry.EntryId = EntryId;
		Entry.EventId = TEXT("event/3x3x3x3/single");
		Entry.EventTitle = TEXT("3x3x3 Single");
		Entry.MetricLabel = TEXT("Single");
		Entry.SolverDisplayName = DisplayName;
		Entry.ProgramLabel = TEXT("HSC");
		Entry.RecordedAtUtc = RecordedAtUtc;
		Entry.RelativeDateLabel = TEXT("today");
		Entry.RankLabel = TEXT("#1 local");
		Entry.MetricPrimaryValue = PrimaryValue;
		Entry.MetricSecondaryValue = SecondaryValue;
		Entry.DeckId = TEXT("deck/hypercube-3333");
		Entry.bEligible = true;
		Entry.bPublished = true;
		return Entry;
	}

	FHyperTwistTrainingChallengeRecord MakeChallengeRecord(
		const FString& ChallengeId,
		const FString& ChallengerUserId,
		const FString& ChallengerDisplayName,
		const FString& RecipientUserId,
		const FString& RecipientDisplayName,
		const EHyperTwistTrainingChallengeLifecycleState LifecycleState,
		const EHyperTwistTrainingLocalMatchReadinessState MatchReadinessState,
		const FString& ReferenceUtc
	)
	{
		FHyperTwistTrainingChallengeRecord ChallengeRecord;
		ChallengeRecord.ChallengeId = ChallengeId;
		ChallengeRecord.ChallengerUserId = ChallengerUserId;
		ChallengeRecord.RecipientUserId = RecipientUserId;
		ChallengeRecord.DeckId = TEXT("deck/hypercube-3333");
		ChallengeRecord.EventId = TEXT("event/3x3x3x3/single");
		ChallengeRecord.Title = TEXT("Bound5 challenge");
		ChallengeRecord.SummaryLine = TEXT("Local comparison challenge");
		ChallengeRecord.SeedLabel = TEXT("single");
		ChallengeRecord.ChallengerSnapshot.UserId = ChallengerUserId;
		ChallengeRecord.ChallengerSnapshot.DisplayName = ChallengerDisplayName;
		ChallengeRecord.ChallengerSnapshot.PublicationId =
			FString::Printf(TEXT("publication/%s/%s"), *ChallengerUserId, TEXT("deck/hypercube-3333"));
		ChallengeRecord.ChallengerSnapshot.DeckId = TEXT("deck/hypercube-3333");
		ChallengeRecord.ChallengerSnapshot.Headline = TEXT("Local challenger");
		ChallengeRecord.ChallengerSnapshot.ReferenceEntry = MakeLeaderboardEntry(
			FString::Printf(TEXT("%s-entry"), *ChallengerUserId),
			ChallengerDisplayName,
			TEXT("12.34"),
			TEXT("12340 ms"),
			ReferenceUtc
		);
		ChallengeRecord.RecipientSnapshot.UserId = RecipientUserId;
		ChallengeRecord.RecipientSnapshot.DisplayName = RecipientDisplayName;
		ChallengeRecord.RecipientSnapshot.PublicationId =
			FString::Printf(TEXT("publication/%s/%s"), *RecipientUserId, TEXT("deck/hypercube-3333"));
		ChallengeRecord.RecipientSnapshot.DeckId = TEXT("deck/hypercube-3333");
		ChallengeRecord.RecipientSnapshot.Headline = TEXT("Local recipient");
		ChallengeRecord.RecipientSnapshot.ReferenceEntry = MakeLeaderboardEntry(
			FString::Printf(TEXT("%s-entry"), *RecipientUserId),
			RecipientDisplayName,
			TEXT("13.21"),
			TEXT("13210 ms"),
			ReferenceUtc
		);
		return UHyperTwistTrainingRepositoryLibrary::ResolveChallengeRecordState(
			ChallengeRecord,
			LifecycleState,
			MatchReadinessState,
			ReferenceUtc
		);
	}

	const FHyperTwistTrainingChallengeComparisonEntry* FindCurrentUserEntry(
		const FHyperTwistTrainingChallengeComparisonBundle& ComparisonBundle
	)
	{
		return ComparisonBundle.Entries.FindByPredicate(
			[](const FHyperTwistTrainingChallengeComparisonEntry& Entry)
			{
				return Entry.bCurrentUser;
			}
		);
	}
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistCubeDeskBound5FriendLinkLifecycleTest,
	"HyperTwist.CleanRoom.CubeDesk.Bound5.FriendLinkLifecycle",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistCubeDeskBound5FriendLinkLifecycleTest::RunTest(const FString& Parameters)
{
	FHyperTwistTrainingRepositoryState RepositoryState;
	FHyperTwistTrainingFriendLinkRecord FriendLinkRecord =
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeFriendLinkRecord(
			TEXT("bound5-user"),
			TEXT("friend-user"),
			TEXT("Friend User"),
			EHyperTwistTrainingFriendLinkState::PendingOutgoing,
			TEXT("2026-05-20T10:00:00Z")
		);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertFriendLinkRecord(
		RepositoryState,
		FriendLinkRecord
	);

	TArray<FHyperTwistTrainingFriendLinkRecord> FriendLinks =
		UHyperTwistTrainingRepositoryLibrary::ListFriendLinksForUser(
			RepositoryState,
			TEXT("bound5-user")
		);
	TestEqual(TEXT("One pending friend link must be stored."), FriendLinks.Num(), 1);
	if (FriendLinks.Num() != 1)
	{
		return false;
	}
	TestEqual(
		TEXT("The pending friend link must retain the outgoing state."),
		FriendLinks[0].State,
		EHyperTwistTrainingFriendLinkState::PendingOutgoing
	);

	FriendLinkRecord = UHyperTwistTrainingRepositoryLibrary::ResolveFriendLinkRecordState(
		FriendLinks[0],
		EHyperTwistTrainingFriendLinkState::Active,
		TEXT("2026-05-20T10:15:00Z")
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertFriendLinkRecord(
		RepositoryState,
		FriendLinkRecord
	);
	FriendLinks = UHyperTwistTrainingRepositoryLibrary::ListFriendLinksForUser(
		RepositoryState,
		TEXT("bound5-user")
	);

	TestEqual(TEXT("Friend-link dedupe must retain one active record."), FriendLinks.Num(), 1);
	if (FriendLinks.Num() != 1)
	{
		return false;
	}
	TestEqual(
		TEXT("The active friend link must retain the accepted lifecycle state."),
		FriendLinks[0].State,
		EHyperTwistTrainingFriendLinkState::Active
	);
	TestTrue(
		TEXT("The active friend link must record a responded-at timestamp."),
		!FriendLinks[0].RespondedAtUtc.IsEmpty()
	);

	const FHyperTwistTrainingRepositoryIntegrityReport IntegrityReport =
		UHyperTwistTrainingRepositoryLibrary::DeriveRepositoryIntegrityReport(RepositoryState);
	TestEqual(TEXT("Repository integrity must retain one friend link."), IntegrityReport.TotalFriendLinkCount, 1);
	TestEqual(TEXT("Repository integrity must retain zero duplicate friend links."), IntegrityReport.DuplicateFriendLinkCount, 0);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistCubeDeskBound5NotificationVisibilityTest,
	"HyperTwist.CleanRoom.CubeDesk.Bound5.NotificationVisibility",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistCubeDeskBound5NotificationVisibilityTest::RunTest(const FString& Parameters)
{
	FHyperTwistTrainingRepositoryState RepositoryState;
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertNotificationRecord(
		RepositoryState,
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeNotificationRecord(
			TEXT("bound5-notification"),
			TEXT("bound5-user"),
			TEXT("friend-user"),
			TEXT("friend-user"),
			TEXT(""),
			EHyperTwistTrainingNotificationKind::FriendRequest,
			TEXT("2026-05-20T11:00:00Z")
		)
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::MarkNotificationRead(
		RepositoryState,
		TEXT("bound5-notification"),
		TEXT("2026-05-20T11:01:00Z")
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::AcknowledgeNotification(
		RepositoryState,
		TEXT("bound5-notification"),
		TEXT("2026-05-20T11:02:00Z")
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::ArchiveNotification(
		RepositoryState,
		TEXT("bound5-notification"),
		TEXT("2026-05-20T11:03:00Z")
	);

	const TArray<FHyperTwistTrainingNotificationRecord> Notifications =
		UHyperTwistTrainingRepositoryLibrary::ListNotificationsForUser(
			RepositoryState,
			TEXT("bound5-user")
		);
	TestEqual(TEXT("Exactly one notification must be retained."), Notifications.Num(), 1);
	if (Notifications.Num() != 1)
	{
		return false;
	}
	TestTrue(TEXT("The notification must be marked as read."), Notifications[0].bRead);
	TestTrue(TEXT("The notification must be marked as acknowledged."), Notifications[0].bAcknowledged);
	TestEqual(
		TEXT("The notification must move into the archived visibility state."),
		Notifications[0].VisibilityState,
		EHyperTwistTrainingNotificationVisibilityState::Archived
	);

	const FHyperTwistTrainingSocialChallengeBundle SocialBundle =
		UHyperTwistTrainingRepositoryLibrary::DeriveSocialChallengeBundle(
			RepositoryState,
			TEXT("bound5-user"),
			TEXT("2026-05-20T11:04:00Z"),
			HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeVisibleEntitledGate()
		);
	TestEqual(TEXT("Archived notifications must no longer count as inbox-visible."), SocialBundle.VisibleNotificationCount, 0);
	TestEqual(TEXT("Archived notifications must not count as unread."), SocialBundle.UnreadNotificationCount, 0);
	TestEqual(TEXT("Acknowledged notifications must be counted."), SocialBundle.AcknowledgedNotificationCount, 1);
	TestEqual(TEXT("Archived notifications must be counted."), SocialBundle.ArchivedNotificationCount, 1);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistCubeDeskBound5ChallengeLifecycleTest,
	"HyperTwist.CleanRoom.CubeDesk.Bound5.ChallengeLifecycle",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistCubeDeskBound5ChallengeLifecycleTest::RunTest(const FString& Parameters)
{
	FHyperTwistTrainingRepositoryState RepositoryState;
	FHyperTwistTrainingChallengeRecord ChallengeRecord =
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeChallengeRecord(
			TEXT("bound5-challenge"),
			TEXT("bound5-user"),
			TEXT("Bound5 User"),
			TEXT("friend-user"),
			TEXT("Friend User"),
			EHyperTwistTrainingChallengeLifecycleState::Pending,
			EHyperTwistTrainingLocalMatchReadinessState::Seeded,
			TEXT("2026-05-20T12:00:00Z")
		);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertChallengeRecord(
		RepositoryState,
		ChallengeRecord
	);

	ChallengeRecord = UHyperTwistTrainingRepositoryLibrary::ResolveChallengeRecordState(
		ChallengeRecord,
		EHyperTwistTrainingChallengeLifecycleState::Accepted,
		EHyperTwistTrainingLocalMatchReadinessState::AwaitingAttempts,
		TEXT("2026-05-20T12:05:00Z")
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertChallengeRecord(
		RepositoryState,
		ChallengeRecord
	);

	ChallengeRecord = UHyperTwistTrainingRepositoryLibrary::ResolveChallengeRecordState(
		ChallengeRecord,
		EHyperTwistTrainingChallengeLifecycleState::Ready,
		EHyperTwistTrainingLocalMatchReadinessState::ReadyToCompare,
		TEXT("2026-05-20T12:10:00Z")
	);
	RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertChallengeRecord(
		RepositoryState,
		ChallengeRecord
	);

	const TArray<FHyperTwistTrainingChallengeRecord> Challenges =
		UHyperTwistTrainingRepositoryLibrary::ListChallengesForUser(
			RepositoryState,
			TEXT("bound5-user")
		);
	TestEqual(TEXT("Exactly one challenge must be retained."), Challenges.Num(), 1);
	if (Challenges.Num() != 1)
	{
		return false;
	}
	TestEqual(
		TEXT("The challenge lifecycle must advance into the ready state."),
		Challenges[0].LifecycleState,
		EHyperTwistTrainingChallengeLifecycleState::Ready
	);
	TestEqual(
		TEXT("The challenge readiness must advance into the ready-to-compare state."),
		Challenges[0].MatchReadinessState,
		EHyperTwistTrainingLocalMatchReadinessState::ReadyToCompare
	);
	TestTrue(TEXT("Accepted challenges must retain a responded-at timestamp."), !Challenges[0].RespondedAtUtc.IsEmpty());
	TestTrue(TEXT("Ready challenges must retain a ready-at timestamp."), !Challenges[0].ReadyAtUtc.IsEmpty());

	const FHyperTwistTrainingSocialChallengeBundle SocialBundle =
		UHyperTwistTrainingRepositoryLibrary::DeriveSocialChallengeBundle(
			RepositoryState,
			TEXT("bound5-user"),
			TEXT("2026-05-20T12:11:00Z"),
			HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeVisibleEntitledGate()
		);
	TestEqual(TEXT("The ready challenge must count as active."), SocialBundle.ActiveChallengeCount, 1);
	TestEqual(TEXT("The ready challenge must count as ready to compare."), SocialBundle.ReadyChallengeCount, 1);
	return true;
}

IMPLEMENT_SIMPLE_AUTOMATION_TEST(
	FHyperTwistCubeDeskBound5ComparisonProjectionTest,
	"HyperTwist.CleanRoom.CubeDesk.Bound5.ComparisonProjection",
	EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)

bool FHyperTwistCubeDeskBound5ComparisonProjectionTest::RunTest(const FString& Parameters)
{
	UGameInstance* GameInstance = NewObject<UGameInstance>();
	TestNotNull(TEXT("A game instance must exist for the Bound 5 subsystem test."), GameInstance);
	if (GameInstance == nullptr)
	{
		return false;
	}

	UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
	TestNotNull(TEXT("The training subsystem must be created."), TrainingSubsystem);
	if (TrainingSubsystem == nullptr)
	{
		return false;
	}

	const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeDeck(TEXT("deck/hypercube-3333")),
		TEXT("bound5-user"),
		TEXT("bound5-social-session"),
		EHyperTwistTrainingDeliveryMode::Timer
	);
	TestTrue(TEXT("A social-challenge repository view must start from a valid run."), RunState.IsStructurallyValid());
	if (!RunState.IsStructurallyValid())
	{
		return false;
	}

	const bool bSavedFriendLink = TrainingSubsystem->SaveFriendLinkRecord(
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeFriendLinkRecord(
			TEXT("bound5-user"),
			TEXT("friend-user"),
			TEXT("Friend User"),
			EHyperTwistTrainingFriendLinkState::Active,
			TEXT("2026-05-20T13:00:00Z")
		)
	);
	TestTrue(TEXT("The subsystem must persist local friend-link state."), bSavedFriendLink);

	const bool bSavedNotification = TrainingSubsystem->SaveNotificationRecord(
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeNotificationRecord(
			TEXT("bound5-challenge-notification"),
			TEXT("bound5-user"),
			TEXT("friend-user"),
			TEXT("friend-user"),
			TEXT("bound5-comparison-challenge"),
			EHyperTwistTrainingNotificationKind::ChallengeReady,
			TEXT("2026-05-20T13:01:00Z")
		)
	);
	TestTrue(TEXT("The subsystem must persist local notification state."), bSavedNotification);

	const bool bSavedChallenge = TrainingSubsystem->SaveChallengeRecord(
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::MakeChallengeRecord(
			TEXT("bound5-comparison-challenge"),
			TEXT("bound5-user"),
			TEXT("Bound5 User"),
			TEXT("friend-user"),
			TEXT("Friend User"),
			EHyperTwistTrainingChallengeLifecycleState::Ready,
			EHyperTwistTrainingLocalMatchReadinessState::ReadyToCompare,
			TEXT("2026-05-20T13:02:00Z")
		)
	);
	TestTrue(TEXT("The subsystem must persist local challenge state."), bSavedChallenge);

	const FHyperTwistTrainingSocialChallengeBundle SocialBundle =
		TrainingSubsystem->GetActiveSocialChallengeBundle();
	TestTrue(TEXT("The subsystem must materialize a structurally valid social bundle."), SocialBundle.IsStructurallyValid());
	TestEqual(TEXT("The subsystem must surface one stored friend link."), SocialBundle.FriendLinks.Num(), 1);
	TestEqual(TEXT("The subsystem must surface one stored notification."), SocialBundle.Notifications.Num(), 1);
	TestEqual(TEXT("The subsystem must surface one stored challenge."), SocialBundle.Challenges.Num(), 1);
	TestEqual(TEXT("The subsystem must surface one comparison bundle."), SocialBundle.ComparisonBundles.Num(), 1);
	if (SocialBundle.ComparisonBundles.Num() != 1)
	{
		return false;
	}

	const FHyperTwistTrainingChallengeComparisonBundle& ComparisonBundle = SocialBundle.ComparisonBundles[0];
	TestTrue(TEXT("The comparison bundle must resolve as structurally valid."), ComparisonBundle.IsStructurallyValid());
	TestEqual(TEXT("The comparison bundle must retain the challenge id."), ComparisonBundle.ChallengeId, TEXT("bound5-comparison-challenge"));
	TestEqual(TEXT("The comparison bundle must retain two compared entries."), ComparisonBundle.Entries.Num(), 2);
	TestEqual(TEXT("The comparison bundle must retain the landed event title."), ComparisonBundle.EventTitle, TEXT("3x3x3x3 Single"));
	TestEqual(TEXT("The comparison bundle must retain the landed metric label."), ComparisonBundle.MetricLabel, TEXT("Time"));
	TestTrue(TEXT("The comparison bundle must report ready-to-compare state."), ComparisonBundle.bReadyToCompare);

	const FHyperTwistTrainingChallengeComparisonEntry* CurrentUserEntry =
		HyperTwistCubeDeskBound5SocialChallengeTestInternal::FindCurrentUserEntry(ComparisonBundle);
	TestNotNull(TEXT("One comparison entry must be marked as the current user."), CurrentUserEntry);
	if (CurrentUserEntry == nullptr)
	{
		return false;
	}

	TestEqual(TEXT("The current-user comparison entry must retain the user id."), CurrentUserEntry->UserId, TEXT("bound5-user"));
	TestEqual(TEXT("The current-user comparison entry must retain the local metric value."), CurrentUserEntry->MetricPrimaryValue, TEXT("12.34"));
	TestEqual(TEXT("The current-user comparison entry must retain the program badge."), CurrentUserEntry->ProgramLabel, TEXT("HSC"));

	const FHyperTwistTrainingRepositoryRoundTripVerification Verification =
		UHyperTwistTrainingPersistenceLibrary::VerifyTrainingRepositoryStateRoundTrip(
			TrainingSubsystem->GetTrainingRepositoryState(),
			TEXT("Automation/HyperTwistCubeDeskBound5RepositoryRoundTrip.json")
		);
	TestTrue(TEXT("Repository roundtrip verification must pass for Bound 5 state."), Verification.bDidRoundTripPass);
	TestEqual(TEXT("Roundtrip verification must retain one friend link."), Verification.LoadedFriendLinkCount, 1);
	TestEqual(TEXT("Roundtrip verification must retain one notification."), Verification.LoadedNotificationCount, 1);
	TestEqual(TEXT("Roundtrip verification must retain one challenge."), Verification.LoadedChallengeCount, 1);

	return true;
}

#endif
