import { Loader2 } from 'lucide-react';
import { AnimatePresence, motion } from 'motion/react';
import {
  type Dispatch,
  lazy,
  type SetStateAction,
  Suspense,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
import type {
  BarkBattleConfigEditorPayload,
  BarkBattleDraftConfig,
  BarkBattlePublishedConfig,
  BarkBattleWorkSummary,
} from '../../../packages/shared/src/contracts/barkBattle';
import type {
  BigFishRuntimeSnapshotResponse,
  BigFishSessionSnapshotResponse,
  ExecuteBigFishActionRequest,
  SendBigFishMessageRequest,
  SubmitBigFishInputRequest,
} from '../../../packages/shared/src/contracts/bigFish';
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
import type {
  CreativeAgentSessionSnapshot,
  CreativeAgentSseEvent,
  CreativeDraftEditResult,
  StreamCreativeAgentMessageRequest,
} from '../../../packages/shared/src/contracts/creativeAgent';
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldWorkSummary';
import type {
  BabyObjectMatchDraft,
  CreateBabyObjectMatchDraftRequest,
} from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type {
  JumpHopJumpRequest,
  JumpHopWorkSummaryResponse,
} from '../../../packages/shared/src/contracts/jumpHop';
import type {
  CreateMatch3DSessionRequest,
  ExecuteMatch3DActionRequest,
  Match3DActionResponse,
  Match3DAgentSessionSnapshot,
  Match3DSessionResponse,
  SendMatch3DMessageRequest,
} from '../../../packages/shared/src/contracts/match3dAgent';
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
import type {
  Match3DGeneratedBackgroundAsset,
  Match3DGeneratedItemAsset,
  Match3DWorkProfile,
  Match3DWorkSummary,
} from '../../../packages/shared/src/contracts/match3dWorks';
import type {
  PuzzleAgentActionRequest,
  PuzzleAgentOperationRecord,
} from '../../../packages/shared/src/contracts/puzzleAgentActions';
import type {
  PuzzleDraftLevel,
  PuzzleResultDraft,
} from '../../../packages/shared/src/contracts/puzzleAgentDraft';
import type {
  CreatePuzzleAgentSessionRequest,
  PuzzleAgentSessionSnapshot,
  SendPuzzleAgentMessageRequest,
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
import { isPuzzleCompileActionReady } from './puzzleDraftGenerationState';
import type { PuzzleCreativeTemplateSelection } from '../../../packages/shared/src/contracts/puzzleCreativeTemplate';
import type {
  PuzzleRunSnapshot,
  PuzzleRuntimePropKind,
  SubmitPuzzleLeaderboardRequest,
} from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type {
  CustomWorldGalleryCard,
  CustomWorldLibraryEntry,
  ProfileDashboardSummary,
  ProfilePlayedWorkSummary,
  ProfilePlayStatsResponse,
  ProfileSaveArchiveResumeResponse,
  ProfileSaveArchiveSummary,
} from '../../../packages/shared/src/contracts/runtime';
import type {
  CreateSquareHoleSessionRequest,
  ExecuteSquareHoleActionRequest,
  SendSquareHoleMessageRequest,
  SquareHoleActionResponse,
  SquareHoleSessionResponse,
  SquareHoleSessionSnapshot,
} from '../../../packages/shared/src/contracts/squareHoleAgent';
import type { SquareHoleRunSnapshot } from '../../../packages/shared/src/contracts/squareHoleRuntime';
import type {
  SquareHoleWorkProfile,
  SquareHoleWorkSummary,
} from '../../../packages/shared/src/contracts/squareHoleWorks';
import type {
  CreateVisualNovelSessionRequest,
  ExecuteVisualNovelAgentActionRequest,
  SendVisualNovelMessageRequest,
  VisualNovelAgentSessionSnapshot,
  VisualNovelResultDraft,
  VisualNovelRunSnapshot,
  VisualNovelRuntimeActionRequest,
  VisualNovelSessionResponse,
  VisualNovelWorkDetail,
  VisualNovelWorkSummary,
} from '../../../packages/shared/src/contracts/visualNovel';
import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish';
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
import {
  buildPublicWorkStagePath,
  pushAppHistoryPath,
} from '../../routing/appPageRoutes';
import { resolveWorkNotFoundRecoveryAction } from '../../routing/runtimeNotFoundRecovery';
import {
  ApiClientError,
  BACKGROUND_AUTH_REQUEST_OPTIONS,
  getStoredAccessToken,
} from '../../services/apiClient';
import {
  ensureRuntimeGuestToken,
  getPublicAuthUserByCode,
  getPublicAuthUserById,
} from '../../services/authService';
import {
  createBarkBattleDraft,
  deleteBarkBattleWork,
  listBarkBattleGallery,
  listBarkBattleWorks,
  publishBarkBattleWork,
  updateBarkBattleDraftConfig,
} from '../../services/bark-battle-creation';
import { startBarkBattleRun } from '../../services/bark-battle-runtime';
import {
  createBigFishCreationSession,
  executeBigFishCreationAction,
  getBigFishCreationSession,
  streamBigFishCreationMessage,
} from '../../services/big-fish-creation';
import {
  likeBigFishGalleryWork,
  listBigFishGallery,
  remixBigFishGalleryWork,
} from '../../services/big-fish-gallery';
import {
  recordBigFishPlay,
  startBigFishRun as startBigFishRuntimeRun,
  submitBigFishInput as submitBigFishRuntimeInput,
} from '../../services/big-fish-runtime';
import {
  deleteBigFishWork,
  listBigFishWorks,
} from '../../services/big-fish-works';
import {
  type CreationEntryConfig,
  fetchCreationEntryConfig,
} from '../../services/creationEntryConfigService';
import {
  clearCreationUrlState,
  type CreationUrlState,
  isCreationRestorePath,
  readCreationUrlState,
  writeCreationUrlState,
} from '../../services/creationUrlState';
import {
  cancelCreativeAgentSession,
  confirmCreativePuzzleTemplate,
  createCreativeAgentSession,
  streamCreativeAgentMessage,
  streamCreativeDraftEdit,
} from '../../services/creative-agent';
import {
  readCustomWorldAgentUiState,
  shouldRestoreCustomWorldAgentUiState,
} from '../../services/customWorldAgentUiState';
import {
  createBabyObjectMatchDraft,
  deleteLocalBabyObjectMatchDraft,
  hasBabyObjectMatchPlaceholderAssets,
  listLocalBabyObjectMatchDrafts,
  publishBabyObjectMatchWork,
  regenerateBabyObjectMatchDraftAssets,
  saveBabyObjectMatchDraft,
} from '../../services/edutainment-baby-object';
import {
  jumpHopClient,
  type JumpHopGalleryCardResponse,
  type JumpHopRunResponse,
  type JumpHopRuntimeRequestOptions,
  type JumpHopSessionResponse,
  type JumpHopSessionSnapshotResponse,
  JumpHopWorkProfileResponse,
  JumpHopWorkspaceCreateRequest,
} from '../../services/jump-hop/jumpHopClient';
import { match3dCreationClient } from '../../services/match3d-creation';
import { createServerMatch3DRuntimeAdapter } from '../../services/match3d-runtime';
import {
  deleteMatch3DWork,
  getMatch3DWorkDetail,
  listMatch3DGallery,
  listMatch3DWorks,
} from '../../services/match3d-works';
import {
  hasMatch3DGeneratedImageAsset,
  mergeMatch3DGeneratedItemAssetsForRuntime,
  normalizeMatch3DGeneratedItemAssetsForRuntime,
  preloadMatch3DGeneratedRuntimeAssets,
} from '../../services/match3dGeneratedModelCache';
import {
  buildBabyObjectMatchGenerationAnchorEntries,
  buildBigFishGenerationAnchorEntries,
  buildJumpHopGenerationAnchorEntries,
  buildMatch3DGenerationAnchorEntries,
  buildMiniGameDraftGenerationProgress,
  buildPuzzleClearGenerationAnchorEntries,
  buildPuzzleGenerationAnchorEntries,
  buildSquareHoleGenerationAnchorEntries,
  buildWoodenFishGenerationAnchorEntries,
  createMiniGameDraftGenerationState,
  resolveMiniGameDraftGenerationStartedAtMs,
  type MiniGameDraftGenerationKind,
  type MiniGameDraftGenerationPhase,
  type MiniGameDraftGenerationState,
} from '../../services/miniGameDraftGenerationProgress';
import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient';
import { UnifiedCreationPage } from '../unified-creation/UnifiedCreationPage';
import {
  getUnifiedCreationSpec,
  type UnifiedCreationPlayId,
} from '../unified-creation/unifiedCreationSpecs';
import {
  buildBabyObjectMatchPublicWorkCode,
  buildBarkBattlePublicWorkCode,
  buildBigFishPublicWorkCode,
  buildJumpHopPublicWorkCode,
  buildMatch3DPublicWorkCode,
  buildPuzzleClearPublicWorkCode,
  buildPuzzlePublicWorkCode,
  buildSquareHolePublicWorkCode,
  buildVisualNovelPublicWorkCode,
  buildWoodenFishPublicWorkCode,
  isSameBabyObjectMatchPublicWorkCode,
  isSameBarkBattlePublicWorkCode,
  isSameBigFishPublicWorkCode,
  isSameJumpHopPublicWorkCode,
  isSameMatch3DPublicWorkCode,
  isSamePuzzleClearPublicWorkCode,
  isSamePuzzlePublicWorkCode,
  isSameSquareHolePublicWorkCode,
  isSameVisualNovelPublicWorkCode,
  isSameWoodenFishPublicWorkCode,
} from '../../services/publicWorkCode';
import {
  createPuzzleAgentSession,
  executePuzzleAgentAction,
  getPuzzleAgentSession,
  streamPuzzleAgentMessage,
} from '../../services/puzzle-agent';
import {
  puzzleClearClient,
  type PuzzleClearGalleryCardResponse,
  type PuzzleClearRunResponse,
  type PuzzleClearSessionResponse,
  type PuzzleClearSessionSnapshotResponse,
  type PuzzleClearWorkProfileResponse,
  type PuzzleClearWorkspaceCreateRequest,
  type PuzzleClearWorkSummaryResponse,
} from '../../services/puzzle-clear/puzzleClearClient';
import {
  advancePuzzleClearLocalLevel,
  createPuzzleClearLocalRuntimeSnapshot,
  isPuzzleClearLocalRuntimeSnapshot,
  markPuzzleClearLocalTimeUp,
  retryPuzzleClearLocalLevel,
  swapPuzzleClearLocalCards,
} from '../../services/puzzle-clear/puzzleClearLocalRuntime';
import {
  getPuzzleGalleryDetail,
  likePuzzleGalleryWork,
  listPuzzleGallery,
  remixPuzzleGalleryWork,
} from '../../services/puzzle-gallery';
import {
  generatePuzzleOnboardingWork,
  savePuzzleOnboardingWork,
} from '../../services/puzzle-onboarding';
import {
  advancePuzzleNextLevel,
  startPuzzleRun,
  submitPuzzleLeaderboard,
} from '../../services/puzzle-runtime';
import {
  advanceLocalPuzzleLevel,
  applyLocalPuzzleFreezeTime,
  dragLocalPuzzlePiece,
  extendLocalPuzzleTime,
  isLocalPuzzleRun,
  refreshLocalPuzzleTimer,
  resolvePuzzleRestartLevelId,
  restartLocalPuzzleLevel,
  setLocalPuzzlePaused,
  startLocalPuzzleRun,
  submitLocalPuzzleLeaderboard,
  swapLocalPuzzlePieces,
} from '../../services/puzzle-runtime/puzzleLocalRuntime';
import {
  claimPuzzleWorkPointIncentive,
  deletePuzzleWork,
  listPuzzleWorks,
  updatePuzzleWork,
} from '../../services/puzzle-works';
import {
  clearPuzzleRuntimeUrlState,
  type PuzzleRuntimeUrlState,
  readPuzzleRuntimeUrlState,
  writePuzzleRuntimeUrlState,
} from '../../services/puzzleRuntimeUrlState';
import { deleteRpgCreationAgentSession } from '../../services/rpg-creation';
import { rpgCreationPreviewAdapter } from '../../services/rpg-creation/rpgCreationPreviewAdapter';
import {
  deleteRpgEntryWorldProfile,
  getRpgEntryWorldGalleryDetailByCode,
  likeRpgEntryWorldGallery,
  recordRpgEntryWorldGalleryPlay,
  remixRpgEntryWorldGallery,
} from '../../services/rpg-entry/rpgEntryLibraryClient';
import {
  getRpgProfilePlayStats,
  submitRpgProfileFeedback,
} from '../../services/rpg-entry/rpgProfileClient';
import { requestRpgRuntimeJson } from '../../services/rpg-runtime/rpgRuntimeRequest';
import { squareHoleCreationClient } from '../../services/square-hole-creation';
import {
  dropSquareHoleShape,
  finishSquareHoleTimeUp,
  restartSquareHoleRun,
  startSquareHoleRun,
  stopSquareHoleRun,
} from '../../services/square-hole-runtime';
import {
  deleteSquareHoleWork,
  getSquareHoleWorkDetail,
  listSquareHoleGallery,
  listSquareHoleWorks,
} from '../../services/square-hole-works';
import {
  compileVisualNovelWorkProfile,
  createVisualNovelSession,
  executeVisualNovelAction,
  getVisualNovelSession,
  streamVisualNovelMessage,
} from '../../services/visual-novel-creation';
import {
  listVisualNovelGallery,
  startVisualNovelRun,
  streamVisualNovelRuntimeAction,
} from '../../services/visual-novel-runtime';
import {
  deleteVisualNovelWork,
  getVisualNovelWorkDetail,
  listVisualNovelWorks,
  publishVisualNovelWork,
  updateVisualNovelWork,
} from '../../services/visual-novel-works';
import {
  woodenFishClient,
  type WoodenFishGalleryCardResponse,
  type WoodenFishRunResponse,
  type WoodenFishSessionResponse,
  type WoodenFishSessionSnapshotResponse,
  type WoodenFishWorkProfileResponse,
  type WoodenFishWorkspaceCreateRequest,
} from '../../services/wooden-fish/woodenFishClient';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { PublishShareModal } from '../common/PublishShareModal';
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
import { UnifiedModal } from '../common/UnifiedModal';
import { resolveCreativeAgentTargetSelectionStage } from '../creative-agent/creativeAgentViewModel';
import {
  buildCreationWorkShelfItems,
  type CreationWorkShelfItem,
  isPersistedBarkBattleDraftGenerating,
  isPersistedPuzzleDraftGenerating,
  resolvePuzzleWorkCoverImageSrc,
} from '../custom-world-home/creationWorkShelf';
import {
  buildPlatformPublicGalleryCardKey,
  isBarkBattleGalleryEntry,
  isBigFishGalleryEntry,
  isCustomWorldGalleryEntry,
  isEdutainmentGalleryEntry,
  isJumpHopGalleryEntry,
  isMatch3DGalleryEntry,
  isPuzzleClearGalleryEntry,
  isPuzzleGalleryEntry,
  isSquareHoleGalleryEntry,
  isVisualNovelGalleryEntry,
  isWoodenFishGalleryEntry,
  mapBabyObjectMatchDraftToPlatformGalleryCard,
  mapBarkBattleWorkToPlatformGalleryCard,
  mapBigFishWorkToPlatformGalleryCard,
  mapJumpHopWorkToPlatformGalleryCard,
  mapMatch3DWorkToPlatformGalleryCard,
  mapPuzzleClearWorkToPlatformGalleryCard,
  mapPuzzleWorkToPlatformGalleryCard,
  mapSquareHoleWorkToPlatformGalleryCard,
  mapVisualNovelWorkToPlatformGalleryCard,
  mapWoodenFishWorkToPlatformGalleryCard,
  type PlatformPublicGalleryCard,
  resolvePlatformPublicWorkCode,
} from '../rpg-entry/rpgEntryWorldPresentation';
import { useRpgCreationAgentOperationPolling } from '../rpg-entry/useRpgCreationAgentOperationPolling';
import { useRpgCreationEnterWorld } from '../rpg-entry/useRpgCreationEnterWorld';
import { useRpgCreationResultAutosave } from '../rpg-entry/useRpgCreationResultAutosave';
import { useRpgCreationSessionController } from '../rpg-entry/useRpgCreationSessionController';
import {
  buildVisualNovelEntryGenerationAnchorEntries,
  buildVisualNovelEntryGenerationProgress,
  type VisualNovelEntryFormPayload,
} from '../visual-novel-creation/visualNovelEntryGeneration';
import { createMockVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelMockData';
import {
  type BarkBattleGenerationStatus,
  buildBarkBattleWorkSummaryFromDraft,
  mergeBarkBattleWorksByWorkId,
  mergeBarkBattleWorkSummary,
  shouldPreserveLocalBarkBattleWorkOnRefresh,
} from './barkBattleWorkCache';
import {
  canExposePublicWork,
  EDUTAINMENT_HIDDEN_MESSAGE,
  filterGeneralPublicWorks,
} from './platformEdutainmentVisibility';
import { buildPlatformRecommendedEntries } from './platformRecommendation';
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
import {
  derivePlatformCreationTypes,
  isPlatformCreationTypeOpen,
  isPlatformCreationTypeVisible,
} from './platformEntryCreationTypes';
import {
  PuzzleOnboardingLoginOverlay,
  type PuzzleOnboardingPhase,
  PuzzleOnboardingView,
} from './PlatformEntryFlowShellImpl/PuzzleOnboardingView';
import {
  PlatformEntryHomeView,
  type PlatformHomeTab,
} from './PlatformEntryHomeView';
import { usePlatformDesktopLayout } from './platformEntryResponsive';
import {
  buildCreationHubFallbackItems,
  resolveRpgCreationErrorMessage,
} from './platformEntryShared';
import type {
  PlatformEntryFlowShellProps,
  SelectionStage,
} from './platformEntryTypes';
import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView';
import {
  PlatformErrorDialog,
  type PlatformErrorDialogPayload,
} from './PlatformErrorDialog';
import { PlatformFeedbackView } from './PlatformFeedbackView';
import {
  PlatformTaskCompletionDialog,
  type PlatformTaskCompletionDialogPayload,
} from './PlatformTaskCompletionDialog';
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
import { usePlatformEntryBootstrap } from './usePlatformEntryBootstrap';
import { usePlatformEntryLibraryDetail } from './usePlatformEntryLibraryDetail';
import { usePlatformEntryNavigation } from './usePlatformEntryNavigation';

type AgentResultPublishGateView = {
  blockers: string[];
  publishReady: boolean;
};
type DraftGenerationNoticeStatus = 'generating' | 'ready' | 'failed';
type DraftGenerationNotice = {
  status: DraftGenerationNoticeStatus;
  seen: boolean;
  completedAtMs?: number;
  message?: string;
};
type DraftGenerationNoticeMap = Record<string, DraftGenerationNotice>;
type CreationWorkShelfKind = CreationWorkShelfItem['kind'];
type PendingDraftShelfState = {
  status: DraftGenerationNoticeStatus;
  seen: boolean;
  updatedAt: string;
  title?: string;
  summary?: string;
};
type PendingDraftShelfMap = Partial<
  Record<
    Exclude<CreationWorkShelfKind, 'rpg'>,
    Record<string, PendingDraftShelfState>
  >
>;
type CreationFlowReturnTarget = 'create' | 'draft-shelf';
type Match3DBackgroundCompileTask = {
  session: Match3DAgentSessionSnapshot;
  payload: CreateMatch3DSessionRequest;
  generationState: MiniGameDraftGenerationState;
  error: string | null;
};
type PuzzleBackgroundCompileTask = {
  session: PuzzleAgentSessionSnapshot;
  payload: CreatePuzzleAgentSessionRequest;
  generationState: MiniGameDraftGenerationState;
  error: string | null;
};

type MiniGameGenerationProgressTickStateMap = Partial<
  Record<MiniGameDraftGenerationKind, MiniGameDraftGenerationState | null>
>;

export function resolveMiniGameGenerationProgressTickState(
  selectionStage: SelectionStage,
  states: MiniGameGenerationProgressTickStateMap,
) {
  const stageKindMap: Partial<
    Record<SelectionStage, MiniGameDraftGenerationKind>
  > = {
    'puzzle-generating': 'puzzle',
    'big-fish-generating': 'big-fish',
    'square-hole-generating': 'square-hole',
    'match3d-generating': 'match3d',
    'baby-object-match-generating': 'baby-object-match',
    'jump-hop-generating': 'jump-hop',
    'puzzle-clear-generating': 'puzzle-clear',
    'wooden-fish-generating': 'wooden-fish',
  };
  const kind = stageKindMap[selectionStage];

  return kind ? (states[kind] ?? null) : null;
}

type PuzzleDetailReturnTarget = {
  tab: PlatformHomeTab;
};

type PuzzleRuntimeReturnStage =
  | 'puzzle-result'
  | 'puzzle-gallery-detail'
  | 'work-detail'
  | 'platform';
type PuzzleRuntimeAuthMode = 'default' | 'isolated';

type PuzzleOnboardingDraft = {
  promptText: string;
  item: PuzzleWorkSummary;
};

type BigFishRuntimeReturnStage = 'big-fish-result' | 'work-detail' | 'platform';
type BarkBattleRuntimeReturnStage =
  | 'bark-battle-result'
  | 'work-detail'
  | 'platform';
type BigFishRuntimeSessionSource = 'draft' | 'work' | null;
type RecommendRuntimeKind =
  | 'bark-battle'
  | 'big-fish'
  | 'edutainment'
  | 'jump-hop'
  | 'match3d'
  | 'puzzle'
  | 'puzzle-clear'
  | 'square-hole'
  | 'wooden-fish'
  | 'visual-novel'
  | 'rpg';
type SquareHoleRuntimeReturnStage =
  | 'square-hole-result'
  | 'work-detail'
  | 'platform';
type VisualNovelRuntimeReturnStage =
  | 'visual-novel-result'
  | 'visual-novel-gallery-detail'
  | 'work-detail'
  | 'platform';
type BabyObjectMatchRuntimeReturnStage =
  | 'baby-object-match-result'
  | 'work-detail'
  | 'platform';
type JumpHopRuntimeReturnStage = 'jump-hop-result' | 'work-detail' | 'platform';
type PuzzleClearRuntimeReturnStage =
  | 'puzzle-clear-result'
  | 'work-detail'
  | 'platform';
type WoodenFishRuntimeReturnStage =
  | 'wooden-fish-result'
  | 'work-detail'
  | 'platform';
type VisualNovelEntryGenerationPhase = 'generating' | 'ready' | 'failed';
type BabyObjectMatchGenerationPhase = 'generating' | 'ready' | 'failed';

type RecommendRuntimeState = {
  activeKind: RecommendRuntimeKind | null;
  barkBattlePublishedConfig: BarkBattlePublishedConfig | null;
  babyObjectMatchDraft: BabyObjectMatchDraft | null;
  bigFishRun: BigFishRuntimeSnapshotResponse | null;
  jumpHopRun: JumpHopRunResponse['run'] | null;
  match3dRun: Match3DRunSnapshot | null;
  puzzleRun: PuzzleRunSnapshot | null;
  puzzleClearRun: PuzzleClearRunResponse['run'] | null;
  squareHoleRun: SquareHoleRunSnapshot | null;
  visualNovelRun: VisualNovelRunSnapshot | null;
  woodenFishRun: WoodenFishRunResponse['run'] | null;
};

type PuzzleSaveArchiveState = {
  runtimeKind?: unknown;
  entryProfileId?: unknown;
  currentProfileId?: unknown;
  currentLevelId?: unknown;
};

type DeleteCreationWorkConfirmation = {
  id: string;
  title: string;
  detail: string;
  run: () => void;
};

async function resumePuzzleProfileSaveArchiveRaw(worldKey: string) {
  return requestRpgRuntimeJson<
    ProfileSaveArchiveResumeResponse<PuzzleSaveArchiveState>
  >(
    `/profile/save-archives/${encodeURIComponent(worldKey)}`,
    { method: 'POST' },
    '恢复拼图存档失败',
  );
}

type AgentResultBlockerView = {
  code?: string;
  message: string;
};

const AGENT_RESULT_STRUCTURAL_BLOCKER_CODES = new Set([
  'publish_missing_world_hook',
  'publish_missing_player_premise',
  'publish_missing_core_conflict',
  'publish_missing_main_chapter',
  'publish_missing_first_act',
]);
const RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
  BACKGROUND_AUTH_REQUEST_OPTIONS;
const RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS: JumpHopRuntimeRequestOptions =
  RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
async function buildRecommendRuntimeGuestOptions(): Promise<JumpHopRuntimeRequestOptions> {
  const { token } = await ensureRuntimeGuestToken();
  return {
    ...RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
    runtimeGuestToken: token,
  };
}
function shouldUseRecommendRuntimeGuestAuth(
  authUi: { user?: { id?: string } | null } | null | undefined,
) {
  return !authUi?.user?.id?.trim() && !getStoredAccessToken();
}
async function buildRecommendRuntimeAuthOptions(
  authUi: { user?: { id?: string } | null } | null | undefined,
  embedded?: boolean,
): Promise<JumpHopRuntimeRequestOptions> {
  if (!embedded) {
    return RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
  }

  if (shouldUseRecommendRuntimeGuestAuth(authUi)) {
    return buildRecommendRuntimeGuestOptions();
  }

  return RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
}
const PUZZLE_DRAFT_GENERATION_POINT_COST = 2;
const MATCH3D_DRAFT_GENERATION_POINT_COST = 10;
const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3;

function getPlatformPublicGalleryEntryTime(entry: PlatformPublicGalleryCard) {
  const rawTime = entry.publishedAt ?? entry.updatedAt;
  const timestamp = new Date(rawTime).getTime();
  return Number.isNaN(timestamp) ? 0 : timestamp;
}

function getPlatformPublicGalleryEntryKey(entry: PlatformPublicGalleryCard) {
  return buildPlatformPublicGalleryCardKey(entry);
}

function getPlatformRecommendRuntimeKind(
  entry: PlatformPublicGalleryCard,
): RecommendRuntimeKind {
  if (isCustomWorldGalleryEntry(entry)) {
    return 'rpg';
  }

  if (isBigFishGalleryEntry(entry)) {
    return 'big-fish';
  }

  if (isPuzzleGalleryEntry(entry)) {
    return 'puzzle';
  }

  if (isPuzzleClearGalleryEntry(entry)) {
    return 'puzzle-clear';
  }

  if (isJumpHopGalleryEntry(entry)) {
    return 'jump-hop';
  }

  if (isWoodenFishGalleryEntry(entry)) {
    return 'wooden-fish';
  }

  if (isMatch3DGalleryEntry(entry)) {
    return 'match3d';
  }

  if (isSquareHoleGalleryEntry(entry)) {
    return 'square-hole';
  }

  if (isVisualNovelGalleryEntry(entry)) {
    return 'visual-novel';
  }

  if (isBarkBattleGalleryEntry(entry)) {
    return 'bark-battle';
  }

  if (isEdutainmentGalleryEntry(entry)) {
    return 'edutainment';
  }

  throw new Error('未知公开作品类型，无法启动推荐玩法。');
}

function resolveRecommendEntryShareStage(
  entry: PlatformPublicGalleryCard,
): PublishShareModalPayload['stage'] {
  if (isBigFishGalleryEntry(entry)) {
    return 'big-fish-runtime';
  }

  if (isPuzzleGalleryEntry(entry)) {
    return 'puzzle-gallery-detail';
  }

  return 'work-detail';
}

function resolveUnsupportedPublicWorkActionMessage(
  entry: PlatformPublicGalleryCard,
  actionLabel: string,
) {
  const sourceType = 'sourceType' in entry ? entry.sourceType : 'custom-world';
  return `作品类型 ${sourceType} 暂不支持${actionLabel}。`;
}

function isRecommendRuntimeReadyForEntry(
  entry: PlatformPublicGalleryCard,
  state: RecommendRuntimeState,
) {
  const expectedKind = getPlatformRecommendRuntimeKind(entry);
  if (state.activeKind !== expectedKind) {
    return false;
  }

  if (expectedKind === 'big-fish') {
    return Boolean(state.bigFishRun);
  }
  if (expectedKind === 'jump-hop') {
    return Boolean(state.jumpHopRun);
  }
  if (expectedKind === 'wooden-fish') {
    return Boolean(state.woodenFishRun);
  }
  if (expectedKind === 'match3d') {
    return Boolean(state.match3dRun);
  }
  if (expectedKind === 'puzzle') {
    return (
      state.puzzleRun?.entryProfileId === entry.profileId ||
      state.puzzleRun?.currentLevel?.profileId === entry.profileId
    );
  }
  if (expectedKind === 'puzzle-clear') {
    return Boolean(state.puzzleClearRun);
  }
  if (expectedKind === 'square-hole') {
    return Boolean(state.squareHoleRun);
  }
  if (expectedKind === 'visual-novel') {
    return Boolean(state.visualNovelRun);
  }
  if (expectedKind === 'bark-battle') {
    return Boolean(state.barkBattlePublishedConfig);
  }
  if (expectedKind === 'edutainment') {
    return Boolean(state.babyObjectMatchDraft);
  }
  if (expectedKind === 'rpg') {
    return true;
  }

  throw new Error('未知推荐玩法类型。');
}

function isSamePlatformPublicGalleryEntry(
  left: PlatformPublicGalleryCard,
  right: PlatformPublicGalleryCard,
) {
  return (
    getPlatformPublicGalleryEntryKey(left) ===
    getPlatformPublicGalleryEntryKey(right)
  );
}

function mergePlatformPublicGalleryEntries(
  rpgEntries: CustomWorldGalleryCard[],
  puzzleEntries: PlatformPublicGalleryCard[],
) {
  const entryMap = new Map<string, PlatformPublicGalleryCard>();

  [...rpgEntries, ...puzzleEntries].forEach((entry) => {
    entryMap.set(getPlatformPublicGalleryEntryKey(entry), entry);
  });

  return Array.from(entryMap.values()).sort(
    (left, right) =>
      getPlatformPublicGalleryEntryTime(right) -
      getPlatformPublicGalleryEntryTime(left),
  );
}

function mapRpgGalleryCardToPublicWorkDetail(
  entry: CustomWorldGalleryCard,
): PlatformPublicGalleryCard {
  return {
    ...entry,
    sourceType: 'custom-world',
  };
}

function mapPuzzleWorkToPublicWorkDetail(
  item: PuzzleWorkSummary,
): PlatformPublicGalleryCard {
  return mapPuzzleWorkToPlatformGalleryCard(item);
}

function resolveVisiblePuzzleDetailCoverCount(
  entry: PlatformPublicGalleryCard | null,
  run: PuzzleRunSnapshot | null,
) {
  if (!entry || !isPuzzleGalleryEntry(entry)) {
    return 1;
  }

  if (run?.entryProfileId !== entry.profileId) {
    return 1;
  }

  // 中文注释：封面首图永远公开，后续封面跟随当前玩家本次 run 的通关进度即时解锁。
  return Math.max(1, run.clearedLevelCount + 1);
}

function mapMatch3DWorkToPublicWorkDetail(
  item: Match3DWorkSummary,
): PlatformPublicGalleryCard {
  return mapMatch3DWorkToPlatformGalleryCard(
    normalizeMatch3DWorkForRuntimeUi(item),
  );
}

function mapSquareHoleWorkToPublicWorkDetail(
  item: SquareHoleWorkSummary,
): PlatformPublicGalleryCard {
  return mapSquareHoleWorkToPlatformGalleryCard(item);
}

function mapBigFishWorkToPublicWorkDetail(
  item: BigFishWorkSummary,
): PlatformPublicGalleryCard {
  return mapBigFishWorkToPlatformGalleryCard(item);
}

function mapVisualNovelWorkToPublicWorkDetail(
  item: VisualNovelWorkSummary,
): PlatformPublicGalleryCard {
  return mapVisualNovelWorkToPlatformGalleryCard(item);
}

function mapJumpHopWorkToPublicWorkDetail(
  item: JumpHopGalleryCardResponse | JumpHopWorkProfileResponse,
): PlatformPublicGalleryCard {
  return mapJumpHopWorkToPlatformGalleryCard(item);
}

function mapBarkBattleWorkToPublicWorkDetail(
  item: BarkBattleWorkSummary,
): PlatformPublicGalleryCard {
  return mapBarkBattleWorkToPlatformGalleryCard(item);
}

function mapBarkBattleWorkToPublishedConfig(
  work: BarkBattleWorkSummary,
): BarkBattlePublishedConfig {
  return {
    workId: work.workId,
    draftId: work.draftId ?? null,
    configVersion: 1,
    rulesetVersion: 'bark-battle-ruleset-v1',
    playTypeId: 'bark-battle',
    title: work.title,
    description: work.summary,
    themeDescription: work.themeDescription,
    playerImageDescription: work.playerImageDescription,
    opponentImageDescription: work.opponentImageDescription,
    onomatopoeia: work.onomatopoeia,
    playerCharacterImageSrc: work.playerCharacterImageSrc ?? undefined,
    opponentCharacterImageSrc: work.opponentCharacterImageSrc ?? undefined,
    uiBackgroundImageSrc: work.uiBackgroundImageSrc ?? undefined,
    difficultyPreset: work.difficultyPreset,
    updatedAt: work.updatedAt,
    publishedAt: work.publishedAt ?? work.updatedAt,
  };
}

function mapBarkBattlePublicDetailToWorkSummary(
  entry: PlatformPublicGalleryCard,
): BarkBattleWorkSummary | null {
  if (!isBarkBattleGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    draftId: entry.sourceSessionId ?? null,
    ownerUserId: entry.ownerUserId,
    authorDisplayName: entry.authorDisplayName,
    title: entry.worldName,
    summary: entry.summaryText,
    themeDescription: entry.themeTags[0] ?? entry.summaryText,
    playerImageDescription: entry.themeTags[1] ?? entry.summaryText,
    opponentImageDescription: entry.themeTags[2] ?? entry.summaryText,
    onomatopoeia: undefined,
    playerCharacterImageSrc: entry.coverCharacterImageSrcs[0] ?? null,
    opponentCharacterImageSrc: entry.coverCharacterImageSrcs[1] ?? null,
    uiBackgroundImageSrc: entry.coverImageSrc,
    difficultyPreset: 'normal',
    status: 'published',
    generationStatus: 'ready',
    publishReady: true,
    playCount: entry.playCount ?? 0,
    recentPlayCount7d: entry.recentPlayCount7d ?? 0,
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
  };
}

function mapWoodenFishWorkToPublicWorkDetail(
  item: WoodenFishGalleryCardResponse | WoodenFishWorkProfileResponse,
): PlatformPublicGalleryCard {
  return mapWoodenFishWorkToPlatformGalleryCard(item);
}

function mapVisualNovelWorkDetailToSession(
  work: VisualNovelWorkDetail,
): VisualNovelAgentSessionSnapshot {
  return {
    sessionId: work.sourceSessionId?.trim() || work.workId,
    ownerUserId: work.summary.ownerUserId,
    sourceMode: work.draft.sourceMode,
    status: 'ready',
    messages: [],
    draft: work.draft,
    pendingAction: null,
    createdAt: work.createdAt,
    updatedAt: work.summary.updatedAt,
  };
}

function mapPublicWorkDetailToMatch3DWork(
  entry: PlatformPublicGalleryCard,
): Match3DWorkSummary | null {
  if (!isMatch3DGalleryEntry(entry)) {
    return null;
  }

  return promoteMatch3DGeneratedBackgroundAsset({
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.sourceSessionId
        : null,
    gameName: entry.worldName,
    themeText: entry.themeTags[0] ?? '经典消除',
    summary: entry.summaryText,
    tags: entry.themeTags,
    coverImageSrc: entry.coverImageSrc,
    referenceImageSrc: null,
    clearCount: 12,
    difficulty: 4,
    publicationStatus: 'published',
    playCount: entry.playCount ?? 0,
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
    backgroundPrompt: entry.backgroundPrompt ?? null,
    backgroundImageSrc: entry.backgroundImageSrc ?? null,
    backgroundImageObjectKey: entry.backgroundImageObjectKey ?? null,
    generatedBackgroundAsset:
      entry.generatedBackgroundAsset ??
      entry.generatedItemAssets
        ?.map((asset) => asset.backgroundAsset ?? null)
        .find(Boolean) ??
      null,
    generatedItemAssets: normalizeMatch3DGeneratedItemAssetsForRuntime(
      entry.generatedItemAssets ?? [],
    ),
  });
}

function findMatch3DGeneratedBackgroundAsset(
  generatedItemAssets: readonly Match3DGeneratedItemAsset[] | null | undefined,
): Match3DGeneratedBackgroundAsset | null {
  return (
    generatedItemAssets
      ?.map((asset) => asset.backgroundAsset ?? null)
      .find(Boolean) ?? null
  );
}

function promoteMatch3DGeneratedBackgroundAsset<
  T extends Pick<
    Match3DWorkSummary,
    | 'backgroundPrompt'
    | 'backgroundImageSrc'
    | 'backgroundImageObjectKey'
    | 'generatedBackgroundAsset'
    | 'generatedItemAssets'
  >,
>(profile: T): T {
  const backgroundAsset =
    profile.generatedBackgroundAsset ??
    findMatch3DGeneratedBackgroundAsset(profile.generatedItemAssets);
  if (!backgroundAsset) {
    return profile;
  }

  return {
    ...profile,
    backgroundPrompt:
      profile.backgroundPrompt ?? backgroundAsset.prompt ?? null,
    backgroundImageSrc:
      profile.backgroundImageSrc ??
      backgroundAsset.imageSrc ??
      backgroundAsset.imageObjectKey ??
      null,
    backgroundImageObjectKey:
      profile.backgroundImageObjectKey ??
      backgroundAsset.imageObjectKey ??
      backgroundAsset.imageSrc ??
      null,
    generatedBackgroundAsset:
      profile.generatedBackgroundAsset ?? backgroundAsset,
  };
}

function normalizeMatch3DWorkForRuntimeUi<T extends Match3DWorkSummary>(
  profile: T,
): T {
  return promoteMatch3DGeneratedBackgroundAsset({
    ...profile,
    generatedItemAssets: normalizeMatch3DGeneratedItemAssetsForRuntime(
      profile.generatedItemAssets,
    ),
  });
}

function mapMatch3DWorksForRuntimeUi<T extends Match3DWorkSummary>(
  profiles: readonly T[],
): T[] {
  return profiles.map(normalizeMatch3DWorkForRuntimeUi);
}

function buildMatch3DProfileFromSession(
  session: Match3DAgentSessionSnapshot | null,
): Match3DWorkProfile | null {
  const draft = session?.draft;
  if (!session || !draft?.profileId) {
    return null;
  }

  const now = session.updatedAt || new Date().toISOString();
  const generatedItemAssets = normalizeMatch3DGeneratedItemAssetsForRuntime(
    draft.generatedItemAssets,
  );
  return promoteMatch3DGeneratedBackgroundAsset({
    workId: draft.profileId,
    profileId: draft.profileId,
    ownerUserId: 'current-user',
    sourceSessionId: session.sessionId,
    gameName: draft.gameName,
    themeText: draft.themeText,
    summary: draft.summary ?? draft.summaryText ?? '',
    tags: draft.tags,
    coverImageSrc: draft.coverImageSrc ?? draft.referenceImageSrc ?? null,
    referenceImageSrc: draft.referenceImageSrc ?? null,
    clearCount: draft.clearCount,
    difficulty: draft.difficulty,
    publicationStatus: 'draft',
    playCount: 0,
    updatedAt: now,
    publishedAt: null,
    publishReady: Boolean(draft.publishReady),
    backgroundPrompt: draft.backgroundPrompt ?? null,
    backgroundImageSrc: draft.backgroundImageSrc ?? null,
    backgroundImageObjectKey: draft.backgroundImageObjectKey ?? null,
    generatedBackgroundAsset: draft.generatedBackgroundAsset ?? null,
    generatedItemAssets,
  });
}

function hasMatch3DRuntimeAsset(
  assets: readonly Match3DGeneratedItemAsset[] | null | undefined,
) {
  return hasMatch3DGeneratedImageAsset(assets);
}

function hasMatch3DRuntimeBackgroundAsset(
  profile: Pick<
    Match3DWorkSummary,
    | 'backgroundImageSrc'
    | 'backgroundImageObjectKey'
    | 'generatedBackgroundAsset'
    | 'generatedItemAssets'
  >,
) {
  return Boolean(
    profile.backgroundImageSrc?.trim() ||
      profile.backgroundImageObjectKey?.trim() ||
      profile.generatedBackgroundAsset?.imageSrc?.trim() ||
      profile.generatedBackgroundAsset?.imageObjectKey?.trim() ||
      profile.generatedBackgroundAsset?.containerImageSrc?.trim() ||
      profile.generatedBackgroundAsset?.containerImageObjectKey?.trim() ||
      profile.generatedItemAssets?.some(
        (asset) =>
          asset.backgroundAsset?.imageSrc?.trim() ||
          asset.backgroundAsset?.imageObjectKey?.trim() ||
          asset.backgroundAsset?.containerImageSrc?.trim() ||
          asset.backgroundAsset?.containerImageObjectKey?.trim(),
      ),
  );
}

function resolveMatch3DRuntimeGeneratedItemAssets(
  run: Match3DRunSnapshot | null,
  profile: Match3DWorkProfile | null,
  publicWorkDetail: PlatformPublicGalleryCard | null,
) {
  const runProfileId = run?.profileId?.trim() ?? '';
  const profileAssets = profile?.generatedItemAssets ?? [];
  const publicDetailAssets =
    publicWorkDetail && isMatch3DGalleryEntry(publicWorkDetail)
      ? (publicWorkDetail.generatedItemAssets ?? [])
      : [];

  if (runProfileId && profile?.profileId === runProfileId) {
    if (hasMatch3DRuntimeAsset(profileAssets)) {
      return normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
    }

    if (
      publicWorkDetail &&
      isMatch3DGalleryEntry(publicWorkDetail) &&
      publicWorkDetail.profileId === runProfileId
    ) {
      return hasMatch3DRuntimeAsset(publicDetailAssets)
        ? mergeMatch3DGeneratedItemAssetsForRuntime(
            publicDetailAssets,
            profileAssets,
          )
        : normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
    }

    return normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
  }

  if (
    runProfileId &&
    publicWorkDetail &&
    isMatch3DGalleryEntry(publicWorkDetail) &&
    publicWorkDetail.profileId === runProfileId
  ) {
    return normalizeMatch3DGeneratedItemAssetsForRuntime(publicDetailAssets);
  }

  if (hasMatch3DRuntimeAsset(profileAssets)) {
    return normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
  }
  return publicDetailAssets.length > 0
    ? normalizeMatch3DGeneratedItemAssetsForRuntime(publicDetailAssets)
    : normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
}

function resolveMatch3DRuntimeGeneratedBackgroundAsset(
  run: Match3DRunSnapshot | null,
  profile: Match3DWorkProfile | null,
  publicWorkDetail: PlatformPublicGalleryCard | null,
) {
  const runProfileId = run?.profileId?.trim() ?? '';
  const profileBackground = profile
    ? (promoteMatch3DGeneratedBackgroundAsset(profile)
        .generatedBackgroundAsset ?? null)
    : null;
  const publicBackground =
    publicWorkDetail && isMatch3DGalleryEntry(publicWorkDetail)
      ? (promoteMatch3DGeneratedBackgroundAsset(publicWorkDetail)
          .generatedBackgroundAsset ?? null)
      : null;

  if (runProfileId && profile?.profileId === runProfileId) {
    return profileBackground ?? publicBackground;
  }
  if (
    runProfileId &&
    publicWorkDetail &&
    isMatch3DGalleryEntry(publicWorkDetail) &&
    publicWorkDetail.profileId === runProfileId
  ) {
    return publicBackground ?? profileBackground;
  }
  return profileBackground ?? publicBackground;
}

function resolveActiveMatch3DRuntimeProfile(
  run: Match3DRunSnapshot | null,
  runtimeProfile: Match3DWorkProfile | null,
  profile: Match3DWorkProfile | null,
) {
  const runProfileId = run?.profileId?.trim() ?? '';
  if (runProfileId && runtimeProfile?.profileId === runProfileId) {
    return runtimeProfile;
  }
  if (runProfileId && profile?.profileId === runProfileId) {
    return profile;
  }
  return runtimeProfile ?? profile;
}

function resolveMatch3DRuntimeBackgroundImageSrc(
  run: Match3DRunSnapshot | null,
  profile: Match3DWorkProfile | null,
  publicWorkDetail: PlatformPublicGalleryCard | null,
) {
  const runProfileId = run?.profileId?.trim() ?? '';
  const resolvedProfile = profile
    ? promoteMatch3DGeneratedBackgroundAsset(profile)
    : null;
  const resolvedPublicWork =
    publicWorkDetail && isMatch3DGalleryEntry(publicWorkDetail)
      ? promoteMatch3DGeneratedBackgroundAsset(publicWorkDetail)
      : null;
  const profileBackground =
    resolvedProfile?.backgroundImageSrc?.trim() ||
    resolvedProfile?.generatedBackgroundAsset?.imageSrc?.trim() ||
    resolvedProfile?.backgroundImageObjectKey?.trim() ||
    resolvedProfile?.generatedBackgroundAsset?.imageObjectKey?.trim() ||
    '';
  const publicBackground =
    resolvedPublicWork?.backgroundImageSrc?.trim() ||
    resolvedPublicWork?.generatedBackgroundAsset?.imageSrc?.trim() ||
    resolvedPublicWork?.backgroundImageObjectKey?.trim() ||
    resolvedPublicWork?.generatedBackgroundAsset?.imageObjectKey?.trim() ||
    '';

  if (runProfileId && profile?.profileId === runProfileId) {
    return profileBackground || publicBackground || null;
  }
  if (
    runProfileId &&
    publicWorkDetail &&
    isMatch3DGalleryEntry(publicWorkDetail) &&
    publicWorkDetail.profileId === runProfileId
  ) {
    return publicBackground || profileBackground || null;
  }
  return profileBackground || publicBackground || null;
}

function resolveMatch3DGenerationStateFromAssets(
  current: MiniGameDraftGenerationState | null,
  assets: readonly Match3DGeneratedItemAsset[] | null | undefined,
): MiniGameDraftGenerationState | null {
  if (!current || current.phase === 'ready' || current.phase === 'failed') {
    return current;
  }

  const assetList = assets ?? [];
  const imageReadyCount = assetList.filter(
    (asset) =>
      asset.imageViews?.some(
        (view) => view.imageObjectKey?.trim() || view.imageSrc?.trim(),
      ) ||
      asset.imageObjectKey?.trim() ||
      asset.imageSrc?.trim(),
  ).length;
  const totalAssetCount = Math.max(5, assetList.length);
  const failedAsset = assetList.find((asset) => asset.error?.trim());

  return {
    ...current,
    phase: imageReadyCount > 0 ? 'match3d-generate-views' : current.phase,
    completedAssetCount: imageReadyCount,
    totalAssetCount,
    error: failedAsset?.error?.trim() || current.error,
  };
}

function mapPublicWorkDetailToPuzzleWork(
  entry: PlatformPublicGalleryCard,
): PuzzleWorkSummary | null {
  if (!isPuzzleGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.sourceSessionId
        : null,
    authorDisplayName: entry.authorDisplayName,
    levelName: entry.worldName,
    summary: entry.summaryText,
    themeTags: entry.themeTags,
    coverImageSrc: entry.coverImageSrc,
    publicationStatus: 'published',
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    playCount: entry.playCount ?? 0,
    remixCount: entry.remixCount ?? 0,
    likeCount: entry.likeCount ?? 0,
    pointIncentiveTotalHalfPoints: 0,
    pointIncentiveClaimedPoints: 0,
    pointIncentiveTotalPoints: 0,
    pointIncentiveClaimablePoints: 0,
    publishReady: true,
    levels:
      entry.coverSlides?.map((slide, index) => ({
        levelId: slide.id || `puzzle-level-${index + 1}`,
        levelName: slide.label,
        pictureDescription: entry.summaryText,
        candidates: [],
        selectedCandidateId: null,
        coverImageSrc: slide.imageSrc,
        coverAssetId: null,
        generationStatus: 'ready' as const,
      })) ?? [],
  };
}

function mapPublicWorkDetailToBigFishWork(
  entry: PlatformPublicGalleryCard,
): BigFishWorkSummary | null {
  if (!isBigFishGalleryEntry(entry)) {
    return null;
  }

  const levelCount = Number.parseInt(
    entry.themeTags.find((tag) => /^\d+级$/u.test(tag))?.replace('级', '') ??
      '0',
    10,
  );

  return {
    workId: entry.workId,
    sourceSessionId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    authorDisplayName: entry.authorDisplayName,
    title: entry.worldName,
    subtitle: entry.subtitle,
    summary: entry.summaryText,
    coverImageSrc: entry.coverImageSrc,
    status: 'published',
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
    levelCount: Number.isNaN(levelCount) ? 0 : levelCount,
    levelMainImageReadyCount: 0,
    levelMotionReadyCount: 0,
    backgroundReady: Boolean(entry.coverImageSrc),
    playCount: entry.playCount ?? 0,
    remixCount: entry.remixCount ?? 0,
    likeCount: entry.likeCount ?? 0,
  };
}

function mapPublicWorkDetailToSquareHoleWork(
  entry: PlatformPublicGalleryCard,
): SquareHoleWorkSummary | null {
  if (!isSquareHoleGalleryEntry(entry)) {
    return null;
  }

  return {
    workId: entry.workId,
    profileId: entry.profileId,
    ownerUserId: entry.ownerUserId,
    sourceSessionId:
      'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
        ? entry.sourceSessionId
        : null,
    gameName: entry.worldName,
    themeText: entry.themeTags[0] ?? '方洞挑战',
    twistRule: entry.subtitle,
    summary: entry.summaryText,
    tags: entry.themeTags,
    coverImageSrc: entry.coverImageSrc,
    backgroundPrompt: entry.backgroundPrompt ?? '方洞挑战运行背景',
    backgroundImageSrc: entry.backgroundImageSrc ?? null,
    shapeOptions: entry.shapeOptions ?? [],
    holeOptions: entry.holeOptions ?? [],
    shapeCount: entry.shapeCount ?? 8,
    difficulty: entry.difficulty ?? 4,
    publicationStatus: 'published',
    playCount: entry.playCount ?? 0,
    updatedAt: entry.updatedAt,
    publishedAt: entry.publishedAt,
    publishReady: true,
  };
}

function buildSquareHoleProfileFromSession(
  session: SquareHoleSessionSnapshot | null,
): SquareHoleWorkProfile | null {
  const draft = session?.draft;
  if (!session || !draft?.profileId) {
    return null;
  }

  const now = session.updatedAt || new Date().toISOString();
  return {
    workId: draft.profileId,
    profileId: draft.profileId,
    ownerUserId: 'current-user',
    sourceSessionId: session.sessionId,
    gameName: draft.gameName,
    themeText: draft.themeText,
    twistRule: draft.twistRule,
    summary: draft.summary,
    tags: draft.tags,
    coverImageSrc: draft.coverImageSrc ?? null,
    backgroundPrompt: draft.backgroundPrompt,
    backgroundImageSrc: draft.backgroundImageSrc ?? null,
    shapeOptions: draft.shapeOptions,
    holeOptions: draft.holeOptions,
    shapeCount: draft.shapeCount,
    difficulty: draft.difficulty,
    publicationStatus: 'draft',
    playCount: 0,
    updatedAt: now,
    publishedAt: null,
    publishReady: Boolean(draft.publishReady),
  };
}

function mergePuzzleWorkSummary(
  current: PuzzleWorkSummary,
  updated: PuzzleWorkSummary,
): PuzzleWorkSummary {
  return current.profileId === updated.profileId ? updated : current;
}

const PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY =
  'genarrative.puzzle-onboarding.first-visit.v1';
const PUZZLE_ONBOARDING_COPY = '待定待定待定';
const PUZZLE_ONBOARDING_CLEAR_COPY = '只差一步，就可以永久保留你的梦';
const PUZZLE_ONBOARDING_GENERATED_DELAY_MS = 700;

function isPuzzleOnboardingEnabled(): boolean {
  // 中文注释：当前产品要求隐藏首访新手引导，旧流程暂时保留用于后续显式恢复。
  return false;
}

function escapePuzzleOnboardingSvgText(value: string) {
  return value
    .replace(/&/gu, '&amp;')
    .replace(/</gu, '&lt;')
    .replace(/>/gu, '&gt;')
    .replace(/"/gu, '&quot;');
}

function buildPuzzleOnboardingFallbackImage(promptText: string) {
  const trimmedPrompt = promptText.trim();
  const displayPrompt = escapePuzzleOnboardingSvgText(
    trimmedPrompt.slice(0, 12) || '陶泥儿拼图',
  );
  return (
    'data:image/svg+xml;utf8,' +
    encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  <defs>
    <linearGradient id="sky" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0" stop-color="#fde68a" />
      <stop offset="0.48" stop-color="#38bdf8" />
      <stop offset="1" stop-color="#312e81" />
    </linearGradient>
    <radialGradient id="glow" cx="34%" cy="30%" r="42%">
      <stop offset="0" stop-color="#fff7ed" stop-opacity="0.86" />
      <stop offset="1" stop-color="#fff7ed" stop-opacity="0" />
    </radialGradient>
  </defs>
  <rect width="1024" height="1024" fill="url(#sky)" />
  <circle cx="352" cy="292" r="248" fill="url(#glow)" />
  <path d="M0 654 C156 570 296 680 452 610 C626 532 782 598 1024 500 V1024 H0 Z" fill="#0f172a" opacity="0.88" />
  <path d="M0 802 C196 710 332 838 540 750 C704 682 846 724 1024 670 V1024 H0 Z" fill="#111827" opacity="0.72" />
  <path d="M160 344 C242 268 340 282 430 350 C532 428 646 388 752 322 C844 266 918 302 958 362" fill="none" stroke="#fff7ed" stroke-width="18" stroke-linecap="round" opacity="0.72" />
  <path d="M188 538 H836" stroke="#ffffff" stroke-width="16" stroke-linecap="round" opacity="0.26" />
  <path d="M260 620 H764" stroke="#ffffff" stroke-width="12" stroke-linecap="round" opacity="0.2" />
  <text x="512" y="868" text-anchor="middle" font-family="sans-serif" font-size="54" font-weight="800" fill="#ffffff" opacity="0.84">${displayPrompt}</text>
</svg>`)
  );
}

function buildPuzzleOnboardingFallbackWork(
  promptText: string,
): PuzzleWorkSummary {
  const now = new Date().toISOString();
  const seed = Date.now();
  const coverImageSrc = buildPuzzleOnboardingFallbackImage(promptText);
  const level: PuzzleDraftLevel = {
    levelId: 'onboarding-local-level-1',
    levelName: '梦境拼图',
    pictureDescription: promptText,
    pictureReference: null,
    candidates: [
      {
        candidateId: 'onboarding-local-candidate-1',
        imageSrc: coverImageSrc,
        assetId: 'onboarding-local-asset-1',
        prompt: promptText,
        actualPrompt: promptText,
        sourceType: 'generated',
        selected: true,
      },
    ],
    selectedCandidateId: 'onboarding-local-candidate-1',
    coverImageSrc,
    coverAssetId: 'onboarding-local-asset-1',
    generationStatus: 'ready',
  };

  return {
    workId: `onboarding-local-work-${seed}`,
    profileId: `onboarding-local-profile-${seed}`,
    ownerUserId: 'onboarding-guest',
    sourceSessionId: null,
    authorDisplayName: '陶泥儿主',
    workTitle: '梦境拼图',
    workDescription: promptText,
    levelName: level.levelName,
    summary: promptText,
    themeTags: ['新手引导', '拼图'],
    coverImageSrc,
    coverAssetId: level.coverAssetId,
    publicationStatus: 'draft',
    updatedAt: now,
    publishedAt: null,
    playCount: 0,
    remixCount: 0,
    likeCount: 0,
    publishReady: true,
    levels: [level],
  };
}

function shouldUseLocalPuzzleOnboardingFallback(error: unknown) {
  return (
    error instanceof ApiClientError &&
    error.status === 404 &&
    (error.code === 'NOT_FOUND' || error.message.includes('资源不存在'))
  );
}

function isMissingPuzzleWorkError(error: unknown) {
  return (
    (error instanceof ApiClientError &&
      error.status === 404 &&
      (error.code === 'NOT_FOUND' ||
        error.message.includes('资源不存在') ||
        error.message.includes('未找到'))) ||
    (error instanceof Error &&
      (error.message.includes('资源不存在') ||
        error.message.includes('未找到拼图作品')))
  );
}

function maybeAlertWorkNotFoundAndReturnHome() {
  if (typeof window === 'undefined') {
    return false;
  }

  const recoveryAction = resolveWorkNotFoundRecoveryAction(
    window.location.pathname,
  );
  if (!recoveryAction) {
    return false;
  }

  // 中文注释：直接打开公开详情或运行态深链失效时，确认提示后必须离开空详情页。
  window.alert('作品不存在或已下架，将返回首页。');
  pushAppHistoryPath(recoveryAction.nextPath);
  return true;
}

function hasSeenPuzzleOnboarding() {
  if (!isPuzzleOnboardingEnabled()) {
    return true;
  }

  if (typeof window === 'undefined') {
    return true;
  }

  try {
    return (
      window.localStorage.getItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY) ===
      '1'
    );
  } catch {
    return false;
  }
}

function markPuzzleOnboardingSeen() {
  if (typeof window === 'undefined') {
    return;
  }

  try {
    window.localStorage.setItem(PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY, '1');
  } catch {
    // 中文注释：localStorage 不可写时只降级为本次会话展示，不影响主流程。
  }
}

function mergeBigFishWorkSummary(
  current: BigFishWorkSummary,
  updated: BigFishWorkSummary,
): BigFishWorkSummary {
  return current.sourceSessionId === updated.sourceSessionId
    ? updated
    : current;
}

function hasBarkBattleDraftRequiredImages(draft: BarkBattleDraftConfig) {
  return Boolean(
    draft.playerCharacterImageSrc?.trim() &&
      draft.opponentCharacterImageSrc?.trim() &&
      draft.uiBackgroundImageSrc?.trim(),
  );
}

function resolveBarkBattleDraftGenerationStatus(
  draft: BarkBattleDraftConfig,
  partialFailed: boolean,
): BarkBattleGenerationStatus {
  if (hasBarkBattleDraftRequiredImages(draft)) {
    return 'ready';
  }
  return partialFailed ? 'partial_failed' : 'pending_assets';
}

async function resolvePublicWorkAuthorSummary(
  entry: PlatformPublicGalleryCard,
): Promise<PublicUserSummary | null> {
  if ('authorPublicUserCode' in entry && entry.authorPublicUserCode?.trim()) {
    try {
      return await getPublicAuthUserByCode(entry.authorPublicUserCode);
    } catch {
      if (!entry.ownerUserId.trim()) {
        return null;
      }
    }
  }

  if (entry.ownerUserId.trim()) {
    return getPublicAuthUserById(entry.ownerUserId);
  }

  return null;
}

function readProfileTextField(
  profile: CustomWorldProfile | null,
  paths: string[],
) {
  for (const path of paths) {
    let current: unknown = profile;
    for (const segment of path.split('.')) {
      if (!current || typeof current !== 'object') {
        current = null;
        break;
      }
      current = (current as Record<string, unknown>)[segment];
    }
    if (typeof current === 'string' && current.trim()) {
      return current.trim();
    }
  }
  return null;
}

function hasProfileTextArray(profile: CustomWorldProfile | null, key: string) {
  const value = profile
    ? (profile as unknown as Record<string, unknown>)[key]
    : null;
  return Array.isArray(value)
    ? value.some((entry) => typeof entry === 'string' && entry.trim())
    : false;
}

function hasProfileArray(profile: CustomWorldProfile | null, key: string) {
  const value = profile
    ? (profile as unknown as Record<string, unknown>)[key]
    : null;
  return Array.isArray(value) && value.length > 0;
}

function hasSceneAct(profile: CustomWorldProfile | null) {
  const rawProfile = profile as unknown as Record<string, unknown> | null;
  const chapters =
    rawProfile &&
    (Array.isArray(rawProfile.sceneChapterBlueprints)
      ? rawProfile.sceneChapterBlueprints
      : Array.isArray(rawProfile.sceneChapters)
        ? rawProfile.sceneChapters
        : []);
  return Array.isArray(chapters)
    ? chapters.some((chapter) => {
        const acts =
          chapter && typeof chapter === 'object'
            ? (chapter as Record<string, unknown>).acts
            : null;
        return Array.isArray(acts) && acts.length > 0;
      })
    : false;
}

function isAgentResultStructuralBlockerResolved(
  profile: CustomWorldProfile,
  code: string | undefined,
) {
  if (!code || !AGENT_RESULT_STRUCTURAL_BLOCKER_CODES.has(code)) {
    return false;
  }

  if (code === 'publish_missing_world_hook') {
    return Boolean(
      readProfileTextField(profile, [
        'worldHook',
        'creatorIntent.worldHook',
        'anchorContent.worldPromise',
        'anchorContent.worldPromise.hook',
        'settingText',
      ]),
    );
  }
  if (code === 'publish_missing_player_premise') {
    return Boolean(
      readProfileTextField(profile, [
        'playerPremise',
        'creatorIntent.playerPremise',
        'anchorContent.playerEntryPoint',
        'anchorContent.playerEntryPoint.openingIdentity',
        'anchorContent.playerEntryPoint.openingProblem',
        'anchorContent.playerEntryPoint.entryMotivation',
      ]),
    );
  }
  if (code === 'publish_missing_core_conflict') {
    return hasProfileTextArray(profile, 'coreConflicts');
  }
  if (code === 'publish_missing_main_chapter') {
    return (
      hasProfileArray(profile, 'chapters') ||
      hasProfileArray(profile, 'sceneChapterBlueprints') ||
      hasProfileArray(profile, 'sceneChapters')
    );
  }
  return hasSceneAct(profile);
}

function buildAgentResultPublishGateView(
  profile: CustomWorldProfile | null,
  fallbackBlockers: AgentResultBlockerView[],
  fallbackPublishReady: boolean,
): AgentResultPublishGateView {
  if (!profile) {
    return {
      blockers: fallbackBlockers.map((entry) => entry.message),
      publishReady: fallbackPublishReady,
    };
  }

  const blockers = fallbackBlockers
    .filter(
      (entry) => !isAgentResultStructuralBlockerResolved(profile, entry.code),
    )
    .map((entry) => entry.message);

  return {
    blockers,
    publishReady: blockers.length === 0,
  };
}

function buildPuzzleResultProfileId(sessionId: string | null | undefined) {
  const normalizedSessionId = sessionId?.trim();
  if (!normalizedSessionId) {
    return null;
  }
  const stableSuffix = normalizedSessionId.startsWith('puzzle-session-')
    ? normalizedSessionId.slice('puzzle-session-'.length)
    : normalizedSessionId;
  return `puzzle-profile-${stableSuffix}`;
}

function buildPuzzleResultWorkId(sessionId: string | null | undefined) {
  const normalizedSessionId = sessionId?.trim();
  if (!normalizedSessionId) {
    return null;
  }
  const stableSuffix = normalizedSessionId.startsWith('puzzle-session-')
    ? normalizedSessionId.slice('puzzle-session-'.length)
    : normalizedSessionId;
  return `puzzle-work-${stableSuffix}`;
}

function buildPuzzleSessionIdFromProfileId(
  profileId: string | null | undefined,
) {
  const normalizedProfileId = profileId?.trim();
  if (!normalizedProfileId?.startsWith('puzzle-profile-')) {
    return null;
  }

  const stableSuffix = normalizedProfileId.slice('puzzle-profile-'.length);
  return stableSuffix ? `puzzle-session-${stableSuffix}` : null;
}

function normalizeCreationUrlValue(value: string | null | undefined) {
  return value?.trim() || null;
}

function hasCreationUrlStateValue(state: CreationUrlState) {
  return Boolean(
    normalizeCreationUrlValue(state.sessionId) ||
      normalizeCreationUrlValue(state.profileId) ||
      normalizeCreationUrlValue(state.draftId) ||
      normalizeCreationUrlValue(state.workId),
  );
}

function hasPuzzleRuntimeUrlStateValue(state: PuzzleRuntimeUrlState) {
  return Boolean(
    normalizeCreationUrlValue(state.runtimeSessionId) ||
      normalizeCreationUrlValue(state.runtimeProfileId) ||
      normalizeCreationUrlValue(state.runtimeLevelId) ||
      normalizeCreationUrlValue(state.publicWorkCode) ||
      normalizeCreationUrlValue(state.mode),
  );
}

function buildPuzzleRuntimeUrlStateKey(state: PuzzleRuntimeUrlState) {
  return [
    normalizeCreationUrlValue(state.mode),
    normalizeCreationUrlValue(state.runtimeSessionId),
    normalizeCreationUrlValue(state.runtimeProfileId),
    normalizeCreationUrlValue(state.runtimeLevelId),
    normalizeCreationUrlValue(state.publicWorkCode),
  ].join('|');
}

function buildBigFishCreationUrlState(
  session: BigFishSessionSnapshotResponse | null,
): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(session?.sessionId);
  return {
    sessionId,
    workId: sessionId ? `big-fish-work-${sessionId}` : null,
  };
}

function buildMatch3DCreationUrlState(
  session: Match3DAgentSessionSnapshot | null,
): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    session?.draft?.profileId ?? session?.publishedProfileId,
  );
  return {
    sessionId,
    profileId,
    workId: profileId,
  };
}

function buildSquareHoleCreationUrlState(
  session: SquareHoleSessionSnapshot | null,
): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    session?.draft?.profileId ?? session?.publishedProfileId,
  );
  return {
    sessionId,
    profileId,
    workId: profileId,
  };
}

function buildPuzzleCreationUrlState(
  session: PuzzleAgentSessionSnapshot | null,
): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    session?.publishedProfileId ?? buildPuzzleResultProfileId(sessionId),
  );
  return {
    sessionId,
    profileId,
    workId: sessionId ? buildPuzzleResultWorkId(sessionId) : null,
  };
}

function pushPuzzleResultHistoryEntry(
  session: PuzzleAgentSessionSnapshot | null,
) {
  pushAppHistoryPath('/creation/puzzle/result');
  writeCreationUrlState(buildPuzzleCreationUrlState(session));
}

function buildPuzzleDraftRuntimeUrlState(
  item: PuzzleWorkSummary,
  levelId?: string | null,
): PuzzleRuntimeUrlState {
  const runtimeSessionId =
    normalizeCreationUrlValue(item.sourceSessionId) ??
    buildPuzzleSessionIdFromProfileId(item.profileId);

  return {
    mode: 'draft',
    runtimeSessionId,
    runtimeProfileId: normalizeCreationUrlValue(item.profileId),
    runtimeLevelId: normalizeCreationUrlValue(levelId),
  };
}

function buildPuzzlePublishedRuntimeUrlState(
  item: PuzzleWorkSummary,
  levelId?: string | null,
): PuzzleRuntimeUrlState {
  return {
    mode: 'published',
    runtimeProfileId: normalizeCreationUrlValue(item.profileId),
    runtimeLevelId: normalizeCreationUrlValue(levelId),
    publicWorkCode: buildPuzzlePublicWorkCode(item.profileId),
  };
}

function openPuzzleRuntimeStage(
  setSelectionStage: (stage: SelectionStage) => void,
  state: PuzzleRuntimeUrlState,
) {
  pushAppHistoryPath('/runtime/puzzle');
  setSelectionStage('puzzle-runtime');
  writePuzzleRuntimeUrlState(state);
}

function buildPuzzleRuntimeWorkFromSession(
  session: PuzzleAgentSessionSnapshot,
  owner: { userId?: string | null; displayName?: string | null },
): PuzzleWorkSummary | null {
  const draft = session.draft;
  const profileId =
    session.publishedProfileId ?? buildPuzzleResultProfileId(session.sessionId);
  if (!draft || !profileId || !draft.coverImageSrc?.trim()) {
    return null;
  }

  return {
    workId: buildPuzzleResultWorkId(session.sessionId) ?? profileId,
    profileId,
    ownerUserId: owner.userId ?? 'current-user',
    sourceSessionId: session.sessionId,
    authorDisplayName: owner.displayName ?? '玩家',
    workTitle: draft.workTitle,
    workDescription: draft.workDescription,
    levelName: draft.levelName,
    summary: draft.summary,
    themeTags: draft.themeTags,
    coverImageSrc: draft.coverImageSrc,
    coverAssetId: draft.coverAssetId,
    publicationStatus: 'draft',
    updatedAt: session.updatedAt,
    publishedAt: null,
    playCount: 0,
    remixCount: 0,
    likeCount: 0,
    publishReady: Boolean(session.resultPreview?.publishReady),
    levels: draft.levels,
  };
}

function buildVisualNovelCreationUrlState(
  session: VisualNovelAgentSessionSnapshot | null,
): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(session?.sessionId);
  const profileId = normalizeCreationUrlValue(session?.draft?.profileId);
  return {
    sessionId,
    profileId,
    workId: profileId ?? sessionId,
  };
}

function buildJumpHopCreationUrlState(params: {
  session?: JumpHopSessionSnapshotResponse | null;
  work?: JumpHopWorkProfileResponse | null;
}): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(params.session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    params.work?.summary.profileId ?? params.session?.draft?.profileId,
  );
  return {
    sessionId,
    profileId,
    workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
  };
}

function buildPuzzleClearCreationUrlState(params: {
  session?: PuzzleClearSessionSnapshotResponse | null;
  work?: PuzzleClearWorkProfileResponse | null;
}): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(params.session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    params.work?.summary.profileId ?? params.session?.draft?.profileId,
  );
  return {
    sessionId,
    profileId,
    workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
  };
}

function buildPuzzleClearSessionFromWorkDetail(
  work: PuzzleClearWorkProfileResponse,
  fallbackItem?: PuzzleClearWorkSummaryResponse | null,
): PuzzleClearSessionSnapshotResponse {
  const sessionId =
    normalizeCreationUrlValue(work.summary.sourceSessionId) ??
    normalizeCreationUrlValue(fallbackItem?.sourceSessionId) ??
    work.summary.profileId;
  return {
    sessionId,
    ownerUserId: work.summary.ownerUserId,
    status: work.summary.generationStatus,
    draft: work.draft,
    createdAt: work.summary.updatedAt,
    updatedAt: work.summary.updatedAt,
  };
}

function buildPuzzleClearPendingSession(
  item: PuzzleClearWorkSummaryResponse,
): PuzzleClearSessionSnapshotResponse {
  const sessionId =
    normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
  return {
    sessionId,
    ownerUserId: item.ownerUserId,
    status: item.generationStatus,
    draft: {
      templateId: 'puzzle-clear',
      templateName: '拼消消',
      profileId: item.profileId,
      workTitle: item.workTitle,
      workDescription: item.workDescription,
      themePrompt: item.themePrompt,
      boardBackgroundPrompt: item.themePrompt,
      generateBoardBackground: true,
      boardBackgroundAsset: null,
      cardBackImageSrc: null,
      atlasAsset: null,
      patternGroups: [],
      cardAssets: [],
      generationStatus: item.generationStatus,
    },
    createdAt: item.updatedAt,
    updatedAt: item.updatedAt,
  };
}

function buildJumpHopPendingSession(
  item: JumpHopWorkSummaryResponse,
): JumpHopSessionSnapshotResponse {
  const sessionId =
    normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
  return {
    sessionId,
    ownerUserId: item.ownerUserId,
    status: item.generationStatus,
    draft: {
      templateId: 'jump-hop',
      templateName: '跳一跳',
      profileId: item.profileId,
      themeText: item.themeText || item.workTitle,
      workTitle: item.workTitle,
      workDescription: item.workDescription,
      themeTags: item.themeTags,
      difficulty: item.difficulty,
      stylePreset: item.stylePreset,
      characterPrompt: '',
      tilePrompt: '',
      endMoodPrompt: null,
      characterAsset: null,
      tileAtlasAsset: null,
      tileAssets: [],
      path: null,
      coverComposite: item.coverImageSrc,
      generationStatus: item.generationStatus,
    },
    createdAt: item.updatedAt,
    updatedAt: item.updatedAt,
  };
}

function buildWoodenFishCreationUrlState(params: {
  session?: WoodenFishSessionSnapshotResponse | null;
  work?: WoodenFishWorkProfileResponse | null;
}): CreationUrlState {
  const sessionId = normalizeCreationUrlValue(params.session?.sessionId);
  const profileId = normalizeCreationUrlValue(
    params.work?.summary.profileId ?? params.session?.draft?.profileId,
  );
  const draftId = profileId ?? sessionId;
  return {
    sessionId,
    profileId,
    draftId,
    workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
  };
}

function buildWoodenFishSessionFromWorkDetail(
  work: WoodenFishWorkProfileResponse,
  fallbackItem?: WoodenFishWorkSummaryResponse | null,
): WoodenFishSessionSnapshotResponse {
  const sessionId =
    normalizeCreationUrlValue(work.summary.sourceSessionId) ??
    normalizeCreationUrlValue(fallbackItem?.sourceSessionId) ??
    work.summary.profileId;
  return {
    sessionId,
    ownerUserId: work.summary.ownerUserId,
    status: work.summary.generationStatus,
    draft: work.draft,
    createdAt: work.summary.updatedAt,
    updatedAt: work.summary.updatedAt,
  };
}

function buildWoodenFishPendingSession(
  item: WoodenFishWorkSummaryResponse,
): WoodenFishSessionSnapshotResponse {
  const sessionId =
    normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
  return {
    sessionId,
    ownerUserId: item.ownerUserId,
    status: item.generationStatus,
    draft: {
      templateId: 'wooden-fish',
      templateName: '敲木鱼',
      profileId: item.profileId,
      workTitle: item.workTitle,
      workDescription: item.workDescription,
      themeTags: item.themeTags,
      hitObjectPrompt: '',
      hitObjectReferenceImageSrc: null,
      hitSoundPrompt: null,
      floatingWords: ['功德 +1'],
      hitObjectAsset: null,
      backgroundAsset: null,
      backButtonAsset: null,
      hitSoundAsset: null,
      coverImageSrc: item.coverImageSrc,
      generationStatus: item.generationStatus,
    },
    createdAt: item.updatedAt,
    updatedAt: item.updatedAt,
  };
}

function buildBarkBattleCreationUrlState(
  draft: BarkBattleDraftConfig | null,
): CreationUrlState {
  return {
    draftId: normalizeCreationUrlValue(draft?.draftId),
    workId: normalizeCreationUrlValue(draft?.workId ?? draft?.draftId),
  };
}

function buildBabyObjectMatchCreationUrlState(
  draft: BabyObjectMatchDraft | null,
): CreationUrlState {
  const profileId = normalizeCreationUrlValue(draft?.profileId);
  return {
    profileId,
    draftId: normalizeCreationUrlValue(draft?.draftId),
    workId: profileId,
  };
}

function buildDraftNoticeKey(kind: CreationWorkShelfKind, id: string) {
  return `${kind}:${id}`;
}

function collectDraftNoticeKeys(
  kind: CreationWorkShelfKind,
  ids: Array<string | null | undefined>,
) {
  const keys = new Set<string>();
  for (const id of ids) {
    const normalizedId = id?.trim();
    if (normalizedId) {
      keys.add(buildDraftNoticeKey(kind, normalizedId));
    }
  }
  return Array.from(keys);
}

function normalizeDraftNoticeId(id: string | null | undefined) {
  return id?.trim() || null;
}

function normalizePendingDraftShelfLookupId(
  kind: Exclude<CreationWorkShelfKind, 'rpg'>,
  id: string | null | undefined,
) {
  const normalizedId = normalizeDraftNoticeId(id);
  if (!normalizedId) {
    return null;
  }

  const noticePrefix = `${kind}:`;
  if (!normalizedId.startsWith(noticePrefix)) {
    return normalizedId;
  }

  return normalizeDraftNoticeId(normalizedId.slice(noticePrefix.length));
}

function createPendingDraftShelfState(
  status: DraftGenerationNoticeStatus,
  seen = false,
  updatedAt = new Date().toISOString(),
  metadata?: { title?: string | null; summary?: string | null },
): PendingDraftShelfState {
  const title = metadata?.title?.trim();
  const summary = metadata?.summary?.trim();
  return {
    status,
    seen,
    updatedAt,
    ...(title ? { title } : {}),
    ...(summary ? { summary } : {}),
  };
}

function normalizePlatformErrorMessage(message: string | null | undefined) {
  const normalized = message?.trim();
  return normalized ? normalized : null;
}

/** 首页公开数据降级时，入口关闭错误不弹窗；真实创作动作仍由对应工作台提示。 */
function isCreationEntryDisabledErrorMessage(
  message: string | null | undefined,
) {
  const normalized = normalizePlatformErrorMessage(message);
  return Boolean(
    normalized &&
      (normalized.includes('creation_entry_disabled') ||
        normalized.includes('该玩法入口暂不可用') ||
        normalized.includes('创作入口已关闭')),
  );
}

function formatPlatformErrorSource(label: string, id?: string | null) {
  const normalizedId = id?.trim();
  return normalizedId ? `${label} ${normalizedId}` : label;
}

function formatPlatformTaskCompletionSource(label: string, id?: string | null) {
  const normalizedId = id?.trim();
  return normalizedId ? `${label} ${normalizedId}` : label;
}

function isBackgroundGenerationStillRunningMessage(message: string) {
  return /仍在后台处理|后台仍在处理|仍在生成|后台生成/u.test(message);
}

function buildDraftFailedShelfSummary(kind: CreationWorkShelfKind) {
  switch (kind) {
    case 'puzzle':
      return '拼图草稿生成失败，可重新打开处理。';
    case 'match3d':
      return '玩法素材生成失败，可重新打开处理。';
    case 'big-fish':
      return '草稿生成失败，可重新打开处理。';
    case 'square-hole':
      return '挑战素材生成失败，可重新打开处理。';
    case 'jump-hop':
      return '跳一跳玩法草稿生成失败，可重新打开处理。';
    case 'wooden-fish':
      return '敲木鱼草稿生成失败，可重新打开处理。';
    case 'visual-novel':
      return '视觉小说草稿生成失败，可重新打开处理。';
    case 'bark-battle':
      return '声浪竞技素材生成失败，可重新打开处理。';
    case 'baby-object-match':
      return '宝贝识物草稿生成失败，可重新打开处理。';
    default:
      return '草稿生成失败，可重新打开处理。';
  }
}

function isDraftShelfSummaryPlaceholder(value: string | null | undefined) {
  const normalized = value?.trim();
  if (!normalized) {
    return true;
  }

  return /^(正在生成|.*生成失败，可重新打开处理。$|未填写作品描述$)/u.test(
    normalized,
  );
}

function buildPlatformErrorDialogDismissKey(
  error: (PlatformErrorDialogPayload & { key: string }) | null,
) {
  return error ? `${error.key}:${error.source}:${error.message}` : null;
}

function buildPlatformTaskCompletionDialogDismissKey(
  completion:
    | (PlatformTaskCompletionDialogPayload & {
        key: string;
        completedAtMs: number | null;
      })
    | null,
) {
  return completion
    ? `${completion.key}:${completion.source}:${completion.message}:${completion.completedAtMs ?? 0}`
    : null;
}

function pickDraftCompletionDialogSourceId(
  ids: Array<string | null | undefined>,
) {
  const normalizedIds = ids
    .map((id) => id?.trim() ?? '')
    .filter((id) => Boolean(id));
  return (
    normalizedIds.find((id) => /session/i.test(id)) ??
    normalizedIds.find((id) => /work/i.test(id)) ??
    normalizedIds.find((id) => /draft/i.test(id)) ??
    normalizedIds.find((id) => /run/i.test(id)) ??
    normalizedIds.find((id) => /profile/i.test(id)) ??
    normalizedIds[0] ??
    null
  );
}

function buildDraftCompletionDialogSource(
  kind: CreationWorkShelfKind,
  ids: Array<string | null | undefined>,
): string {
  const sourceId = pickDraftCompletionDialogSourceId(ids);
  switch (kind) {
    case 'rpg':
      return formatPlatformTaskCompletionSource('RPG 草稿', sourceId);
    case 'big-fish':
      return formatPlatformTaskCompletionSource('大鱼吃小鱼草稿', sourceId);
    case 'match3d':
      return formatPlatformTaskCompletionSource('抓大鹅草稿', sourceId);
    case 'square-hole':
      return formatPlatformTaskCompletionSource('方洞挑战草稿', sourceId);
    case 'jump-hop':
      return formatPlatformTaskCompletionSource('跳一跳草稿', sourceId);
    case 'puzzle-clear':
      return formatPlatformTaskCompletionSource('拼消消草稿', sourceId);
    case 'wooden-fish':
      return formatPlatformTaskCompletionSource('敲木鱼草稿', sourceId);
    case 'puzzle':
      return formatPlatformTaskCompletionSource('拼图草稿', sourceId);
    case 'visual-novel':
      return formatPlatformTaskCompletionSource('视觉小说草稿', sourceId);
    case 'bark-battle':
      return formatPlatformTaskCompletionSource('汪汪声浪草稿', sourceId);
    case 'baby-object-match':
      return formatPlatformTaskCompletionSource('宝贝识物草稿', sourceId);
  }
  return formatPlatformTaskCompletionSource('创作草稿', sourceId);
}

/** 为恢复的小游戏草稿重建生成态，保留后端开始时间作为进度事实源。 */
function createMiniGameDraftGenerationStateForRestoredDraft(
  kind: MiniGameDraftGenerationKind,
  metadata?: MiniGameDraftGenerationState['metadata'],
  startedAtMs = Date.now(),
): MiniGameDraftGenerationState {
  return {
    ...createMiniGameDraftGenerationState(kind, startedAtMs),
    ...(metadata ? { metadata } : {}),
  };
}

function createFailedMiniGameDraftGenerationStateForRestoredDraft(
  kind: MiniGameDraftGenerationKind,
  updatedAt: string | null | undefined,
  error: string,
  metadata?: MiniGameDraftGenerationState['metadata'],
): MiniGameDraftGenerationState {
  return resolveFinishedMiniGameDraftGenerationState(
    createMiniGameDraftGenerationStateForRestoredDraft(
      kind,
      metadata,
      resolveMiniGameDraftGenerationStartedAtMs(updatedAt),
    ),
    'failed',
    { error },
  );
}

function buildPuzzleFormPayloadFromWork(
  item: PuzzleWorkSummary,
): CreatePuzzleAgentSessionRequest {
  const pictureDescription =
    item.workDescription?.trim() ||
    item.summary?.trim() ||
    item.levels?.[0]?.pictureDescription?.trim() ||
    item.levelName?.trim() ||
    item.workTitle?.trim() ||
    '';

  return {
    seedText: pictureDescription,
    workTitle: item.workTitle?.trim() || item.levelName?.trim() || undefined,
    workDescription: item.workDescription?.trim() || item.summary?.trim(),
    pictureDescription,
    referenceImageSrc: null,
    referenceImageSrcs: [],
    referenceImageAssetObjectId: null,
    referenceImageAssetObjectIds: [],
    imageModel: null,
    aiRedraw: true,
  };
}

function parseOptionalFiniteNumber(value: string | number | null | undefined) {
  if (typeof value === 'number') {
    return Number.isFinite(value) ? value : undefined;
  }

  const normalizedValue = value?.trim();
  if (!normalizedValue) {
    return undefined;
  }

  const parsedValue = Number(normalizedValue);
  return Number.isFinite(parsedValue) ? parsedValue : undefined;
}

function buildMatch3DFormPayloadFromSession(
  session: Match3DAgentSessionSnapshot,
): CreateMatch3DSessionRequest {
  const themeText =
    session.config?.themeText?.trim() ||
    session.draft?.themeText?.trim() ||
    session.anchorPack.theme.value.trim() ||
    '';

  return {
    seedText: themeText,
    themeText,
    referenceImageSrc:
      session.config?.referenceImageSrc ?? session.draft?.referenceImageSrc ?? null,
    clearCount:
      session.config?.clearCount ??
      session.draft?.clearCount ??
      parseOptionalFiniteNumber(session.anchorPack.clearCount.value) ??
      undefined,
    difficulty:
      session.config?.difficulty ??
      session.draft?.difficulty ??
      parseOptionalFiniteNumber(session.anchorPack.difficulty.value) ??
      undefined,
    assetStyleId: session.config?.assetStyleId ?? null,
    assetStyleLabel: session.config?.assetStyleLabel ?? null,
    assetStylePrompt: session.config?.assetStylePrompt ?? null,
    generateClickSound: session.config?.generateClickSound,
  };
}

function buildMatch3DFormPayloadFromWork(
  item: Match3DWorkSummary,
): CreateMatch3DSessionRequest {
  const themeText = item.themeText?.trim() || item.gameName?.trim() || '';
  return {
    seedText: themeText,
    themeText,
    referenceImageSrc: item.referenceImageSrc ?? null,
    clearCount: item.clearCount,
    difficulty: item.difficulty,
  };
}

/** 清理生成态完成时间，避免返回生成页后继续沿用结束态计时。 */
function rebaseMiniGameDraftGenerationStateForDisplay(
  state: MiniGameDraftGenerationState,
): MiniGameDraftGenerationState {
  return {
    ...state,
    finishedAtMs: undefined,
  };
}

function rebaseMiniGameDraftBackgroundCompileTaskForDisplay<
  T extends PuzzleBackgroundCompileTask | Match3DBackgroundCompileTask,
>(task: T): T {
  return {
    ...task,
    generationState: rebaseMiniGameDraftGenerationStateForDisplay(
      task.generationState,
    ),
  };
}

function createPuzzleDraftGenerationStateFromPayload(
  payload: CreatePuzzleAgentSessionRequest | null | undefined,
  session: PuzzleAgentSessionSnapshot | null | undefined = null,
): MiniGameDraftGenerationState {
  const puzzleProgressPercent =
    session?.draft && !session.draft.formDraft
      ? session.progressPercent
      : undefined;

  return {
    ...createMiniGameDraftGenerationState(
      'puzzle',
      resolveMiniGameDraftGenerationStartedAtMs(session?.updatedAt),
    ),
    metadata: {
      puzzleAiRedraw: payload?.aiRedraw ?? true,
      puzzleActivePhaseId:
        typeof puzzleProgressPercent === 'number' ? 'compile' : undefined,
      puzzleActiveStepStartedAtMs:
        typeof puzzleProgressPercent === 'number' ? Date.now() : undefined,
      puzzleProgressPercent,
    },
  };
}

function resolvePuzzlePhaseFromSessionProgress(
  state: MiniGameDraftGenerationState,
  session: PuzzleAgentSessionSnapshot,
): MiniGameDraftGenerationPhase {
  if (session.progressPercent >= 96) {
    return 'puzzle-select-image';
  }
  if (session.progressPercent >= 94) {
    return 'puzzle-ui-assets';
  }
  if (session.progressPercent >= 88) {
    return state.metadata?.puzzleAiRedraw === false
      ? 'puzzle-level-scene'
      : 'puzzle-cover-image';
  }

  return 'compile';
}

function mergePuzzleSessionProgressIntoGenerationState(
  state: MiniGameDraftGenerationState,
  session: PuzzleAgentSessionSnapshot,
): MiniGameDraftGenerationState {
  const isCompiledGenerationSession = Boolean(
    session.draft && !session.draft.formDraft,
  );

  const nextPhaseId = isCompiledGenerationSession
    ? resolvePuzzlePhaseFromSessionProgress(state, session)
    : state.metadata?.puzzleActivePhaseId;
  const shouldResetActiveStepStart =
    isCompiledGenerationSession &&
    nextPhaseId != null &&
    nextPhaseId !== state.metadata?.puzzleActivePhaseId;

  return {
    ...state,
    metadata: {
      ...state.metadata,
      puzzleActivePhaseId: nextPhaseId,
      puzzleActiveStepStartedAtMs: shouldResetActiveStepStart
        ? resolveMiniGameDraftGenerationStartedAtMs(session.updatedAt)
        : state.metadata?.puzzleActiveStepStartedAtMs,
      puzzleProgressPercent: isCompiledGenerationSession
        ? session.progressPercent
        : state.metadata?.puzzleProgressPercent,
    },
  };
}

function resolveFinishedMiniGameDraftGenerationState(
  state: MiniGameDraftGenerationState,
  phase: 'ready' | 'failed',
  options: {
    error?: string | null;
    completedAssetCount?: number;
    totalAssetCount?: number;
  } = {},
): MiniGameDraftGenerationState {
  return {
    ...state,
    phase,
    finishedAtMs: Date.now(),
    error: options.error ?? state.error,
    completedAssetCount:
      options.completedAssetCount ?? state.completedAssetCount,
    totalAssetCount: options.totalAssetCount ?? state.totalAssetCount,
  };
}

function normalizeRecoveredPuzzleDraftSession(
  session: PuzzleAgentSessionSnapshot,
): PuzzleAgentSessionSnapshot {
  const draft = session.draft;
  if (!draft) {
    return session;
  }

  const primaryLevel = draft.levels?.[0];
  const selectedCandidate =
    primaryLevel?.candidates.find((candidate) => candidate.selected) ??
    primaryLevel?.candidates[0] ??
    draft.candidates.find((candidate) => candidate.selected) ??
    draft.candidates[0] ??
    null;
  const coverImageSrc =
    draft.coverImageSrc?.trim() ||
    primaryLevel?.coverImageSrc?.trim() ||
    selectedCandidate?.imageSrc.trim() ||
    null;
  const coverAssetId =
    draft.coverAssetId?.trim() ||
    primaryLevel?.coverAssetId?.trim() ||
    selectedCandidate?.assetId.trim() ||
    null;
  const selectedCandidateId =
    draft.selectedCandidateId ??
    primaryLevel?.selectedCandidateId ??
    selectedCandidate?.candidateId ??
    null;

  return {
    ...session,
    draft: {
      ...draft,
      coverImageSrc,
      coverAssetId,
      selectedCandidateId,
      generationStatus: 'ready',
      levels: draft.levels?.map((level, index) =>
        index === 0
          ? {
              ...level,
              coverImageSrc: level.coverImageSrc ?? coverImageSrc,
              coverAssetId: level.coverAssetId ?? coverAssetId,
              selectedCandidateId:
                level.selectedCandidateId ?? selectedCandidateId,
              generationStatus: 'ready',
            }
          : level,
      ),
    },
  };
}

function hasRecoverableGeneratedPuzzleDraft(
  session: PuzzleAgentSessionSnapshot,
) {
  const draft = session.draft;
  if (!draft) {
    return false;
  }

  const firstLevel = draft.levels?.[0];
  return Boolean(
    draft.coverImageSrc?.trim() ||
      firstLevel?.coverImageSrc?.trim() ||
      firstLevel?.candidates.some((candidate) => candidate.imageSrc.trim()),
  );
}

function getGenerationNoticeShelfKeys(item: CreationWorkShelfItem): string[] {
  switch (item.source.kind) {
    case 'rpg':
      return collectDraftNoticeKeys('rpg', [
        item.id,
        item.source.item.workId,
        item.source.item.sessionId,
        item.source.item.profileId,
      ]);
    case 'big-fish':
      return collectDraftNoticeKeys('big-fish', [
        item.id,
        item.source.item.workId,
        item.source.item.sourceSessionId,
      ]);
    case 'match3d':
      return collectDraftNoticeKeys('match3d', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
      ]);
    case 'square-hole':
      return collectDraftNoticeKeys('square-hole', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
      ]);
    case 'jump-hop':
      return collectDraftNoticeKeys('jump-hop', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
      ]);
    case 'puzzle-clear':
      return collectDraftNoticeKeys('puzzle-clear', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
      ]);
    case 'puzzle':
      return collectDraftNoticeKeys('puzzle', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
        buildPuzzleResultWorkId(item.source.item.sourceSessionId),
        buildPuzzleResultProfileId(item.source.item.sourceSessionId),
      ]);
    case 'visual-novel':
      return collectDraftNoticeKeys('visual-novel', [
        item.id,
        item.source.item.profileId,
      ]);
    case 'baby-object-match':
      return collectDraftNoticeKeys('baby-object-match', [
        item.id,
        item.source.item.profileId,
        item.source.item.draftId,
      ]);
    case 'bark-battle':
      return collectDraftNoticeKeys('bark-battle', [
        item.id,
        item.source.item.workId,
        item.source.item.draftId,
      ]);
    case 'wooden-fish':
      return collectDraftNoticeKeys('wooden-fish', [
        item.id,
        item.source.item.workId,
        item.source.item.profileId,
        item.source.item.sourceSessionId,
      ]);
    default:
      return [];
  }
}

function isMiniGameDraftReady(state: MiniGameDraftGenerationState | null) {
  return state?.phase === 'ready';
}

function isMiniGameDraftGenerating(state: MiniGameDraftGenerationState | null) {
  return Boolean(state && state.phase !== 'ready' && state.phase !== 'failed');
}

function isPersistedDraftGenerating(value: string | null | undefined) {
  return value?.trim() === 'generating';
}

function isPersistedDraftFailed(value: string | null | undefined) {
  const normalized = value?.trim();
  return normalized === 'failed' || normalized === 'partial_failed';
}

function resolveProfileWalletBalance(
  dashboard: { walletBalance?: number | null } | null | undefined,
) {
  const walletBalance = dashboard?.walletBalance;
  return typeof walletBalance === 'number' && Number.isFinite(walletBalance)
    ? Math.max(0, Math.floor(walletBalance))
    : 0;
}

function adjustProfileDashboardWalletBalance(
  dashboard: ProfileDashboardSummary | null,
  delta: number,
): ProfileDashboardSummary | null {
  if (!dashboard || !Number.isFinite(delta) || delta === 0) {
    return dashboard;
  }

  return {
    ...dashboard,
    walletBalance: Math.max(
      0,
      resolveProfileWalletBalance(dashboard) + Math.trunc(delta),
    ),
    updatedAt: new Date().toISOString(),
  };
}

function reconcileProfileWalletLocalDeltaWithServerDashboard(
  previousDashboard: ProfileDashboardSummary | null,
  latestDashboard: ProfileDashboardSummary | null,
  localDelta: number,
) {
  if (
    !previousDashboard ||
    !latestDashboard ||
    !Number.isFinite(localDelta) ||
    localDelta === 0
  ) {
    return Number.isFinite(localDelta) ? Math.trunc(localDelta) : 0;
  }

  const previousBalance = resolveProfileWalletBalance(previousDashboard);
  const latestBalance = resolveProfileWalletBalance(latestDashboard);
  const normalizedDelta = Math.trunc(localDelta);

  if (normalizedDelta < 0) {
    const reflectedDebit = Math.max(0, previousBalance - latestBalance);
    return Math.min(0, normalizedDelta + reflectedDebit);
  }

  const reflectedCredit = Math.max(0, latestBalance - previousBalance);
  return Math.max(0, normalizedDelta - reflectedCredit);
}

function buildPendingBigFishWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly BigFishWorkSummary[],
): BigFishWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const isFailed = state.status === 'failed';
      return {
        workId: `big-fish-work-${sessionId}`,
        sourceSessionId: sessionId,
        ownerUserId: '',
        authorDisplayName: '',
        title: '大鱼吃小鱼草稿',
        subtitle: isFailed ? '生成失败待重试' : '草稿生成中',
        summary: isFailed
          ? '草稿生成失败，可重新打开处理。'
          : '正在生成玩法草稿。',
        coverImageSrc: null,
        status: 'draft',
        updatedAt: state.updatedAt,
        publishedAt: null,
        publishReady: false,
        levelCount: 0,
        levelMainImageReadyCount: 0,
        levelMotionReadyCount: 0,
        backgroundReady: false,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
      };
    });
}

function buildPendingJumpHopWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly JumpHopWorkSummaryResponse[],
): JumpHopWorkSummaryResponse[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const generationStatus =
        state.status === 'failed'
          ? 'failed'
          : state.status === 'generating'
            ? 'generating'
            : 'ready';
      return {
        runtimeKind: 'jump-hop',
        workId: `jump-hop-work-${sessionId}`,
        profileId: `jump-hop-profile-${sessionId}`,
        ownerUserId: '',
        sourceSessionId: sessionId,
        themeText: '跳一跳',
        workTitle: '跳一跳草稿',
        workDescription:
          state.status === 'failed'
            ? '跳一跳玩法草稿生成失败，可重新打开处理。'
            : '正在生成跳一跳玩法草稿。',
        themeTags: [],
        difficulty: 'standard',
        stylePreset: 'minimal-blocks',
        coverImageSrc: null,
        publicationStatus: 'draft',
        playCount: 0,
        updatedAt: state.updatedAt,
        publishedAt: null,
        publishReady: false,
        generationStatus,
      };
    });
}

function buildPendingWoodenFishWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly WoodenFishWorkSummaryResponse[],
): WoodenFishWorkSummaryResponse[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const generationStatus =
        state.status === 'failed'
          ? 'failed'
          : state.status === 'generating'
            ? 'generating'
            : 'ready';
      return {
        runtimeKind: 'wooden-fish',
        workId: `wooden-fish-work-${sessionId}`,
        profileId: sessionId,
        ownerUserId: '',
        sourceSessionId: sessionId,
        workTitle: '敲木鱼草稿',
        workDescription:
          state.status === 'failed'
            ? '敲木鱼草稿生成失败，可重新打开处理。'
            : '正在生成敲木鱼草稿。',
        themeTags: ['敲木鱼'],
        coverImageSrc: null,
        publicationStatus: 'draft',
        playCount: 0,
        updatedAt: state.updatedAt,
        publishedAt: null,
        publishReady: false,
        generationStatus,
      };
    });
}

function buildPendingPuzzleClearWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly PuzzleClearWorkSummaryResponse[],
): PuzzleClearWorkSummaryResponse[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const generationStatus =
        state.status === 'failed'
          ? 'failed'
          : state.status === 'generating'
            ? 'generating'
            : 'ready';
      return {
        runtimeKind: 'puzzle-clear',
        workId: `puzzle-clear-work-${sessionId}`,
        profileId: sessionId,
        ownerUserId: '',
        sourceSessionId: sessionId,
        workTitle: '拼消消草稿',
        workDescription:
          state.status === 'failed'
            ? '拼消消草稿生成失败，可重新打开处理。'
            : '正在生成拼消消草稿。',
        themePrompt: '',
        coverImageSrc: null,
        publicationStatus: 'draft',
        playCount: 0,
        updatedAt: state.updatedAt,
        publishedAt: null,
        publishReady: false,
        generationStatus,
      };
    });
}

function buildPendingMatch3DWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly Match3DWorkSummary[],
): Match3DWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const themeText = state.summary?.trim() || state.title?.trim() || '';
      const fallbackSummary =
        state.status === 'failed'
          ? '玩法素材生成失败，可重新打开处理。'
          : '正在生成玩法素材。';
      return {
        workId: `match3d-work-${sessionId}`,
        profileId: sessionId,
        ownerUserId: '',
        sourceSessionId: sessionId,
        gameName: '抓大鹅草稿',
        themeText,
        summary: themeText || fallbackSummary,
        tags: [],
        coverImageSrc: null,
        referenceImageSrc: null,
        clearCount: 0,
        difficulty: 0,
        publicationStatus: 'draft',
        playCount: 0,
        updatedAt: state.updatedAt,
        publishedAt: null,
        publishReady: false,
        generationStatus:
          state.status === 'failed'
            ? 'failed'
            : state.status === 'generating'
              ? 'generating'
              : 'ready',
        generatedItemAssets: [],
      };
    });
}

function buildPendingSquareHoleWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly SquareHoleWorkSummary[],
): SquareHoleWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => ({
      workId: `square-hole-work-${sessionId}`,
      profileId: sessionId,
      ownerUserId: '',
      sourceSessionId: sessionId,
      gameName: '方洞挑战草稿',
      themeText: '',
      twistRule: '',
      summary:
        state.status === 'failed'
          ? '挑战素材生成失败，可重新打开处理。'
          : '正在生成挑战素材。',
      tags: [],
      coverImageSrc: null,
      backgroundPrompt: '',
      backgroundImageSrc: null,
      shapeOptions: [],
      holeOptions: [],
      shapeCount: 0,
      difficulty: 0,
      publicationStatus: 'draft',
      playCount: 0,
      updatedAt: state.updatedAt,
      publishedAt: null,
      publishReady: false,
    }));
}

function buildPendingPuzzleWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly PuzzleWorkSummary[],
): PuzzleWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([sessionId]) =>
      existingItems.every((item) => item.sourceSessionId !== sessionId),
    )
    .map(([sessionId, state]) => {
      const profileId =
        buildPuzzleResultProfileId(sessionId) ?? `puzzle-profile-${sessionId}`;
      const title = state.title?.trim() || '拼图草稿';
      const summary =
        state.summary?.trim() ||
        (state.status === 'failed'
          ? '拼图草稿生成失败，可重新打开处理。'
          : '正在生成拼图草稿。');
      return {
        workId:
          buildPuzzleResultWorkId(sessionId) ?? `puzzle-work-${sessionId}`,
        profileId,
        ownerUserId: '',
        sourceSessionId: sessionId,
        authorDisplayName: '',
        workTitle: title,
        workDescription: summary,
        levelName: title,
        summary,
        themeTags: [],
        coverImageSrc: null,
        coverAssetId: null,
        publicationStatus: 'draft',
        updatedAt: state.updatedAt,
        publishedAt: null,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
        publishReady: false,
        generationStatus:
          state.status === 'generating'
            ? 'generating'
            : state.status === 'failed'
              ? 'failed'
              : 'ready',
        levels: [],
      };
    });
}

function buildPendingVisualNovelWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly VisualNovelWorkSummary[],
): VisualNovelWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([profileId]) =>
      existingItems.every((item) => item.profileId !== profileId),
    )
    .map(([profileId, state]) => ({
      runtimeKind: 'visual-novel',
      profileId,
      ownerUserId: '',
      title: '视觉小说草稿',
      description:
        state.status === 'failed'
          ? '视觉小说草稿生成失败，可重新打开处理。'
          : '正在生成视觉小说草稿。',
      coverImageSrc: null,
      tags: [],
      publishStatus: 'draft',
      publishReady: false,
      playCount: 0,
      updatedAt: state.updatedAt,
      publishedAt: null,
    }));
}

function buildPendingBarkBattleWorks(
  pending: Record<string, PendingDraftShelfState> | undefined,
  existingItems: readonly BarkBattleWorkSummary[],
): BarkBattleWorkSummary[] {
  if (!pending) {
    return [];
  }

  return Object.entries(pending)
    .filter(([id]) =>
      existingItems.every((item) => item.workId !== id && item.draftId !== id),
    )
    .map(([id, state]) => ({
      workId: id,
      draftId: id,
      ownerUserId: '',
      authorDisplayName: '',
      title: '汪汪声浪草稿',
      summary:
        state.status === 'failed'
          ? '声浪竞技素材生成失败，可重新打开处理。'
          : '正在生成声浪竞技素材。',
      themeDescription: '',
      playerImageDescription: '',
      opponentImageDescription: '',
      onomatopoeia: [],
      playerCharacterImageSrc: null,
      opponentCharacterImageSrc: null,
      uiBackgroundImageSrc: null,
      difficultyPreset: 'normal',
      status: 'draft',
      generationStatus:
        state.status === 'generating'
          ? 'pending_assets'
          : state.status === 'failed'
            ? 'partial_failed'
            : 'ready',
      publishReady: false,
      playCount: 0,
      updatedAt: state.updatedAt,
      publishedAt: null,
    }));
}

function buildPuzzleCompileActionFromFormPayload(
  payload: CreatePuzzleAgentSessionRequest | null,
): PuzzleAgentActionRequest {
  const pictureDescription =
    payload?.pictureDescription?.trim() || payload?.seedText?.trim();
  const workTitle = payload?.workTitle?.trim();
  const workDescription = payload?.workDescription?.trim() || pictureDescription;

  return {
    action: 'compile_puzzle_draft',
    promptText: pictureDescription,
    ...(workTitle ? { workTitle } : {}),
    ...(workDescription ? { workDescription } : {}),
    ...(pictureDescription ? { pictureDescription } : {}),
    referenceImageSrc: payload?.referenceImageSrc || null,
    referenceImageSrcs: payload?.referenceImageSrcs ?? [],
    referenceImageAssetObjectId: payload?.referenceImageAssetObjectId ?? null,
    referenceImageAssetObjectIds: payload?.referenceImageAssetObjectIds ?? [],
    imageModel: payload?.imageModel ?? null,
    aiRedraw: payload?.aiRedraw ?? true,
    candidateCount: 1,
  };
}

function buildPuzzleFormPayloadFromSession(
  session: PuzzleAgentSessionSnapshot,
): CreatePuzzleAgentSessionRequest {
  const formDraft = session.draft?.formDraft;
  const pictureDescription =
    formDraft?.pictureDescription?.trim() ||
    session.draft?.levels?.[0]?.pictureDescription?.trim() ||
    session.anchorPack.visualSubject.value.trim() ||
    session.seedText?.trim() ||
    '';
  const workTitle =
    formDraft?.workTitle?.trim() || session.draft?.workTitle?.trim();
  const workDescription =
    formDraft?.workDescription?.trim() ||
    session.draft?.workDescription?.trim() ||
    session.draft?.summary?.trim() ||
    pictureDescription;

  return {
    seedText: pictureDescription,
    ...(workTitle ? { workTitle } : {}),
    ...(workDescription ? { workDescription } : {}),
    pictureDescription,
    referenceImageSrc: null,
    referenceImageSrcs: [],
    referenceImageAssetObjectId: null,
    referenceImageAssetObjectIds: [],
    imageModel: null,
    aiRedraw: true,
  };
}

function buildPendingPuzzleDraftMetadata(
  payload: CreatePuzzleAgentSessionRequest | null | undefined,
) {
  const title = payload?.workTitle?.trim();
  const summary =
    payload?.workDescription?.trim() ||
    payload?.pictureDescription?.trim() ||
    payload?.seedText?.trim();
  return {
    ...(title ? { title } : {}),
    ...(summary ? { summary } : {}),
  };
}

function buildPendingMatch3DDraftMetadata(
  payload: CreateMatch3DSessionRequest | null | undefined,
) {
  const themeText = payload?.themeText?.trim() || payload?.seedText?.trim();
  return {
    ...(themeText ? { title: themeText, summary: themeText } : {}),
  };
}

function buildPuzzleFormPayloadFromAction(
  payload: PuzzleAgentActionRequest,
): CreatePuzzleAgentSessionRequest | null {
  if (
    payload.action !== 'compile_puzzle_draft' &&
    payload.action !== 'save_puzzle_form_draft'
  ) {
    return null;
  }

  const workTitle = payload.workTitle?.trim() ?? '';
  const workDescription = payload.workDescription?.trim() ?? '';
  const pictureDescription =
    payload.pictureDescription?.trim() || payload.promptText?.trim() || '';

  return {
    seedText: pictureDescription,
    ...(workTitle ? { workTitle } : {}),
    ...(workDescription ? { workDescription } : {}),
    pictureDescription,
    referenceImageSrc:
      payload.action === 'compile_puzzle_draft'
        ? (payload.referenceImageSrc ?? null)
        : (payload.referenceImageSrc ?? null),
    referenceImageSrcs: payload.referenceImageSrcs ?? [],
    referenceImageAssetObjectId: payload.referenceImageAssetObjectId ?? null,
    referenceImageAssetObjectIds: payload.referenceImageAssetObjectIds ?? [],
    imageModel:
      payload.action === 'compile_puzzle_draft'
        ? (payload.imageModel ?? null)
        : (payload.imageModel ?? null),
    aiRedraw:
      payload.action === 'compile_puzzle_draft'
        ? (payload.aiRedraw ?? true)
        : (payload.aiRedraw ?? true),
  };
}

function isPuzzleFormOnlyDraft(session: PuzzleAgentSessionSnapshot | null) {
  return Boolean(
    session?.stage === 'collecting_anchors' && session.draft?.formDraft,
  );
}

function isEmptyPuzzleFormOnlyDraft(
  session: PuzzleAgentSessionSnapshot | null,
) {
  if (!isPuzzleFormOnlyDraft(session)) {
    return false;
  }

  const formDraft = session?.draft?.formDraft;
  return !(
    session?.seedText?.trim() ||
    formDraft?.workTitle?.trim() ||
    formDraft?.workDescription?.trim() ||
    formDraft?.pictureDescription?.trim()
  );
}

const CustomWorldGenerationView = lazy(async () => {
  const module = await import('../CustomWorldGenerationView');
  return {
    default: module.CustomWorldGenerationView,
  };
});

const UnifiedCreationWorkspace = lazy(async () => {
  const module = await import('../unified-creation/UnifiedCreationWorkspace');
  return {
    default: module.UnifiedCreationWorkspace,
  };
});

const UnifiedGenerationPage = lazy(async () => {
  const module = await import('../unified-creation/UnifiedGenerationPage');
  return {
    default: module.UnifiedGenerationPage,
  };
});

const RpgCreationResultView = lazy(async () => {
  const module = await import('../rpg-creation-result/RpgCreationResultView');
  return {
    default: module.RpgCreationResultView,
  };
});

const CustomWorldAgentWorkspace = lazy(async () => {
  const module = await import(
    '../custom-world-agent/CustomWorldAgentWorkspace'
  );
  return {
    default: module.CustomWorldAgentWorkspace,
  };
});

const BigFishAgentWorkspace = lazy(async () => {
  const module = await import('../big-fish-creation/BigFishAgentWorkspace');
  return {
    default: module.BigFishAgentWorkspace,
  };
});

const BigFishResultView = lazy(async () => {
  const module = await import('../big-fish-result/BigFishResultView');
  return {
    default: module.BigFishResultView,
  };
});

const BigFishRuntimeShell = lazy(async () => {
  const module = await import('../big-fish-runtime/BigFishRuntimeShell');
  return {
    default: module.BigFishRuntimeShell,
  };
});

const Match3DResultView = lazy(async () => {
  const module = await import('../match3d-result/Match3DResultView');
  return {
    default: module.Match3DResultView,
  };
});

const Match3DRuntimeShell = lazy(async () => {
  const module = await import('../match3d-runtime/Match3DRuntimeShell');
  return {
    default: module.Match3DRuntimeShell,
  };
});

const SquareHoleAgentWorkspace = lazy(async () => {
  const module = await import(
    '../square-hole-creation/SquareHoleAgentWorkspace'
  );
  return {
    default: module.SquareHoleAgentWorkspace,
  };
});

const SquareHoleResultView = lazy(async () => {
  const module = await import('../square-hole-result/SquareHoleResultView');
  return {
    default: module.SquareHoleResultView,
  };
});

const SquareHoleRuntimeShell = lazy(async () => {
  const module = await import('../square-hole-runtime/SquareHoleRuntimeShell');
  return {
    default: module.SquareHoleRuntimeShell,
  };
});

const JumpHopResultView = lazy(async () => {
  const module = await import('../jump-hop-result/JumpHopResultView');
  return {
    default: module.JumpHopResultView,
  };
});

const JumpHopRuntimeShell = lazy(async () => {
  const module = await import('../jump-hop-runtime/JumpHopRuntimeShell');
  return {
    default: module.JumpHopRuntimeShell,
  };
});

const PuzzleClearWorkspace = lazy(async () => {
  const module = await import('../puzzle-clear-creation/PuzzleClearWorkspace');
  return {
    default: module.PuzzleClearWorkspace,
  };
});

const PuzzleClearResultView = lazy(async () => {
  const module = await import('../puzzle-clear-result/PuzzleClearResultView');
  return {
    default: module.PuzzleClearResultView,
  };
});

const PuzzleClearRuntimeShell = lazy(async () => {
  const module = await import('../puzzle-clear-runtime/PuzzleClearRuntimeShell');
  return {
    default: module.PuzzleClearRuntimeShell,
  };
});

const WoodenFishResultView = lazy(async () => {
  const module = await import('../wooden-fish-result/WoodenFishResultView');
  return {
    default: module.WoodenFishResultView,
  };
});

const WoodenFishRuntimeShell = lazy(async () => {
  const module = await import('../wooden-fish-runtime/WoodenFishRuntimeShell');
  return {
    default: module.WoodenFishRuntimeShell,
  };
});

const BarkBattleConfigEditor = lazy(async () => {
  const module = await import('../bark-battle-creation/BarkBattleConfigEditor');
  return {
    default: module.BarkBattleConfigEditor,
  };
});

const BarkBattleGeneratingView = lazy(async () => {
  const module = await import(
    '../bark-battle-creation/BarkBattleGeneratingView'
  );
  return {
    default: module.BarkBattleGeneratingView,
  };
});

const BarkBattleResultView = lazy(async () => {
  const module = await import('../bark-battle-creation/BarkBattleResultView');
  return {
    default: module.BarkBattleResultView,
  };
});

const BarkBattleRuntimeShell = lazy(async () => {
  const module = await import(
    '../../games/bark-battle/ui/BarkBattleRuntimeShell'
  );
  return {
    default: module.BarkBattleRuntimeShell,
  };
});

const CustomWorldCreationHub = lazy(async () => {
  const module = await import('../custom-world-home/CustomWorldCreationHub');
  return {
    default: module.CustomWorldCreationHub,
  };
});

const CreativeAgentWorkspace = lazy(async () => {
  const module = await import('../creative-agent/CreativeAgentWorkspace');
  return {
    default: module.CreativeAgentWorkspace,
  };
});

const VisualNovelAgentWorkspace = lazy(async () => {
  const module = await import(
    '../visual-novel-creation/VisualNovelAgentWorkspace'
  );
  return {
    default: module.VisualNovelAgentWorkspace,
  };
});

const BabyObjectMatchWorkspace = lazy(async () => {
  const module = await import(
    '../edutainment-creation/BabyObjectMatchWorkspace'
  );
  return {
    default: module.BabyObjectMatchWorkspace,
  };
});

const BabyObjectMatchResultView = lazy(async () => {
  const module = await import(
    '../edutainment-result/BabyObjectMatchResultView'
  );
  return {
    default: module.BabyObjectMatchResultView,
  };
});

const BabyObjectMatchRuntimeShell = lazy(async () => {
  const module = await import(
    '../edutainment-runtime/BabyObjectMatchRuntimeShell'
  );
  return {
    default: module.BabyObjectMatchRuntimeShell,
  };
});

const BabyLoveDrawingRuntimeShell = lazy(async () => {
  const module = await import(
    '../edutainment-runtime/BabyLoveDrawingRuntimeShell'
  );
  return {
    default: module.BabyLoveDrawingRuntimeShell,
  };
});

const VisualNovelResultView = lazy(async () => {
  const module = await import('../visual-novel-result/VisualNovelResultView');
  return {
    default: module.VisualNovelResultView,
  };
});

const VisualNovelRuntimeShell = lazy(async () => {
  const module = await import(
    '../visual-novel-runtime/VisualNovelRuntimeShell'
  );
  return {
    default: module.VisualNovelRuntimeShell,
  };
});

const PuzzleResultView = lazy(async () => {
  const module = await import('../puzzle-result/PuzzleResultView');
  return {
    default: module.PuzzleResultView,
  };
});

const PuzzleGalleryDetailView = lazy(async () => {
  const module = await import('../puzzle-gallery/PuzzleGalleryDetailView');
  return {
    default: module.PuzzleGalleryDetailView,
  };
});

const PuzzleRuntimeShell = lazy(async () => {
  const module = await import('../puzzle-runtime/PuzzleRuntimeShell');
  return {
    default: module.PuzzleRuntimeShell,
  };
});

function LazyPanelFallback({ label }: { label: string }) {
  return (
    <div className="flex h-full min-h-0 items-center justify-center">
      <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
        {label}
      </div>
    </div>
  );
}

function CreationResultRecoveryPanel({
  title,
  message,
  actionLabel,
  onAction,
}: {
  title: string;
  message: string;
  actionLabel: string;
  onAction: () => void;
}) {
  return (
    <div className="flex h-full min-h-0 items-center justify-center px-3 py-6">
      <div className="platform-subpanel w-full max-w-sm rounded-[1.5rem] p-5 text-center">
        <div className="text-base font-black text-[var(--platform-text-strong)]">
          {title}
        </div>
        <div className="mt-2 text-sm leading-6 text-[var(--platform-text-base)]">
          {message}
        </div>
        <button
          type="button"
          onClick={onAction}
          className="platform-button platform-button--primary mt-4 min-h-11 justify-center px-4 py-3 text-sm"
        >
          {actionLabel}
        </button>
      </div>
    </div>
  );
}

function mergePuzzleServiceRuntimeState(
  currentRun: PuzzleRunSnapshot,
  serviceRun: PuzzleRunSnapshot,
): PuzzleRunSnapshot {
  if (!currentRun.currentLevel || !serviceRun.currentLevel) {
    return currentRun;
  }

  const serviceLevel = serviceRun.currentLevel;
  const leaderboardEntries =
    serviceLevel.leaderboardEntries.length > 0
      ? serviceLevel.leaderboardEntries
      : serviceRun.leaderboardEntries;

  // 中文注释：拼块布局和通关状态由前端即时裁决；后端快照只合并榜单与下一关 handoff。
  return {
    ...currentRun,
    runId: serviceRun.runId,
    entryProfileId: serviceRun.entryProfileId,
    clearedLevelCount: Math.max(
      currentRun.clearedLevelCount,
      serviceRun.clearedLevelCount,
    ),
    recommendedNextProfileId: serviceRun.recommendedNextProfileId,
    nextLevelMode: serviceRun.nextLevelMode,
    nextLevelProfileId: serviceRun.nextLevelProfileId,
    nextLevelId: serviceRun.nextLevelId,
    recommendedNextWorks: serviceRun.recommendedNextWorks,
    leaderboardEntries,
    currentLevel: {
      ...currentRun.currentLevel,
      leaderboardEntries:
        leaderboardEntries.length > 0
          ? leaderboardEntries
          : currentRun.currentLevel.leaderboardEntries,
    },
  };
}

export function PlatformEntryFlowShellImpl({
  selectionStage,
  setSelectionStage,
  hasSavedGame,
  savedSnapshot,
  handleContinueGame,
  handleStartNewGame,
  handleCustomWorldSelect,
  initialPublicWorkCode,
}: PlatformEntryFlowShellProps) {
  const authUi = useAuthUi();
  const platformThemeClass =
    authUi?.platformTheme === 'dark'
      ? 'platform-theme--dark'
      : 'platform-theme--light';
  const isDesktopLayout = usePlatformDesktopLayout();
  const [showCreationTypeModal, setShowCreationTypeModal] = useState(false);
  const [draftGenerationPointNotice, setDraftGenerationPointNotice] = useState<{
    title: string;
    message: string;
  } | null>(null);
  const [selectedDetailEntry, setSelectedDetailEntry] =
    useState<CustomWorldLibraryEntry<CustomWorldProfile> | null>(null);
  const [selectedPublicWorkDetail, setSelectedPublicWorkDetail] =
    useState<PlatformPublicGalleryCard | null>(null);
  const [selectedPublicWorkAuthor, setSelectedPublicWorkAuthor] =
    useState<PublicUserSummary | null>(null);
  const publicWorkAuthorRequestKeyRef = useRef(0);
  const [publicWorkDetailError, setPublicWorkDetailError] = useState<
    string | null
  >(null);
  const [isPublicWorkDetailBusy, setIsPublicWorkDetailBusy] = useState(false);
  const [bigFishWorks, setBigFishWorks] = useState<BigFishWorkSummary[]>([]);
  const [bigFishGalleryEntries, setBigFishGalleryEntries] = useState<
    BigFishWorkSummary[]
  >([]);
  const [match3dWorks, setMatch3DWorks] = useState<Match3DWorkSummary[]>([]);
  const [match3dGalleryEntries, setMatch3DGalleryEntries] = useState<
    Match3DWorkSummary[]
  >([]);
  const [match3dProfile, setMatch3DProfile] =
    useState<Match3DWorkProfile | null>(null);
  const [match3dRuntimeProfile, setMatch3DRuntimeProfile] =
    useState<Match3DWorkProfile | null>(null);
  const [match3dRun, setMatch3DRun] = useState<Match3DRunSnapshot | null>(null);
  const [match3dRuntimeReturnStage, setMatch3DRuntimeReturnStage] = useState<
    'match3d-result' | 'work-detail'
  >('match3d-result');
  const [match3dFormDraftPayload, setMatch3DFormDraftPayload] =
    useState<CreateMatch3DSessionRequest | null>(null);
  const [match3dGenerationState, setMatch3DGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [match3dBackgroundCompileTasks, setMatch3DBackgroundCompileTasks] =
    useState<Record<string, Match3DBackgroundCompileTask>>({});
  const [isMatch3DLoadingLibrary, setIsMatch3DLoadingLibrary] = useState(false);
  const [squareHoleWorks, setSquareHoleWorks] = useState<
    SquareHoleWorkSummary[]
  >([]);
  const [squareHoleGalleryEntries, setSquareHoleGalleryEntries] = useState<
    SquareHoleWorkSummary[]
  >([]);
  const [squareHoleProfile, setSquareHoleProfile] =
    useState<SquareHoleWorkProfile | null>(null);
  const [squareHoleRun, setSquareHoleRun] =
    useState<SquareHoleRunSnapshot | null>(null);
  const [squareHoleRuntimeReturnStage, setSquareHoleRuntimeReturnStage] =
    useState<SquareHoleRuntimeReturnStage>('square-hole-result');
  const [isSquareHoleLoadingLibrary, setIsSquareHoleLoadingLibrary] =
    useState(false);
  const [squareHoleGenerationState, setSquareHoleGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [jumpHopSession, setJumpHopSession] =
    useState<JumpHopSessionSnapshotResponse | null>(null);
  const [jumpHopRun, setJumpHopRun] = useState<
    JumpHopRunResponse['run'] | null
  >(null);
  const [jumpHopRuntimeRequestOptions, setJumpHopRuntimeRequestOptions] =
    useState<JumpHopRuntimeRequestOptions | null>(null);
  const [jumpHopWork, setJumpHopWork] =
    useState<JumpHopWorkProfileResponse | null>(null);
  const [jumpHopGalleryEntries, setJumpHopGalleryEntries] = useState<
    JumpHopGalleryCardResponse[]
  >([]);
  const [jumpHopWorks, setJumpHopWorks] = useState<
    JumpHopWorkSummaryResponse[]
  >([]);
  const [jumpHopRuntimeReturnStage, setJumpHopRuntimeReturnStage] =
    useState<JumpHopRuntimeReturnStage>('jump-hop-result');
  const [jumpHopGenerationState, setJumpHopGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [jumpHopError, setJumpHopError] = useState<string | null>(null);
  const [isJumpHopBusy, setIsJumpHopBusy] = useState(false);
  const [puzzleClearSession, setPuzzleClearSession] =
    useState<PuzzleClearSessionSnapshotResponse | null>(null);
  const [puzzleClearRun, setPuzzleClearRun] = useState<
    PuzzleClearRunResponse['run'] | null
  >(null);
  const [puzzleClearWork, setPuzzleClearWork] =
    useState<PuzzleClearWorkProfileResponse | null>(null);
  const [puzzleClearWorks, setPuzzleClearWorks] = useState<
    PuzzleClearWorkSummaryResponse[]
  >([]);
  const [puzzleClearGalleryEntries, setPuzzleClearGalleryEntries] = useState<
    PuzzleClearGalleryCardResponse[]
  >([]);
  const [puzzleClearRuntimeReturnStage, setPuzzleClearRuntimeReturnStage] =
    useState<PuzzleClearRuntimeReturnStage>('puzzle-clear-result');
  const [puzzleClearGenerationState, setPuzzleClearGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [puzzleClearError, setPuzzleClearError] = useState<string | null>(null);
  const [isPuzzleClearBusy, setIsPuzzleClearBusy] = useState(false);
  const [barkBattleWorks, setBarkBattleWorks] = useState<
    BarkBattleWorkSummary[]
  >([]);
  const [barkBattleGalleryEntries, setBarkBattleGalleryEntries] = useState<
    BarkBattleWorkSummary[]
  >([]);
  const [woodenFishSession, setWoodenFishSession] =
    useState<WoodenFishSessionSnapshotResponse | null>(null);
  const [woodenFishRun, setWoodenFishRun] = useState<
    WoodenFishRunResponse['run'] | null
  >(null);
  const [woodenFishWork, setWoodenFishWork] =
    useState<WoodenFishWorkProfileResponse | null>(null);
  const [woodenFishWorks, setWoodenFishWorks] = useState<
    WoodenFishWorkSummaryResponse[]
  >([]);
  const [woodenFishGalleryEntries, setWoodenFishGalleryEntries] = useState<
    WoodenFishGalleryCardResponse[]
  >([]);
  const [woodenFishRuntimeReturnStage, setWoodenFishRuntimeReturnStage] =
    useState<WoodenFishRuntimeReturnStage>('wooden-fish-result');
  const [woodenFishGenerationState, setWoodenFishGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [woodenFishError, setWoodenFishError] = useState<string | null>(null);
  const [isWoodenFishBusy, setIsWoodenFishBusy] = useState(false);
  const [barkBattlePublishedConfig, setBarkBattlePublishedConfig] =
    useState<BarkBattlePublishedConfig | null>(null);
  const [barkBattleDraftConfig, setBarkBattleDraftConfig] =
    useState<BarkBattleDraftConfig | null>(null);
  const [barkBattleRuntimeMode, setBarkBattleRuntimeMode] = useState<
    'draft' | 'published'
  >('draft');
  const [barkBattleRuntimeReturnStage, setBarkBattleRuntimeReturnStage] =
    useState<BarkBattleRuntimeReturnStage>('platform');
  const [barkBattleError, setBarkBattleError] = useState<string | null>(null);
  const [
    barkBattleGenerationPartialFailed,
    setBarkBattleGenerationPartialFailed,
  ] = useState(false);
  const [isBarkBattleBusy, setIsBarkBattleBusy] = useState(false);
  const [bigFishRun, setBigFishRun] =
    useState<BigFishRuntimeSnapshotResponse | null>(null);
  const [bigFishRuntimeShare, setBigFishRuntimeShare] = useState<{
    title: string;
    publicWorkCode: string;
  } | null>(null);
  const [_bigFishRuntimeWork, setBigFishRuntimeWork] =
    useState<BigFishWorkSummary | null>(null);
  const [bigFishRuntimeStartedAt, setBigFishRuntimeStartedAt] = useState<
    number | null
  >(null);
  const [_bigFishRuntimeSessionSource, setBigFishRuntimeSessionSource] =
    useState<BigFishRuntimeSessionSource>(null);
  const [bigFishRuntimeReturnStage, setBigFishRuntimeReturnStage] =
    useState<BigFishRuntimeReturnStage>('platform');
  const bigFishInputInFlightRef = useRef(false);
  const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false);
  const [bigFishGenerationState, setBigFishGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [activeRecommendEntryKey, setActiveRecommendEntryKey] = useState<
    string | null
  >(null);
  const [activeRecommendRuntimeKind, setActiveRecommendRuntimeKind] =
    useState<RecommendRuntimeKind | null>(null);
  const [activeRecommendRuntimeError, setActiveRecommendRuntimeError] =
    useState<string | null>(null);
  const [isStartingRecommendEntry, setIsStartingRecommendEntry] =
    useState(false);
  const recommendRuntimeStartRequestRef = useRef(0);
  const [, setPuzzleOperation] = useState<PuzzleAgentOperationRecord | null>(
    null,
  );
  const [puzzleWorks, setPuzzleWorks] = useState<PuzzleWorkSummary[]>([]);
  const [puzzleGalleryEntries, setPuzzleGalleryEntries] = useState<
    PuzzleWorkSummary[]
  >([]);
  const [selectedPuzzleDetail, setSelectedPuzzleDetail] =
    useState<PuzzleWorkSummary | null>(null);
  const [puzzleDetailReturnTarget, setPuzzleDetailReturnTarget] =
    useState<PuzzleDetailReturnTarget | null>(null);
  const [puzzleRuntimeReturnStage, setPuzzleRuntimeReturnStage] =
    useState<PuzzleRuntimeReturnStage>('puzzle-gallery-detail');
  const [puzzleRuntimeAuthMode, setPuzzleRuntimeAuthMode] =
    useState<PuzzleRuntimeAuthMode>('default');
  const [isPuzzleLeaderboardBusy, setIsPuzzleLeaderboardBusy] = useState(false);
  const submittedPuzzleLeaderboardKeysRef = useRef(new Set<string>());
  const puzzleStartInFlightKeyRef = useRef<string | null>(null);
  const [puzzleRun, setPuzzleRun] = useState<PuzzleRunSnapshot | null>(null);
  const puzzleRunRef = useRef<PuzzleRunSnapshot | null>(null);
  const errorSetterRefNoop = useMemo(
    () =>
      ((_: SetStateAction<string | null>) => undefined) as Dispatch<
        SetStateAction<string | null>
      >,
    [],
  );
  const bigFishErrorSetterRef = useRef(errorSetterRefNoop);
  const match3DErrorSetterRef = useRef(errorSetterRefNoop);
  const squareHoleErrorSetterRef = useRef(errorSetterRefNoop);
  const puzzleErrorSetterRef = useRef(errorSetterRefNoop);
  const visualNovelErrorSetterRef = useRef(errorSetterRefNoop);
  const [isPuzzleLoadingLibrary, setIsPuzzleLoadingLibrary] = useState(false);
  const [puzzleShelfError, setPuzzleShelfError] = useState<string | null>(null);
  const [puzzleCreationError, setPuzzleCreationError] = useState<string | null>(
    null,
  );
  const [puzzleGenerationState, setPuzzleGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [puzzleBackgroundCompileTasks, setPuzzleBackgroundCompileTasks] =
    useState<Record<string, PuzzleBackgroundCompileTask>>({});
  const [miniGameGenerationProgressNowMs, setMiniGameGenerationProgressNowMs] =
    useState(() => Date.now());
  const [puzzleFormDraftPayload, setPuzzleFormDraftPayload] =
    useState<CreatePuzzleAgentSessionRequest | null>(null);
  const [puzzleOnboardingPrompt, setPuzzleOnboardingPrompt] = useState('');
  const [puzzleOnboardingPhase, setPuzzleOnboardingPhase] =
    useState<PuzzleOnboardingPhase>('input');
  const [puzzleOnboardingDraft, setPuzzleOnboardingDraft] =
    useState<PuzzleOnboardingDraft | null>(null);
  const [puzzleOnboardingError, setPuzzleOnboardingError] = useState<
    string | null
  >(null);
  const [isPuzzleOnboardingSaving, setIsPuzzleOnboardingSaving] =
    useState(false);
  const [creativeAgentSession, setCreativeAgentSession] =
    useState<CreativeAgentSessionSnapshot | null>(null);
  const [creativeAgentError, setCreativeAgentError] = useState<string | null>(
    null,
  );
  const [isCreativeAgentBusy, setIsCreativeAgentBusy] = useState(false);
  const [isCreativeAgentStreaming, setIsCreativeAgentStreaming] =
    useState(false);
  const [creativeAgentEvents, setCreativeAgentEvents] = useState<
    CreativeAgentSseEvent[]
  >([]);
  const [activeCreativeAgentSessionId, setActiveCreativeAgentSessionId] =
    useState<string | null>(null);
  const [creativeDraftEditError, setCreativeDraftEditError] = useState<
    string | null
  >(null);
  const [isCreativeDraftEditBusy, setIsCreativeDraftEditBusy] = useState(false);
  const [visualNovelWork, setVisualNovelWork] =
    useState<VisualNovelWorkDetail | null>(null);
  const [visualNovelWorks, setVisualNovelWorks] = useState<
    VisualNovelWorkSummary[]
  >([]);
  const [visualNovelGalleryEntries, setVisualNovelGalleryEntries] = useState<
    VisualNovelWorkSummary[]
  >([]);
  const [visualNovelRun, setVisualNovelRun] =
    useState<VisualNovelRunSnapshot | null>(null);
  const [visualNovelRuntimeReturnStage, setVisualNovelRuntimeReturnStage] =
    useState<VisualNovelRuntimeReturnStage>('visual-novel-result');
  const [visualNovelFormDraftPayload, setVisualNovelFormDraftPayload] =
    useState<VisualNovelEntryFormPayload | null>(null);
  const [
    visualNovelGenerationStartedAtMs,
    setVisualNovelGenerationStartedAtMs,
  ] = useState<number | null>(null);
  const [visualNovelGenerationPhase, setVisualNovelGenerationPhase] =
    useState<VisualNovelEntryGenerationPhase>('generating');
  const [babyObjectMatchDraft, setBabyObjectMatchDraft] =
    useState<BabyObjectMatchDraft | null>(null);
  const [babyObjectMatchDrafts, setBabyObjectMatchDrafts] = useState<
    BabyObjectMatchDraft[]
  >([]);
  const [babyObjectMatchFormPayload, setBabyObjectMatchFormPayload] =
    useState<CreateBabyObjectMatchDraftRequest | null>(null);
  const [babyObjectMatchGenerationState, setBabyObjectMatchGenerationState] =
    useState<MiniGameDraftGenerationState | null>(null);
  const [babyObjectMatchGenerationPhase, setBabyObjectMatchGenerationPhase] =
    useState<BabyObjectMatchGenerationPhase>('generating');
  const [
    babyObjectMatchRuntimeReturnStage,
    setBabyObjectMatchRuntimeReturnStage,
  ] = useState<BabyObjectMatchRuntimeReturnStage>('baby-object-match-result');
  const [babyObjectMatchError, setBabyObjectMatchError] = useState<
    string | null
  >(null);
  const [isBabyObjectMatchBusy, setIsBabyObjectMatchBusy] = useState(false);
  const [isVisualNovelLoadingLibrary, setIsVisualNovelLoadingLibrary] =
    useState(false);
  const [isPuzzleNextLevelGenerating, setIsPuzzleNextLevelGenerating] =
    useState(false);
  const [isSearchingPublicCode, setIsSearchingPublicCode] = useState(false);
  const [publicSearchError, setPublicSearchError] = useState<string | null>(
    null,
  );
  const [searchedPublicUser, setSearchedPublicUser] =
    useState<PublicUserSummary | null>(null);
  const [deletingCreationWorkId, setDeletingCreationWorkId] = useState<
    string | null
  >(null);
  const [pendingDeleteCreationWork, setPendingDeleteCreationWork] =
    useState<DeleteCreationWorkConfirmation | null>(null);
  const [
    claimingPuzzlePointIncentiveProfileId,
    setClaimingPuzzlePointIncentiveProfileId,
  ] = useState<string | null>(null);
  const [publishSharePayload, setPublishSharePayload] =
    useState<PublishShareModalPayload | null>(null);
  const [creationEntryConfig, setCreationEntryConfig] =
    useState<CreationEntryConfig | null>(null);
  const [creationEntryConfigError, setCreationEntryConfigError] = useState<
    string | null
  >(null);
  const creationEntryTypes = useMemo(
    () =>
      creationEntryConfig
        ? derivePlatformCreationTypes(creationEntryConfig.creationTypes)
        : [],
    [creationEntryConfig],
  );
  const unifiedCreationConfigById = useMemo(() => {
    const entries = creationEntryConfig?.creationTypes ?? [];
    return new Map(entries.map((entry) => [entry.id, entry]));
  }, [creationEntryConfig]);
  const getUnifiedSpec = useCallback(
    (playId: UnifiedCreationPlayId) =>
      getUnifiedCreationSpec(playId, unifiedCreationConfigById.get(playId)),
    [unifiedCreationConfigById],
  );
  const isBigFishCreationVisible = isPlatformCreationTypeVisible(
    creationEntryTypes,
    'big-fish',
  );
  const isJumpHopCreationVisible = isPlatformCreationTypeVisible(
    creationEntryTypes,
    'jump-hop',
  );
  const isPuzzleClearCreationVisible = isPlatformCreationTypeVisible(
    creationEntryTypes,
    'puzzle-clear',
  );
  const isSquareHoleCreationVisible = isPlatformCreationTypeVisible(
    creationEntryTypes,
    'square-hole',
  );
  const isBabyObjectMatchVisible = isPlatformCreationTypeVisible(
    creationEntryTypes,
    'baby-object-match',
  );
  const isVisualNovelCreationOpen = isPlatformCreationTypeOpen(
    creationEntryTypes,
    'visual-novel',
  );
  const [profilePlayStats, setProfilePlayStats] =
    useState<ProfilePlayStatsResponse | null>(null);
  const [profilePlayStatsError, setProfilePlayStatsError] = useState<
    string | null
  >(null);
  const [isProfilePlayStatsLoading, setIsProfilePlayStatsLoading] =
    useState(false);
  const [isProfilePlayStatsOpen, setIsProfilePlayStatsOpen] = useState(false);
  const hadReadableProtectedDataRef = useRef(false);
  const hasInitialAgentSession = Boolean(
    readCustomWorldAgentUiState().activeSessionId &&
      shouldRestoreCustomWorldAgentUiState(),
  );
  const handledInitialPublicWorkCodeRef = useRef<string | null>(null);
  const selectionStageRef = useRef(selectionStage);
  const creationFlowReturnTargetRef =
    useRef<CreationFlowReturnTarget>('create');
  const activeMatch3DGenerationSessionIdRef = useRef<string | null>(null);
  const activePuzzleGenerationSessionIdRef = useRef<string | null>(null);
  const [draftGenerationNotices, setDraftGenerationNotices] =
    useState<DraftGenerationNoticeMap>({});
  const [pendingDraftShelfItems, setPendingDraftShelfItems] =
    useState<PendingDraftShelfMap>({});
  const profileWalletLocalDeltaRef = useRef(0);
  const lastProfileDashboardSnapshotRef =
    useRef<ProfileDashboardSummary | null>(null);
  const [
    pendingPlatformTaskCompletionDialog,
    setPendingPlatformTaskCompletionDialog,
  ] = useState<
    | (PlatformTaskCompletionDialogPayload & {
        key: string;
        completedAtMs: number | null;
      })
    | null
  >(null);
  const [
    pendingPlatformTaskFailureDialog,
    setPendingPlatformTaskFailureDialog,
  ] = useState<
    | (PlatformErrorDialogPayload & {
        key: string;
        failedAtMs: number;
      })
    | null
  >(null);
  const [profileTaskRefreshKey, setProfileTaskRefreshKey] = useState(0);
  const [initialCreationUrlState] = useState(() => readCreationUrlState());
  const handledInitialCreationUrlStateRef = useRef(false);
  const [initialPuzzleRuntimeUrlState] = useState(() =>
    readPuzzleRuntimeUrlState(),
  );
  const handledPuzzleRuntimeUrlStateKeyRef = useRef<string | null>(null);

  useEffect(() => {
    selectionStageRef.current = selectionStage;
  }, [selectionStage]);

  const resetRecommendRuntimeSelection = useCallback(() => {
    // 中文注释：推荐页嵌入运行态进入改造/创作后会清掉玩法 run；
    // 同步清空推荐选择，避免返回推荐页时复用已失效的运行态占位。
    recommendRuntimeStartRequestRef.current += 1;
    setActiveRecommendEntryKey(null);
    setActiveRecommendRuntimeKind(null);
    setActiveRecommendRuntimeError(null);
    setIsStartingRecommendEntry(false);
  }, []);

  const updatePendingDraftShelfItem = useCallback(
    (
      kind: Exclude<CreationWorkShelfKind, 'rpg'>,
      id: string | null | undefined,
      status: DraftGenerationNoticeStatus,
      seen = false,
      metadata?: { title?: string | null; summary?: string | null },
    ) => {
      const normalizedId = normalizeDraftNoticeId(id);
      if (!normalizedId) {
        return;
      }

      setPendingDraftShelfItems((current) => ({
        ...current,
        [kind]: (() => {
          const currentItems = current[kind] ?? {};
          return {
            ...currentItems,
            [normalizedId]: createPendingDraftShelfState(
              status,
              seen,
              new Date().toISOString(),
              metadata ?? currentItems[normalizedId],
            ),
          };
        })(),
      }));
    },
    [],
  );
  const clearPendingDraftShelfItem = useCallback(
    (
      kind: Exclude<CreationWorkShelfKind, 'rpg'>,
      id: string | null | undefined,
    ) => {
      const normalizedId = normalizeDraftNoticeId(id);
      if (!normalizedId) {
        return;
      }

      setPendingDraftShelfItems((current) => {
        const currentItems = current[kind];
        if (!currentItems?.[normalizedId]) {
          return current;
        }

        const nextItems = { ...currentItems };
        delete nextItems[normalizedId];
        return {
          ...current,
          [kind]: nextItems,
        };
      });
    },
    [],
  );

  const updateDraftGenerationNotices = useCallback(
    (
      keys: string[],
      status: DraftGenerationNoticeStatus,
      seen = false,
      message?: string | null,
    ) => {
      const uniqueKeys = Array.from(new Set(keys.filter(Boolean)));
      if (uniqueKeys.length === 0) {
        return;
      }

      const completedAtMs = status === 'ready' ? Date.now() : undefined;
      const normalizedMessage = message?.trim();
      setDraftGenerationNotices((current) => {
        const next = { ...current };
        for (const key of uniqueKeys) {
          next[key] =
            completedAtMs === undefined
              ? {
                  status,
                  seen,
                  ...(normalizedMessage ? { message: normalizedMessage } : {}),
                }
              : {
                  status,
                  seen,
                  completedAtMs,
                  ...(normalizedMessage ? { message: normalizedMessage } : {}),
                };
        }
        return next;
      });
    },
    [],
  );

  const markDraftNoticeSeen = useCallback((keys: string[]) => {
    const uniqueKeys = Array.from(new Set(keys.filter(Boolean)));
    if (uniqueKeys.length === 0) {
      return;
    }

    setDraftGenerationNotices((current) => {
      let changed = false;
      const next = { ...current };
      for (const key of uniqueKeys) {
        const notice = next[key];
        if (notice?.status === 'ready' && !notice.seen) {
          next[key] = { ...notice, seen: true };
          changed = true;
        }
      }
      return changed ? next : current;
    });
  }, []);

  const getDraftGenerationNotice = useCallback(
    (keys: string[]) => {
      for (const key of keys) {
        const notice = draftGenerationNotices[key];
        if (notice) {
          return notice;
        }
      }
      return null;
    },
    [draftGenerationNotices],
  );
  const getPendingDraftShelfState = useCallback(
    (kind: Exclude<CreationWorkShelfKind, 'rpg'>, keys: string[]) => {
      const entries = pendingDraftShelfItems[kind];
      if (!entries) {
        return null;
      }

      for (const key of keys) {
        const normalizedKey = normalizePendingDraftShelfLookupId(kind, key);
        const pending = normalizedKey ? entries[normalizedKey] : null;
        if (pending) {
          return pending;
        }
      }
      return null;
    },
    [pendingDraftShelfItems],
  );
  const markDraftGenerating = useCallback(
    (kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
      setPendingPlatformTaskCompletionDialog(null);
      setPendingPlatformTaskFailureDialog(null);
      updateDraftGenerationNotices(
        collectDraftNoticeKeys(kind, ids),
        'generating',
      );
    },
    [
      setPendingPlatformTaskCompletionDialog,
      setPendingPlatformTaskFailureDialog,
      updateDraftGenerationNotices,
    ],
  );
  const markDraftReady = useCallback(
    (
      kind: CreationWorkShelfKind,
      ids: Array<string | null | undefined>,
      viewedImmediately: boolean,
    ) => {
      setPendingPlatformTaskFailureDialog(null);
      updateDraftGenerationNotices(
        collectDraftNoticeKeys(kind, ids),
        'ready',
        viewedImmediately,
      );
      setProfileTaskRefreshKey((current) => current + 1);
      const completedAtMs = Date.now();
      setPendingPlatformTaskCompletionDialog({
        key: `${kind}:${collectDraftNoticeKeys(kind, ids).join('|')}:${completedAtMs}`,
        source: buildDraftCompletionDialogSource(kind, ids),
        message: '生成任务已完成，可以继续查看草稿。',
        completedAtMs,
      });
    },
    [
      setPendingPlatformTaskCompletionDialog,
      setPendingPlatformTaskFailureDialog,
      updateDraftGenerationNotices,
    ],
  );
  const markDraftFailed = useCallback(
    (
      kind: CreationWorkShelfKind,
      ids: Array<string | null | undefined>,
      errorMessage?: string | null,
      showFailureDialog = true,
    ) => {
      setPendingPlatformTaskCompletionDialog(null);
      const noticeKeys = collectDraftNoticeKeys(kind, ids);
      updateDraftGenerationNotices(noticeKeys, 'failed', false, errorMessage);
      const normalizedErrorMessage = errorMessage?.trim();
      if (normalizedErrorMessage && showFailureDialog) {
        const failedAtMs = Date.now();
        setPendingPlatformTaskFailureDialog({
          key: `draft-failure:${kind}:${noticeKeys.join('|')}:${failedAtMs}`,
          source: buildDraftCompletionDialogSource(kind, ids),
          message: normalizedErrorMessage,
          failedAtMs,
        });
      }
    },
    [
      setPendingPlatformTaskCompletionDialog,
      setPendingPlatformTaskFailureDialog,
      updateDraftGenerationNotices,
    ],
  );
  const markPendingDraftGenerating = useCallback(
    (
      kind: Exclude<CreationWorkShelfKind, 'rpg'>,
      id: string | null | undefined,
      metadata?: { title?: string | null; summary?: string | null },
    ) => {
      setPendingPlatformTaskCompletionDialog(null);
      setPendingPlatformTaskFailureDialog(null);
      updatePendingDraftShelfItem(kind, id, 'generating', false, metadata);
    },
    [
      setPendingPlatformTaskCompletionDialog,
      setPendingPlatformTaskFailureDialog,
      updatePendingDraftShelfItem,
    ],
  );
  const markPendingDraftReady = useCallback(
    (
      kind: Exclude<CreationWorkShelfKind, 'rpg'>,
      id: string | null | undefined,
      viewedImmediately: boolean,
    ) => {
      updatePendingDraftShelfItem(kind, id, 'ready', viewedImmediately);
    },
    [updatePendingDraftShelfItem],
  );
  const markPendingDraftFailed = useCallback(
    (
      kind: Exclude<CreationWorkShelfKind, 'rpg'>,
      id: string | null | undefined,
    ) => {
      updatePendingDraftShelfItem(kind, id, 'failed', false);
    },
    [updatePendingDraftShelfItem],
  );
  const getMatch3DBackgroundCompileTask = useCallback(
    (sessionId: string | null | undefined) => {
      const normalizedSessionId = normalizeDraftNoticeId(sessionId);
      return normalizedSessionId
        ? (match3dBackgroundCompileTasks[normalizedSessionId] ?? null)
        : null;
    },
    [match3dBackgroundCompileTasks],
  );
  const getPuzzleBackgroundCompileTask = useCallback(
    (sessionId: string | null | undefined) => {
      const normalizedSessionId = normalizeDraftNoticeId(sessionId);
      return normalizedSessionId
        ? (puzzleBackgroundCompileTasks[normalizedSessionId] ?? null)
        : null;
    },
    [puzzleBackgroundCompileTasks],
  );

  useEffect(() => {
    let cancelled = false;
    setCreationEntryConfigError(null);
    void fetchCreationEntryConfig()
      .then((config) => {
        if (!cancelled) {
          setCreationEntryConfig(config);
        }
      })
      .catch((error: unknown) => {
        if (!cancelled) {
          setCreationEntryConfigError(
            error instanceof Error ? error.message : '读取创作入口配置失败。',
          );
        }
      });
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    profileWalletLocalDeltaRef.current = 0;
    lastProfileDashboardSnapshotRef.current = null;
  }, [authUi?.user?.id]);

  const getPlatformProfileDashboardWithLocalWalletDelta = useCallback(
    async (options?: Parameters<typeof getPlatformProfileDashboard>[0]) => {
      const latestDashboard = await getPlatformProfileDashboard(options);
      const reconciledDelta =
        reconcileProfileWalletLocalDeltaWithServerDashboard(
          lastProfileDashboardSnapshotRef.current,
          latestDashboard,
          profileWalletLocalDeltaRef.current,
        );
      lastProfileDashboardSnapshotRef.current = latestDashboard;
      profileWalletLocalDeltaRef.current = reconciledDelta;
      return adjustProfileDashboardWalletBalance(
        latestDashboard,
        reconciledDelta,
      );
    },
    [],
  );

  const platformBootstrap = usePlatformEntryBootstrap({
    user: authUi?.user,
    canAccessProtectedData: authUi?.canAccessProtectedData,
    getProfileDashboard: getPlatformProfileDashboardWithLocalWalletDelta,
    handleContinueGame,
    hasInitialAgentSession,
  });
  const {
    canReadProtectedData: canRefreshPlatformDashboard,
    refreshProfileDashboard,
  } = platformBootstrap;
  const refreshPlatformDashboardSilently = useCallback(() => {
    if (!canRefreshPlatformDashboard) {
      return;
    }
    void refreshProfileDashboard().catch(() => undefined);
  }, [canRefreshPlatformDashboard, refreshProfileDashboard]);
  const adjustProfileWalletBalanceLocally = useCallback(
    (delta: number) => {
      if (!Number.isFinite(delta) || delta === 0) {
        return;
      }

      profileWalletLocalDeltaRef.current += Math.trunc(delta);
      platformBootstrap.setProfileDashboard((current) =>
        adjustProfileDashboardWalletBalance(current, delta),
      );
    },
    [platformBootstrap],
  );
  const entryNavigation = usePlatformEntryNavigation({
    setSelectionStage,
    setSelectedDetailEntry,
  });
  const { setPlatformTab } = platformBootstrap;
  const returnPlatformHomeAfterMissingWork = useCallback(() => {
    setPlatformTab('home');
    setSelectionStage('platform');
    if (!maybeAlertWorkNotFoundAndReturnHome()) {
      pushAppHistoryPath('/');
    }
  }, [setPlatformTab, setSelectionStage]);

  useEffect(() => {
    if (selectionStage === 'profile-feedback') {
      setPlatformTab('profile');
    }
  }, [selectionStage, setPlatformTab]);

  const openProfileFeedback = useCallback(() => {
    if (!authUi?.user) {
      authUi?.openLoginModal();
      return;
    }

    setPlatformTab('profile');
    setSelectionStage('profile-feedback');
  }, [authUi, setPlatformTab, setSelectionStage]);

  const enterCreateTab = useCallback(() => {
    // 只依赖稳定的 setter，避免把 bootstrap 对象的 render 级引用变化
    // 传导成 Agent session 恢复 effect 的重复触发。
    setPlatformTab('create');
  }, [setPlatformTab]);
  const enterDraftTab = useCallback(() => {
    setPlatformTab('saves');
  }, [setPlatformTab]);
  const markCreationFlowReturnToCreate = useCallback(() => {
    creationFlowReturnTargetRef.current = 'create';
  }, []);
  const markCreationFlowReturnToDraftShelf = useCallback(() => {
    creationFlowReturnTargetRef.current = 'draft-shelf';
  }, []);
  const returnToCreationFlowSource = useCallback(() => {
    const returnTarget = creationFlowReturnTargetRef.current;
    creationFlowReturnTargetRef.current = 'create';
    clearCreationUrlState();
    if (returnTarget === 'draft-shelf') {
      enterDraftTab();
    } else {
      enterCreateTab();
    }
    selectionStageRef.current = 'platform';
    setSelectionStage('platform');
  }, [enterCreateTab, enterDraftTab, setSelectionStage]);
  const shouldReturnToDraftShelf = useCallback(
    () => creationFlowReturnTargetRef.current === 'draft-shelf',
    [],
  );
  const returnToCreationCenterFromGeneration = useCallback(() => {
    markCreationFlowReturnToCreate();
    enterCreateTab();
    selectionStageRef.current = 'platform';
    setSelectionStage('platform');
  }, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);
  const isViewingMatch3DGeneration = useCallback((sessionId: string) => {
    return (
      selectionStageRef.current === 'match3d-generating' &&
      activeMatch3DGenerationSessionIdRef.current === sessionId
    );
  }, []);
  const isViewingPuzzleGeneration = useCallback((sessionId: string) => {
    return (
      selectionStageRef.current === 'puzzle-generating' &&
      activePuzzleGenerationSessionIdRef.current === sessionId
    );
  }, []);
  const isDraftNoticeGenerating = useCallback(
    (kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
      return collectDraftNoticeKeys(kind, ids).some(
        (key) => draftGenerationNotices[key]?.status === 'generating',
      );
    },
    [draftGenerationNotices],
  );
  const isDraftNoticeFailed = useCallback(
    (kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
      return collectDraftNoticeKeys(kind, ids).some(
        (key) => draftGenerationNotices[key]?.status === 'failed',
      );
    },
    [draftGenerationNotices],
  );
  const isDraftNoticeReadyUnread = useCallback(
    (kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
      return collectDraftNoticeKeys(kind, ids).some((key) => {
        const notice = draftGenerationNotices[key];
        return notice?.status === 'ready' && !notice.seen;
      });
    },
    [draftGenerationNotices],
  );
  const ensureEnoughDraftGenerationPointsFromServer = useCallback(
    async (pointsCost: number) => {
      try {
        const latestDashboard = await getPlatformProfileDashboardWithLocalWalletDelta(
          RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS,
        );
        platformBootstrap.setProfileDashboard(latestDashboard);
        const walletBalance = resolveProfileWalletBalance(latestDashboard);
        if (walletBalance >= pointsCost) {
          setDraftGenerationPointNotice(null);
          return true;
        }

        setDraftGenerationPointNotice({
          title: '泥点不足',
          message: `本次需要 ${pointsCost} 泥点，当前 ${walletBalance} 泥点。`,
        });
        return false;
      } catch {
        setDraftGenerationPointNotice({
          title: '读取泥点余额失败',
          message: '请稍后重试。',
        });
        return false;
      }
    },
    [getPlatformProfileDashboardWithLocalWalletDelta, platformBootstrap],
  );

  const resolveBigFishErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolvePuzzleErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolveMatch3DErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolveSquareHoleErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );
  const resolveBarkBattleErrorMessage = useCallback(
    (error: unknown, fallback: string) =>
      resolveRpgCreationErrorMessage(error, fallback),
    [],
  );

  const refreshBigFishShelf = useCallback(async () => {
    setIsBigFishLoadingLibrary(true);

    try {
      const worksResponse = await listBigFishWorks();
      setBigFishWorks(worksResponse.items);
      bigFishErrorSetterRef.current(null);
    } catch (error) {
      bigFishErrorSetterRef.current(
        resolveBigFishErrorMessage(error, '读取大鱼吃小鱼作品列表失败。'),
      );
    } finally {
      setIsBigFishLoadingLibrary(false);
    }
  }, [resolveBigFishErrorMessage]);

  const refreshBigFishGallery = useCallback(async () => {
    try {
      const galleryResponse = await listBigFishGallery();
      setBigFishGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch (error) {
      setBigFishGalleryEntries([]);
      bigFishErrorSetterRef.current(
        resolveBigFishErrorMessage(error, '读取大鱼吃小鱼广场失败。'),
      );
      return [];
    }
  }, [resolveBigFishErrorMessage]);

  const refreshMatch3DShelf = useCallback(async () => {
    setIsMatch3DLoadingLibrary(true);

    try {
      const worksResponse = await listMatch3DWorks();
      setMatch3DWorks(mapMatch3DWorksForRuntimeUi(worksResponse.items));
      match3DErrorSetterRef.current(null);
    } catch (error) {
      match3DErrorSetterRef.current(
        resolveMatch3DErrorMessage(error, '读取抓大鹅作品列表失败。'),
      );
    } finally {
      setIsMatch3DLoadingLibrary(false);
    }
  }, [resolveMatch3DErrorMessage]);

  const refreshMatch3DGallery = useCallback(async () => {
    try {
      const galleryResponse = await listMatch3DGallery();
      const items = mapMatch3DWorksForRuntimeUi(galleryResponse.items);
      setMatch3DGalleryEntries(items);
      return items;
    } catch {
      // 中文注释：公开广场是首页展示数据，失败时只降级为空列表；
      // 不写入创作错误态，避免挡住抓大鹅共创入口。
      setMatch3DGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshSquareHoleShelf = useCallback(async () => {
    setIsSquareHoleLoadingLibrary(true);

    try {
      const worksResponse = await listSquareHoleWorks();
      setSquareHoleWorks(worksResponse.items);
      squareHoleErrorSetterRef.current(null);
    } catch (error) {
      squareHoleErrorSetterRef.current(
        resolveSquareHoleErrorMessage(error, '读取方洞挑战作品列表失败。'),
      );
    } finally {
      setIsSquareHoleLoadingLibrary(false);
    }
  }, [resolveSquareHoleErrorMessage]);

  const refreshSquareHoleGallery = useCallback(async () => {
    try {
      const galleryResponse = await listSquareHoleGallery();
      setSquareHoleGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      setSquareHoleGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshJumpHopGallery = useCallback(async () => {
    try {
      const galleryResponse = await jumpHopClient.listGallery();
      setJumpHopGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      setJumpHopGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshJumpHopShelf = useCallback(async () => {
    if (!isJumpHopCreationVisible) {
      setJumpHopWorks([]);
      return [];
    }

    try {
      const worksResponse = await jumpHopClient.listWorks();
      setJumpHopWorks(worksResponse.items);
      return worksResponse.items;
    } catch {
      setJumpHopWorks([]);
      return [];
    }
  }, [isJumpHopCreationVisible]);

  const refreshPuzzleClearGallery = useCallback(async () => {
    try {
      const galleryResponse = await puzzleClearClient.listGallery();
      setPuzzleClearGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      setPuzzleClearGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshPuzzleClearShelf = useCallback(async () => {
    if (!isPuzzleClearCreationVisible) {
      setPuzzleClearWorks([]);
      return [];
    }

    try {
      const worksResponse = await puzzleClearClient.listWorks();
      setPuzzleClearWorks(worksResponse.items);
      return worksResponse.items;
    } catch (error) {
      setPuzzleClearWorks([]);
      setPuzzleClearError(
        resolvePuzzleErrorMessage(error, '读取拼消消作品列表失败。'),
      );
      return [];
    }
  }, [isPuzzleClearCreationVisible]);

  const refreshWoodenFishGallery = useCallback(async () => {
    try {
      const galleryResponse = await woodenFishClient.listGallery();
      setWoodenFishGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      setWoodenFishGalleryEntries([]);
      return [];
    }
  }, []);

  const refreshWoodenFishShelf = useCallback(async () => {
    try {
      const worksResponse = await woodenFishClient.listWorks();
      setWoodenFishWorks(worksResponse.items);
      return worksResponse.items;
    } catch (error) {
      setWoodenFishWorks([]);
      setWoodenFishError(
        resolvePuzzleErrorMessage(error, '读取敲木鱼作品列表失败。'),
      );
      return [];
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshPuzzleShelf = useCallback(async () => {
    setIsPuzzleLoadingLibrary(true);

    try {
      const worksResponse = await listPuzzleWorks();
      setPuzzleWorks(worksResponse.items);
      setPuzzleShelfError(null);
    } catch (error) {
      setPuzzleShelfError(
        resolvePuzzleErrorMessage(error, '读取拼图作品列表失败。'),
      );
    } finally {
      setIsPuzzleLoadingLibrary(false);
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshPuzzleGallery = useCallback(async () => {
    try {
      const galleryResponse = await listPuzzleGallery();
      setPuzzleGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch (error) {
      setPuzzleGalleryEntries([]);
      puzzleErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取拼图广场失败。'),
      );
      return [];
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshVisualNovelShelf = useCallback(async () => {
    if (!isVisualNovelCreationOpen) {
      setVisualNovelWorks([]);
      visualNovelErrorSetterRef.current(null);
      return [];
    }

    setIsVisualNovelLoadingLibrary(true);

    try {
      const worksResponse = await listVisualNovelWorks();
      setVisualNovelWorks(worksResponse.works);
      visualNovelErrorSetterRef.current(null);
      return worksResponse.works;
    } catch (error) {
      visualNovelErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取视觉小说作品列表失败。'),
      );
      return [];
    } finally {
      setIsVisualNovelLoadingLibrary(false);
    }
  }, [isVisualNovelCreationOpen, resolvePuzzleErrorMessage]);

  const refreshVisualNovelGallery = useCallback(async () => {
    if (!isVisualNovelCreationOpen) {
      setVisualNovelGalleryEntries([]);
      visualNovelErrorSetterRef.current(null);
      return [];
    }

    try {
      const galleryResponse = await listVisualNovelGallery();
      setVisualNovelGalleryEntries(galleryResponse.works);
      return galleryResponse.works;
    } catch (error) {
      setVisualNovelGalleryEntries([]);
      visualNovelErrorSetterRef.current(
        resolvePuzzleErrorMessage(error, '读取视觉小说广场失败。'),
      );
      return [];
    }
  }, [isVisualNovelCreationOpen, resolvePuzzleErrorMessage]);

  const refreshBarkBattleShelf = useCallback(async () => {
    setIsBarkBattleBusy(true);

    try {
      const worksResponse = await listBarkBattleWorks();
      const works = mergeBarkBattleWorksByWorkId(worksResponse.items);
      setBarkBattleWorks((current) =>
        mergeBarkBattleWorksByWorkId([
          ...works,
          ...current.filter((item) =>
            shouldPreserveLocalBarkBattleWorkOnRefresh(item, works),
          ),
        ]),
      );
      setBarkBattleError(null);
      return works;
    } catch (error) {
      setBarkBattleError(
        resolvePuzzleErrorMessage(error, '读取汪汪声浪作品列表失败。'),
      );
      return [];
    } finally {
      setIsBarkBattleBusy(false);
    }
  }, [resolvePuzzleErrorMessage]);

  const refreshBarkBattleGallery = useCallback(async () => {
    try {
      const galleryResponse = await listBarkBattleGallery();
      setBarkBattleGalleryEntries(galleryResponse.items);
      return galleryResponse.items;
    } catch {
      // 中文注释：公开广场读取失败只降级为空列表，避免阻断平台首页。
      setBarkBattleGalleryEntries([]);
      return [];
    }
  }, []);

  const syncUpdatedPublicWorkDetail = useCallback(
    (updatedEntry: PlatformPublicGalleryCard) => {
      setSelectedPublicWorkDetail((current) =>
        current && isSamePlatformPublicGalleryEntry(current, updatedEntry)
          ? updatedEntry
          : current,
      );
    },
    [],
  );

  const handleRpgDraftGenerationStarted = useCallback(
    (sessionId: string) => {
      markDraftGenerating('rpg', [sessionId, `draft:${sessionId}`]);
    },
    [markDraftGenerating],
  );
  const updateBarkBattleWorkCaches = useCallback(
    (updated: BarkBattleWorkSummary) => {
      setBarkBattleWorks((current) => {
        const deduped = mergeBarkBattleWorksByWorkId(current);
        const hasExisting = deduped.some(
          (item) => item.workId === updated.workId,
        );
        if (hasExisting) {
          return deduped.map((item) =>
            mergeBarkBattleWorkSummary(item, updated),
          );
        }
        return [updated, ...deduped];
      });
      setBarkBattleGalleryEntries((current) => {
        const hasExisting = current.some(
          (item) => item.workId === updated.workId,
        );
        if (updated.status !== 'published') {
          return hasExisting
            ? current.map((item) => mergeBarkBattleWorkSummary(item, updated))
            : current;
        }
        if (hasExisting) {
          return current.map((item) =>
            mergeBarkBattleWorkSummary(item, updated),
          );
        }
        return [updated, ...current];
      });
      if (updated.status === 'published') {
        syncUpdatedPublicWorkDetail(
          mapBarkBattleWorkToPublicWorkDetail(updated),
        );
      }
    },
    [syncUpdatedPublicWorkDetail],
  );

  const handleRpgDraftGenerationCompleted = useCallback(
    ({
      sessionId,
      profileId,
      viewedImmediately,
    }: {
      sessionId: string;
      profileId: string | null;
      viewedImmediately: boolean;
    }) => {
      markDraftReady(
        'rpg',
        [sessionId, `draft:${sessionId}`, profileId],
        viewedImmediately,
      );
      void platformBootstrap.refreshCustomWorldWorks().catch(() => undefined);
    },
    [markDraftReady, platformBootstrap],
  );

  const refreshBabyObjectMatchShelf = useCallback(async () => {
    setBabyObjectMatchDrafts(await listLocalBabyObjectMatchDrafts());
  }, []);

  useEffect(() => {
    if (!platformBootstrap.canReadProtectedData) {
      setBarkBattleWorks([]);
      return;
    }

    if (
      platformBootstrap.platformTab === 'create' ||
      selectionStage === 'platform'
    ) {
      void refreshBarkBattleShelf();
    }
  }, [
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    refreshBarkBattleShelf,
    selectionStage,
  ]);

  useEffect(() => {
    if (!platformBootstrap.canReadProtectedData) {
      setJumpHopWorks([]);
      return;
    }

    if (
      platformBootstrap.platformTab === 'create' ||
      selectionStage === 'platform'
    ) {
      void refreshJumpHopShelf();
    }
  }, [
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    refreshJumpHopShelf,
    selectionStage,
  ]);

  const sessionController = useRpgCreationSessionController({
    userId: authUi?.user?.id,
    openLoginModal: authUi?.openLoginModal,
    selectionStage,
    setSelectionStage,
    enterCreateTab,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onDraftGenerationStarted: handleRpgDraftGenerationStarted,
    onDraftGenerationCompleted: handleRpgDraftGenerationCompleted,
  });

  useRpgCreationAgentOperationPolling({
    activeAgentSessionId: sessionController.activeAgentSessionId,
    activeAgentOperationId: sessionController.activeAgentOperationId,
    userId: authUi?.user?.id,
    setAgentOperation: sessionController.setAgentOperation,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot,
  });

  const autosaveCoordinator = useRpgCreationResultAutosave({
    selectionStage,
    activeAgentSessionId: sessionController.activeAgentSessionId,
    generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile,
    isAgentDraftResultView: sessionController.isAgentDraftResultView,
    userId: authUi?.user?.id,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
    setAgentOperation: sessionController.setAgentOperation,
    setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries,
    setSelectedDetailEntry,
    refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentSessionSnapshot: sessionController.syncAgentSessionSnapshot,
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    buildDraftResultProfile: (view) =>
      rpgCreationPreviewAdapter.buildPreviewFromResultView(view),
  });

  const detailNavigation = usePlatformEntryLibraryDetail({
    userId: authUi?.user?.id,
    selectedDetailEntry,
    setSelectedDetailEntry,
    savedCustomWorldEntries: platformBootstrap.savedCustomWorldEntries,
    setSavedCustomWorldEntries: platformBootstrap.setSavedCustomWorldEntries,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
    setCustomWorldError: sessionController.setCustomWorldError,
    setCustomWorldAutoSaveError:
      autosaveCoordinator.setCustomWorldAutoSaveError,
    setCustomWorldAutoSaveState:
      autosaveCoordinator.setCustomWorldAutoSaveState,
    setCustomWorldGenerationViewSource:
      sessionController.setCustomWorldGenerationViewSource,
    setCustomWorldResultViewSource:
      sessionController.setCustomWorldResultViewSource,
    setSelectionStage,
    setPlatformTabToCreate: enterCreateTab,
    setPlatformTabToDraft: enterDraftTab,
    setPlatformError: platformBootstrap.setPlatformError,
    appendBrowseHistoryEntry: platformBootstrap.appendBrowseHistoryEntry,
    refreshCustomWorldWorks: platformBootstrap.refreshCustomWorldWorks,
    refreshPublishedGallery: platformBootstrap.refreshPublishedGallery,
    persistAgentUiState: sessionController.persistAgentUiState,
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    buildDraftResultProfile: (view) =>
      rpgCreationPreviewAdapter.buildPreviewFromResultView(view),
    suppressAgentDraftResultAutoOpen:
      sessionController.suppressAgentDraftResultAutoOpen,
    releaseAgentDraftResultAutoOpenSuppression:
      sessionController.releaseAgentDraftResultAutoOpenSuppression,
    resetAutoSaveTrackingToIdle:
      autosaveCoordinator.resetAutoSaveTrackingToIdle,
    markAutoSavedProfile: autosaveCoordinator.markAutoSavedProfile,
  });

  const enterWorldCoordinator = useRpgCreationEnterWorld({
    isAgentDraftResultView: sessionController.isAgentDraftResultView,
    activeAgentSessionId: sessionController.activeAgentSessionId,
    currentAgentSessionStage: sessionController.agentSession?.stage ?? null,
    generatedCustomWorldProfile: sessionController.generatedCustomWorldProfile,
    handleCustomWorldSelect,
    syncAgentDraftResultProfile:
      autosaveCoordinator.syncAgentDraftResultProfile,
    executePublishWorld: async () => {
      const latestSession = await autosaveCoordinator.executeAgentActionAndWait(
        {
          action: 'publish_world',
        },
      );
      // 发布动作会在后端同步 gallery 投影；前端发布完成后立即刷新首页/分类页共用的公开作品列表。
      await Promise.allSettled([
        platformBootstrap.refreshPublishedGallery(),
        platformBootstrap.refreshCustomWorldWorks(),
        isBigFishCreationVisible
          ? refreshBigFishGallery()
          : Promise.resolve([] as BigFishWorkSummary[]),
        refreshMatch3DGallery(),
        refreshPuzzleGallery(),
        refreshBarkBattleGallery(),
      ]);
      return latestSession;
    },
    syncAgentCreationResultView: sessionController.syncAgentCreationResultView,
    setGeneratedCustomWorldProfile:
      sessionController.setGeneratedCustomWorldProfile,
  });

  const previewCustomWorldCharacters = useMemo(
    () =>
      sessionController.generatedCustomWorldProfile
        ? buildCustomWorldPlayableCharacters(
            sessionController.generatedCustomWorldProfile,
          )
        : [],
    [sessionController.generatedCustomWorldProfile],
  );
  const agentResultPreview =
    sessionController.agentSession?.resultPreview ?? null;
  const agentResultPreviewBlockers = useMemo(
    () => agentResultPreview?.blockers ?? [],
    [agentResultPreview],
  );
  const agentResultPublishGateView = useMemo(
    () =>
      buildAgentResultPublishGateView(
        sessionController.generatedCustomWorldProfile,
        agentResultPreviewBlockers,
        Boolean(agentResultPreview?.publishReady),
      ),
    [
      agentResultPreview?.publishReady,
      agentResultPreviewBlockers,
      sessionController.generatedCustomWorldProfile,
    ],
  );
  const agentResultPreviewQualityFindings = useMemo(
    () => agentResultPreview?.qualityFindings ?? [],
    [agentResultPreview],
  );

  const openPublishShareModal = useCallback(
    (payload: PublishShareModalPayload) => {
      const publicWorkCode = payload.publicWorkCode.trim();
      if (!publicWorkCode) {
        return;
      }

      setPublishSharePayload({
        ...payload,
        publicWorkCode,
        title: payload.title.trim() || '我的作品',
      });
    },
    [],
  );

  const openRecommendShareModal = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      const publicWorkCode = resolvePlatformPublicWorkCode(entry)?.trim();
      if (!publicWorkCode) {
        return;
      }

      openPublishShareModal({
        title: entry.worldName,
        publicWorkCode,
        stage: resolveRecommendEntryShareStage(entry),
      });
    },
    [openPublishShareModal],
  );

  const openRpgPublishShareModal = useCallback(
    async (profile: CustomWorldProfile | null | undefined) => {
      const profileId = profile?.id?.trim();
      if (!profileId) {
        return;
      }
      const profileName = profile?.name?.trim() || '我的作品';

      const galleryEntries = await platformBootstrap
        .refreshPublishedGallery()
        .catch(() => [] as CustomWorldGalleryCard[]);
      const galleryEntry = galleryEntries.find(
        (entry) => entry.profileId === profileId,
      );
      const publicWorkCode = galleryEntry?.publicWorkCode?.trim();
      if (!publicWorkCode) {
        return;
      }

      openPublishShareModal({
        title: galleryEntry?.worldName || profileName,
        publicWorkCode,
        stage: 'work-detail',
      });
    },
    [openPublishShareModal, platformBootstrap],
  );
  const agentResultPreviewSourceLabel = useMemo(() => {
    if (!agentResultPreview?.source) {
      return null;
    }
    if (agentResultPreview.source === 'published_profile') {
      return '已发布世界';
    }
    if (agentResultPreview.source === 'session_preview') {
      return '会话预览';
    }
    return '服务端预览';
  }, [agentResultPreview]);
  const featuredGalleryEntries = useMemo(() => {
    const bigFishPublicEntries = isBigFishCreationVisible
      ? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
      : [];
    const babyObjectMatchPublicEntries = isBabyObjectMatchVisible
      ? babyObjectMatchDrafts
          .filter((draft) => draft.publicationStatus === 'published')
          .map(mapBabyObjectMatchDraftToPlatformGalleryCard)
      : [];
    const match3dPublicEntries = match3dGalleryEntries.map(
      mapMatch3DWorkToPublicWorkDetail,
    );
    const puzzlePublicEntries = puzzleGalleryEntries.map(
      mapPuzzleWorkToPlatformGalleryCard,
    );
    const barkBattlePublicEntries =
      barkBattleGalleryEntries.length > 0
        ? barkBattleGalleryEntries.map(mapBarkBattleWorkToPlatformGalleryCard)
        : barkBattleWorks
            .filter((work) => work.status === 'published')
            .map(mapBarkBattleWorkToPlatformGalleryCard);
    const squareHolePublicEntries = squareHoleGalleryEntries.map(
      mapSquareHoleWorkToPlatformGalleryCard,
    );
    const jumpHopPublicEntries = jumpHopGalleryEntries.map(
      mapJumpHopWorkToPlatformGalleryCard,
    );
    const puzzleClearPublicEntries = puzzleClearGalleryEntries.map(
      mapPuzzleClearWorkToPlatformGalleryCard,
    );
    const woodenFishPublicEntries = woodenFishGalleryEntries.map(
      mapWoodenFishWorkToPlatformGalleryCard,
    );
    const visualNovelPublicEntries = visualNovelGalleryEntries.map(
      mapVisualNovelWorkToPlatformGalleryCard,
    );
    return mergePlatformPublicGalleryEntries(
      platformBootstrap.publishedGalleryEntries,
      [
        ...bigFishPublicEntries,
        ...match3dPublicEntries,
        ...puzzlePublicEntries,
        ...barkBattlePublicEntries,
        ...squareHolePublicEntries,
        ...jumpHopPublicEntries,
        ...puzzleClearPublicEntries,
        ...woodenFishPublicEntries,
        ...(isVisualNovelCreationOpen ? visualNovelPublicEntries : []),
        ...babyObjectMatchPublicEntries,
      ],
    ).slice(0, 6);
  }, [
    babyObjectMatchDrafts,
    isBigFishCreationVisible,
    isBabyObjectMatchVisible,
    isVisualNovelCreationOpen,
    bigFishGalleryEntries,
    jumpHopGalleryEntries,
    match3dGalleryEntries,
    platformBootstrap.publishedGalleryEntries,
    puzzleClearGalleryEntries,
    puzzleGalleryEntries,
    barkBattleGalleryEntries,
    barkBattleWorks,
    squareHoleGalleryEntries,
    visualNovelGalleryEntries,
    woodenFishGalleryEntries,
  ]);
  const latestGalleryEntries = useMemo(
    () =>
      mergePlatformPublicGalleryEntries(
        platformBootstrap.publishedGalleryEntries,
        [
          ...(isBigFishCreationVisible
            ? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
            : []),
          ...match3dGalleryEntries.map(mapMatch3DWorkToPublicWorkDetail),
          ...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
          ...puzzleClearGalleryEntries.map(
            mapPuzzleClearWorkToPlatformGalleryCard,
          ),
          ...barkBattleGalleryEntries.map(
            mapBarkBattleWorkToPlatformGalleryCard,
          ),
          ...jumpHopGalleryEntries.map(mapJumpHopWorkToPlatformGalleryCard),
          ...(barkBattleGalleryEntries.length === 0
            ? barkBattleWorks
                .filter((work) => work.status === 'published')
                .map(mapBarkBattleWorkToPlatformGalleryCard)
            : []),
          ...woodenFishGalleryEntries.map(
            mapWoodenFishWorkToPlatformGalleryCard,
          ),
          ...squareHoleGalleryEntries.map(
            mapSquareHoleWorkToPlatformGalleryCard,
          ),
          ...(isVisualNovelCreationOpen
            ? visualNovelGalleryEntries.map(
                mapVisualNovelWorkToPlatformGalleryCard,
              )
            : []),
          ...(isBabyObjectMatchVisible
            ? babyObjectMatchDrafts
                .filter((draft) => draft.publicationStatus === 'published')
                .map(mapBabyObjectMatchDraftToPlatformGalleryCard)
            : []),
        ],
      ),
    [
      babyObjectMatchDrafts,
      isBabyObjectMatchVisible,
      isBigFishCreationVisible,
      isVisualNovelCreationOpen,
      bigFishGalleryEntries,
      jumpHopGalleryEntries,
      match3dGalleryEntries,
      platformBootstrap.publishedGalleryEntries,
      puzzleClearGalleryEntries,
      puzzleGalleryEntries,
      squareHoleGalleryEntries,
      visualNovelGalleryEntries,
      barkBattleGalleryEntries,
      barkBattleWorks,
      woodenFishGalleryEntries,
    ],
  );
  const recommendRuntimeEntries = useMemo(() => {
    return buildPlatformRecommendedEntries({
      featuredEntries: filterGeneralPublicWorks(featuredGalleryEntries),
      latestEntries: filterGeneralPublicWorks(latestGalleryEntries),
    });
  }, [featuredGalleryEntries, latestGalleryEntries]);

  const creationHubItems = useMemo<CustomWorldWorkSummary[]>(
    () =>
      platformBootstrap.customWorldWorkEntries.length > 0
        ? platformBootstrap.customWorldWorkEntries
        : buildCreationHubFallbackItems(
            platformBootstrap.savedCustomWorldEntries,
          ),
    [
      platformBootstrap.customWorldWorkEntries,
      platformBootstrap.savedCustomWorldEntries,
    ],
  );
  const bigFishShelfItems = useMemo(
    () => [
      ...buildPendingBigFishWorks(
        pendingDraftShelfItems['big-fish'],
        bigFishWorks,
      ),
      ...bigFishWorks,
    ],
    [bigFishWorks, pendingDraftShelfItems],
  );
  const jumpHopShelfItems = useMemo(
    () => [
      ...buildPendingJumpHopWorks(
        pendingDraftShelfItems['jump-hop'],
        jumpHopWorks,
      ),
      ...jumpHopWorks,
    ],
    [jumpHopWorks, pendingDraftShelfItems],
  );
  const puzzleClearShelfItems = useMemo(
    () => [
      ...buildPendingPuzzleClearWorks(
        pendingDraftShelfItems['puzzle-clear'],
        puzzleClearWorks,
      ),
      ...puzzleClearWorks,
    ],
    [pendingDraftShelfItems, puzzleClearWorks],
  );
  const woodenFishShelfItems = useMemo(
    () => [
      ...buildPendingWoodenFishWorks(
        pendingDraftShelfItems['wooden-fish'],
        woodenFishWorks,
      ),
      ...woodenFishWorks,
    ],
    [pendingDraftShelfItems, woodenFishWorks],
  );
  const match3dShelfItems = useMemo(
    () => [
      ...buildPendingMatch3DWorks(pendingDraftShelfItems.match3d, match3dWorks),
      ...match3dWorks,
    ],
    [match3dWorks, pendingDraftShelfItems],
  );
  const squareHoleShelfItems = useMemo(
    () => [
      ...buildPendingSquareHoleWorks(
        pendingDraftShelfItems['square-hole'],
        squareHoleWorks,
      ),
      ...squareHoleWorks,
    ],
    [pendingDraftShelfItems, squareHoleWorks],
  );
  const puzzleShelfItems = useMemo(
    () => [
      ...buildPendingPuzzleWorks(pendingDraftShelfItems.puzzle, puzzleWorks),
      ...puzzleWorks,
    ],
    [pendingDraftShelfItems, puzzleWorks],
  );
  const visualNovelShelfItems = useMemo(
    () =>
      isVisualNovelCreationOpen
        ? [
            ...buildPendingVisualNovelWorks(
              pendingDraftShelfItems['visual-novel'],
              visualNovelWorks,
            ),
            ...visualNovelWorks,
          ]
        : [],
    [isVisualNovelCreationOpen, pendingDraftShelfItems, visualNovelWorks],
  );
  const barkBattleShelfItems = useMemo(
    () => [
      ...buildPendingBarkBattleWorks(
        pendingDraftShelfItems['bark-battle'],
        barkBattleWorks,
      ),
      ...barkBattleWorks,
    ],
    [barkBattleWorks, pendingDraftShelfItems],
  );
  const getCreationWorkShelfState = useCallback(
    (item: CreationWorkShelfItem) => {
      const noticeKeys = getGenerationNoticeShelfKeys(item);
      const notice = getDraftGenerationNotice(noticeKeys);
      if (notice?.status === 'failed') {
        const failedSummary = buildDraftFailedShelfSummary(item.source.kind);
        const pending =
          item.source.kind === 'rpg'
            ? null
            : getPendingDraftShelfState(item.source.kind, noticeKeys);
        const pendingSummary = pending?.summary?.trim();
        return {
          isGenerating: false,
          hasGenerationFailure: true,
          generationFailureSummary: failedSummary,
          hasUnreadUpdate: false,
          suppressPersistedGenerating: true,
          titleOverride:
            item.source.kind === 'puzzle' &&
            item.status === 'draft' &&
            !item.source.item.workTitle?.trim()
              ? '拼图草稿'
              : undefined,
          summaryOverride: isDraftShelfSummaryPlaceholder(item.summary)
            ? (pendingSummary ?? failedSummary)
            : undefined,
        };
      }
      if (
        item.source.kind === 'puzzle' &&
        isPersistedDraftFailed(item.source.item.generationStatus)
      ) {
        const failedSummary = buildDraftFailedShelfSummary('puzzle');
        return {
          isGenerating: false,
          hasGenerationFailure: true,
          generationFailureSummary: failedSummary,
          hasUnreadUpdate: false,
          suppressPersistedGenerating: true,
          titleOverride:
            item.status === 'draft' && !item.source.item.workTitle?.trim()
              ? '拼图草稿'
              : undefined,
          summaryOverride: isDraftShelfSummaryPlaceholder(item.summary)
            ? failedSummary
            : undefined,
        };
      }
      const isNoticeGenerating =
        notice?.status === 'generating' &&
        (item.source.kind !== 'puzzle' ||
          !resolvePuzzleWorkCoverImageSrc(item.source.item));
      return {
        isGenerating: isNoticeGenerating || item.isGenerating === true,
        hasUnreadUpdate: notice?.status === 'ready' && !notice.seen,
      };
    },
    [getDraftGenerationNotice, getPendingDraftShelfState],
  );
  const visibleDraftNoticeKeys = useMemo(
    () => [
      ...creationHubItems.flatMap((item) =>
        collectDraftNoticeKeys('rpg', [
          item.workId,
          item.sessionId,
          item.profileId,
        ]),
      ),
      ...bigFishShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('big-fish', [item.workId, item.sourceSessionId]),
      ),
      ...jumpHopShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('jump-hop', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      ),
      ...puzzleClearShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('puzzle-clear', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      ),
      ...woodenFishShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('wooden-fish', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      ),
      ...match3dShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('match3d', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      ),
      ...(isSquareHoleCreationVisible
        ? squareHoleShelfItems.flatMap((item) =>
            collectDraftNoticeKeys('square-hole', [
              item.workId,
              item.profileId,
              item.sourceSessionId,
            ]),
          )
        : []),
      ...puzzleShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('puzzle', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
          buildPuzzleResultWorkId(item.sourceSessionId),
          buildPuzzleResultProfileId(item.sourceSessionId),
        ]),
      ),
      ...visualNovelShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('visual-novel', [item.profileId]),
      ),
      ...barkBattleShelfItems.flatMap((item) =>
        collectDraftNoticeKeys('bark-battle', [item.workId, item.draftId]),
      ),
      ...babyObjectMatchDrafts.flatMap((item) =>
        collectDraftNoticeKeys('baby-object-match', [
          item.profileId,
          item.draftId,
        ]),
      ),
    ],
    [
      babyObjectMatchDrafts,
      barkBattleShelfItems,
      bigFishShelfItems,
      jumpHopShelfItems,
      puzzleClearShelfItems,
      woodenFishShelfItems,
      creationHubItems,
      isSquareHoleCreationVisible,
      match3dShelfItems,
      puzzleShelfItems,
      squareHoleShelfItems,
      visualNovelShelfItems,
    ],
  );
  const hasUnreadDraftUpdates = useMemo(
    () =>
      visibleDraftNoticeKeys.some((key) => {
        const notice = draftGenerationNotices[key];
        return notice?.status === 'ready' && !notice.seen;
      }),
    [draftGenerationNotices, visibleDraftNoticeKeys],
  );
  const resultViewError =
    autosaveCoordinator.customWorldAutoSaveError ??
    sessionController.customWorldError;
  const isSelectedPublicWorkOwned = Boolean(
    authUi?.user?.id &&
      selectedPublicWorkDetail?.ownerUserId === authUi.user.id,
  );
  const selectedPublicWorkActionMode = isSelectedPublicWorkOwned
    ? 'edit'
    : 'remix';

  useEffect(() => {
    if (
      selectionStage === 'custom-world-result' &&
      !sessionController.generatedCustomWorldProfile
    ) {
      setSelectionStage(selectedDetailEntry ? 'detail' : 'platform');
    }
  }, [
    selectedDetailEntry,
    selectionStage,
    sessionController.generatedCustomWorldProfile,
    setSelectionStage,
  ]);

  useEffect(() => {
    const activeGenerationState = resolveMiniGameGenerationProgressTickState(
      selectionStage,
      {
        puzzle: puzzleGenerationState,
        'big-fish': bigFishGenerationState,
        'square-hole': squareHoleGenerationState,
        match3d: match3dGenerationState,
        'baby-object-match': babyObjectMatchGenerationState,
        'jump-hop': jumpHopGenerationState,
        'puzzle-clear': puzzleClearGenerationState,
        'wooden-fish': woodenFishGenerationState,
      },
    );
    const shouldTickProgress =
      selectionStage === 'visual-novel-generating'
        ? visualNovelGenerationStartedAtMs != null &&
          visualNovelGenerationPhase !== 'ready' &&
          visualNovelGenerationPhase !== 'failed'
        : activeGenerationState != null &&
          activeGenerationState.phase !== 'ready' &&
          activeGenerationState.phase !== 'failed';

    if (!shouldTickProgress) {
      return undefined;
    }

    setMiniGameGenerationProgressNowMs(Date.now());
    const timerId = window.setInterval(() => {
      setMiniGameGenerationProgressNowMs(Date.now());
    }, 500);

    return () => window.clearInterval(timerId);
  }, [
    babyObjectMatchGenerationState,
    bigFishGenerationState,
    jumpHopGenerationState,
    match3dGenerationState,
    puzzleGenerationState,
    puzzleClearGenerationState,
    squareHoleGenerationState,
    woodenFishGenerationState,
    selectionStage,
    visualNovelGenerationPhase,
    visualNovelGenerationStartedAtMs,
  ]);

  const runProtectedAction = useCallback(
    (action: () => void) => {
      if (!authUi?.requireAuth) {
        action();
        return;
      }

      authUi.requireAuth(action);
    },
    [authUi],
  );

  const savePuzzleOnboardingDraft = useCallback(async () => {
    if (!puzzleOnboardingDraft || isPuzzleOnboardingSaving) {
      return;
    }

    setIsPuzzleOnboardingSaving(true);
    setPuzzleOnboardingError(null);
    try {
      const response = await savePuzzleOnboardingWork({
        promptText: puzzleOnboardingDraft.promptText,
        item: puzzleOnboardingDraft.item,
      });
      setPuzzleWorks((current) => [response.item, ...current]);
      setSelectedPuzzleDetail(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      setPuzzleOnboardingDraft(null);
      setPuzzleOnboardingPrompt('');
      setPuzzleOnboardingPhase('input');
      platformBootstrap.setPlatformTab('home');
      setSelectionStage('platform');
      clearPuzzleRuntimeUrlState();
      void refreshPuzzleShelf();
    } catch (error) {
      setPuzzleOnboardingError(
        resolvePuzzleErrorMessage(error, '保存新手引导拼图失败。'),
      );
    } finally {
      setIsPuzzleOnboardingSaving(false);
    }
  }, [
    isPuzzleOnboardingSaving,
    platformBootstrap,
    puzzleOnboardingDraft,
    refreshPuzzleShelf,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const requestPuzzleOnboardingLogin = useCallback(() => {
    if (isPuzzleOnboardingSaving) {
      return;
    }
    authUi?.openLoginModal(() => {
      void savePuzzleOnboardingDraft();
    });
  }, [authUi, isPuzzleOnboardingSaving, savePuzzleOnboardingDraft]);

  const skipPuzzleOnboarding = useCallback(() => {
    markPuzzleOnboardingSeen();
    setPuzzleOnboardingDraft(null);
    setPuzzleOnboardingPrompt('');
    setPuzzleOnboardingPhase('input');
    setPuzzleOnboardingError(null);
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setSelectedPuzzleDetail(null);
    platformBootstrap.setPlatformTab('home');
    setSelectionStage('platform');
    clearPuzzleRuntimeUrlState();
  }, [platformBootstrap, setSelectionStage]);

  useEffect(() => {
    if (
      !authUi ||
      authUi?.user ||
      selectionStage !== 'platform' ||
      hasSeenPuzzleOnboarding()
    ) {
      return;
    }

    setPuzzleOnboardingPhase('input');
    setPuzzleOnboardingError(null);
    setSelectionStage('puzzle-onboarding');
  }, [authUi, authUi?.user, selectionStage, setSelectionStage]);

  const submitPuzzleOnboardingPrompt = useCallback(async () => {
    const promptText = puzzleOnboardingPrompt.trim();
    if (!promptText || puzzleOnboardingPhase === 'generating') {
      return;
    }

    setPuzzleOnboardingPhase('generating');
    setPuzzleOnboardingError(null);
    try {
      let item: PuzzleWorkSummary;
      try {
        const response = await generatePuzzleOnboardingWork({ promptText });
        item = {
          ...response.item,
          levels:
            response.item.levels && response.item.levels.length > 0
              ? response.item.levels
              : [response.level],
        };
      } catch (error) {
        if (!shouldUseLocalPuzzleOnboardingFallback(error)) {
          throw error;
        }

        // 中文注释：旧后端或资源路由尚未更新时，首访体验继续用前端临时图面兜底，不写入作品库。
        item = buildPuzzleOnboardingFallbackWork(promptText);
      }
      setPuzzleOnboardingDraft({ promptText, item });
      setSelectedPuzzleDetail(item);
      setPuzzleOnboardingPhase('generated');
      markPuzzleOnboardingSeen();
      window.setTimeout(() => {
        setPuzzleRun(startLocalPuzzleRun(item));
        setPuzzleRuntimeAuthMode('default');
        setPuzzleRuntimeReturnStage('platform');
        openPuzzleRuntimeStage(
          setSelectionStage,
          buildPuzzleDraftRuntimeUrlState(item, null),
        );
      }, PUZZLE_ONBOARDING_GENERATED_DELAY_MS);
    } catch (error) {
      setPuzzleOnboardingPhase('input');
      setPuzzleOnboardingError(
        resolvePuzzleErrorMessage(error, '生成新手引导拼图失败。'),
      );
    }
  }, [
    puzzleOnboardingPhase,
    puzzleOnboardingPrompt,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const requestDeleteCreationWork = useCallback(
    (confirmation: DeleteCreationWorkConfirmation) => {
      if (deletingCreationWorkId) {
        return;
      }

      runProtectedAction(() => {
        setPendingDeleteCreationWork(confirmation);
      });
    },
    [deletingCreationWorkId, runProtectedAction],
  );

  const closeDeleteCreationWorkConfirmation = useCallback(() => {
    if (deletingCreationWorkId) {
      return;
    }

    setPendingDeleteCreationWork(null);
  }, [deletingCreationWorkId]);

  const confirmDeleteCreationWork = useCallback(() => {
    const confirmation = pendingDeleteCreationWork;
    if (!confirmation || deletingCreationWorkId) {
      return;
    }

    setPendingDeleteCreationWork(null);
    confirmation.run();
  }, [deletingCreationWorkId, pendingDeleteCreationWork]);

  const prepareCreationLaunch = useCallback(() => {
    if (sessionController.isCreatingAgentSession) {
      return false;
    }

    if (!hasSavedGame) {
      handleStartNewGame();
    }

    markCreationFlowReturnToCreate();
    sessionController.setCreationTypeError(null);
    return true;
  }, [
    handleStartNewGame,
    hasSavedGame,
    markCreationFlowReturnToCreate,
    sessionController,
  ]);

  const openCreationTypePicker = useCallback(() => {
    if (!prepareCreationLaunch()) {
      return;
    }

    setShowCreationTypeModal(true);
  }, [prepareCreationLaunch]);

  const bigFishFlow = usePlatformCreationAgentFlowController<
    BigFishSessionSnapshotResponse,
    Record<string, never>,
    { session: BigFishSessionSnapshotResponse },
    SendBigFishMessageRequest,
    ExecuteBigFishActionRequest,
    { session: BigFishSessionSnapshotResponse }
  >({
    client: {
      createSession: createBigFishCreationSession,
      getSession: getBigFishCreationSession,
      streamMessage: streamBigFishCreationMessage,
      executeAction: executeBigFishCreationAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'big-fish-agent-workspace',
    resultStage: 'big-fish-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'big_fish_compile_draft',
    resolveErrorMessage: resolveBigFishErrorMessage,
    errorMessages: {
      open: '开启大鱼吃小鱼共创工作台失败。',
      restoreMissingSession: '这份大鱼吃小鱼草稿缺少会话信息，请重新开始创作。',
      restore: '读取大鱼吃小鱼创作草稿失败。',
      submit: '发送大鱼吃小鱼共创消息失败。',
      execute: '执行大鱼吃小鱼操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onSessionChanged: (session) => {
      writeCreationUrlState(buildBigFishCreationUrlState(session));
    },
    onActionComplete: ({ payload, response, setSession }) => {
      setSession(response.session);
      if (payload.action === 'big_fish_publish_game') {
        void refreshBigFishShelf();
        void refreshBigFishGallery();
        openPublishShareModal({
          title: response.session.draft?.title ?? '大鱼吃小鱼',
          publicWorkCode: buildBigFishPublicWorkCode(
            response.session.sessionId,
          ),
          stage: 'big-fish-runtime',
        });
      }
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      setBigFishGenerationState((current) =>
        current
          ? resolveFinishedMiniGameDraftGenerationState(current, 'ready', {
              completedAssetCount: response.session.assetSlots.filter(
                (slot) => slot.status === 'ready',
              ).length,
              totalAssetCount: response.session.assetSlots.length,
            })
          : current,
      );
      const openResult = selectionStageRef.current === 'big-fish-generating';
      const noticeIds = [
        `big-fish-work-${response.session.sessionId}`,
        response.session.sessionId,
      ];
      markPendingDraftReady('big-fish', response.session.sessionId, openResult);
      markDraftReady('big-fish', noticeIds, openResult);
      void refreshBigFishShelf();
      return { openResult };
    },
    beforeExecuteAction: ({ payload, session }) => {
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      markDraftGenerating('big-fish', [
        `big-fish-work-${session.sessionId}`,
        session.sessionId,
      ]);
      markPendingDraftGenerating('big-fish', session.sessionId);
      selectionStageRef.current = 'big-fish-generating';
      setSelectionStage('big-fish-generating');
      setBigFishGenerationState(createMiniGameDraftGenerationState('big-fish'));
    },
    onActionError: ({ payload, errorMessage, session }) => {
      if (payload.action !== 'big_fish_compile_draft') {
        return;
      }
      setBigFishGenerationState((current) =>
        current
          ? resolveFinishedMiniGameDraftGenerationState(current, 'failed', {
              error: errorMessage,
            })
          : current,
      );
      markPendingDraftFailed('big-fish', session.sessionId);
      markDraftFailed(
        'big-fish',
        [`big-fish-work-${session.sessionId}`, session.sessionId],
        errorMessage,
      );
      void refreshBigFishShelf();
    },
  });

  const match3dRuntimeAdapter = useMemo(
    () => createServerMatch3DRuntimeAdapter(),
    [],
  );
  const resolveMatch3DRuntimeAdapter = useCallback(
    (_profileId: string | null | undefined) => match3dRuntimeAdapter,
    [match3dRuntimeAdapter],
  );
  const match3dFlow = usePlatformCreationAgentFlowController<
    Match3DAgentSessionSnapshot,
    CreateMatch3DSessionRequest,
    Match3DSessionResponse,
    SendMatch3DMessageRequest,
    ExecuteMatch3DActionRequest,
    Match3DActionResponse
  >({
    client: {
      createSession: match3dCreationClient.createSession,
      getSession: match3dCreationClient.getSession,
      streamMessage: match3dCreationClient.streamMessage,
      executeAction: match3dCreationClient.executeAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'match3d-agent-workspace',
    resultStage: 'match3d-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'match3d_compile_draft',
    resolveErrorMessage: resolveMatch3DErrorMessage,
    errorMessages: {
      open: '开启抓大鹅共创工作台失败。',
      restoreMissingSession: '这份抓大鹅草稿缺少会话信息，请重新开始创作。',
      restore: '读取抓大鹅创作草稿失败。',
      submit: '发送抓大鹅共创消息失败。',
      execute: '执行抓大鹅操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onSessionChanged: (session) => {
      writeCreationUrlState(buildMatch3DCreationUrlState(session));
    },
    onActionComplete: async ({ payload, response, setSession }) => {
      setSession(response.session);
      if (payload.action !== 'match3d_compile_draft') {
        return;
      }
      const openResult = selectionStageRef.current === 'match3d-generating';
      setMatch3DGenerationState((current) =>
        current
          ? resolveFinishedMiniGameDraftGenerationState(current, 'ready', {
              completedAssetCount:
                response.session.draft?.generatedItemAssets?.length ?? 5,
              totalAssetCount:
                response.session.draft?.generatedItemAssets?.length ?? 5,
            })
          : current,
      );

      const profileId = response.session.draft?.profileId;
      if (!profileId) {
        setMatch3DProfile(null);
        setMatch3DRuntimeProfile(null);
        return { openResult };
      }

      let runtimeProfile: Match3DWorkProfile | null = null;
      try {
        const { item } = await getMatch3DWorkDetail(profileId);
        runtimeProfile = normalizeMatch3DWorkForRuntimeUi({
          ...item,
          generatedItemAssets: mergeMatch3DGeneratedItemAssetsForRuntime(
            response.session.draft?.generatedItemAssets,
            item.generatedItemAssets,
          ),
        });
        setMatch3DProfile(runtimeProfile);
        await refreshMatch3DShelf().catch(() => undefined);
      } catch {
        runtimeProfile = buildMatch3DProfileFromSession(response.session);
        setMatch3DProfile(runtimeProfile);
      }
      markPendingDraftReady('match3d', response.session.sessionId, openResult);
      markDraftReady(
        'match3d',
        [profileId, response.session.sessionId],
        openResult,
      );
      if (openResult && runtimeProfile) {
        try {
          await preloadMatch3DGeneratedRuntimeAssets(
            runtimeProfile.generatedItemAssets,
            runtimeProfile.generatedBackgroundAsset,
            { expireSeconds: 300 },
          );
          const { run } = await match3dRuntimeAdapter.startRun(
            runtimeProfile.profileId,
          );
          setMatch3DRuntimeProfile(runtimeProfile);
          setMatch3DRun(run);
          setMatch3DProfile(runtimeProfile);
          setMatch3DRuntimeReturnStage('match3d-result');
          setSelectionStage('match3d-runtime');
        } catch (error) {
          setMatch3DError(
            resolveMatch3DErrorMessage(error, '启动抓大鹅玩法失败。'),
          );
          setSelectionStage('match3d-result');
        }
        return { openResult: false };
      }
      return { openResult };
    },
    beforeExecuteAction: ({ payload, session }) => {
      if (payload.action !== 'match3d_compile_draft') {
        return;
      }
      markDraftGenerating('match3d', [
        session.draft?.profileId,
        session.publishedProfileId,
        session.sessionId,
      ]);
      markPendingDraftGenerating('match3d', session.sessionId);
      selectionStageRef.current = 'match3d-generating';
      setSelectionStage('match3d-generating');
      setMatch3DGenerationState(
        createMiniGameDraftGenerationState(
          'match3d',
          resolveMiniGameDraftGenerationStartedAtMs(session.updatedAt),
        ),
      );
    },
    onActionError: async ({ payload, errorMessage, session, setSession }) => {
      if (payload.action !== 'match3d_compile_draft') {
        return;
      }
      if (isBackgroundGenerationStillRunningMessage(errorMessage)) {
        try {
          const { session: latestSession } =
            await match3dCreationClient.getSession(session.sessionId);
          setSession(latestSession);
          const profileId =
            latestSession.draft?.profileId ?? latestSession.publishedProfileId;
          if (profileId) {
            const { item } = await getMatch3DWorkDetail(profileId);
            setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(item));
          }
          markPendingDraftGenerating('match3d', latestSession.sessionId);
          markDraftGenerating('match3d', [
            latestSession.draft?.profileId,
            latestSession.publishedProfileId,
            latestSession.sessionId,
          ]);
          await refreshMatch3DShelf().catch(() => undefined);
          return;
        } catch {
          await refreshMatch3DShelf().catch(() => undefined);
        }
      }
      setMatch3DGenerationState((current) =>
        current
          ? resolveFinishedMiniGameDraftGenerationState(current, 'failed', {
              error: errorMessage,
            })
          : current,
      );
      markPendingDraftFailed('match3d', session.sessionId);
      markDraftFailed(
        'match3d',
        [session.draft?.profileId, session.publishedProfileId, session.sessionId],
        errorMessage,
      );
      try {
        const { session: latestSession } =
          await match3dCreationClient.getSession(session.sessionId);
        setSession(latestSession);
        const profileId =
          latestSession.draft?.profileId ?? latestSession.publishedProfileId;
        if (profileId) {
          const { item } = await getMatch3DWorkDetail(profileId);
          setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(item));
        }
        await refreshMatch3DShelf().catch(() => undefined);
      } catch {
        await refreshMatch3DShelf().catch(() => undefined);
      }
    },
  });

  const squareHoleFlow = usePlatformCreationAgentFlowController<
    SquareHoleSessionSnapshot,
    CreateSquareHoleSessionRequest,
    SquareHoleSessionResponse,
    SendSquareHoleMessageRequest,
    ExecuteSquareHoleActionRequest,
    SquareHoleActionResponse
  >({
    client: {
      createSession: squareHoleCreationClient.createSession,
      getSession: squareHoleCreationClient.getSession,
      streamMessage: squareHoleCreationClient.streamMessage,
      executeAction: squareHoleCreationClient.executeAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'square-hole-agent-workspace',
    resultStage: 'square-hole-result',
    platformStage: 'platform',
    isCompileAction: () => false,
    resolveErrorMessage: resolveSquareHoleErrorMessage,
    errorMessages: {
      open: '开启方洞挑战共创工作台失败。',
      restoreMissingSession: '这份方洞挑战草稿缺少会话信息，请重新开始创作。',
      restore: '读取方洞挑战创作草稿失败。',
      submit: '发送方洞挑战共创消息失败。',
      execute: '执行方洞挑战操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onSessionChanged: (session) => {
      writeCreationUrlState(buildSquareHoleCreationUrlState(session));
    },
    beforeExecuteAction: ({ payload, session }) => {
      if (payload.action === 'square_hole_compile_draft') {
        markDraftGenerating('square-hole', [
          session.draft?.profileId,
          session.publishedProfileId,
          session.sessionId,
        ]);
        markPendingDraftGenerating('square-hole', session.sessionId);
        setSquareHoleGenerationState(
          createMiniGameDraftGenerationState('square-hole'),
        );
        selectionStageRef.current = 'square-hole-generating';
        setSelectionStage('square-hole-generating');
      }
      if (payload.action === 'square_hole_generate_visual_assets') {
        markDraftGenerating('square-hole', [
          session.draft?.profileId,
          session.publishedProfileId,
          session.sessionId,
        ]);
        markPendingDraftGenerating('square-hole', session.sessionId);
        setSquareHoleGenerationState((current) => ({
          ...(current ?? createMiniGameDraftGenerationState('square-hole')),
          phase: 'square-hole-cover',
          completedAssetCount: 0,
          totalAssetCount: 0,
          error: null,
        }));
        selectionStageRef.current = 'square-hole-generating';
        setSelectionStage('square-hole-generating');
      }
    },
    onActionComplete: async ({ payload, response, setSession }) => {
      setSession(response.session);
      if (
        payload.action !== 'square_hole_compile_draft' &&
        payload.action !== 'square_hole_generate_visual_assets'
      ) {
        return;
      }
      const shouldOpenSquareHoleResult = () =>
        selectionStageRef.current === 'square-hole-generating';

      const profileId = response.session.draft?.profileId;
      if (!profileId) {
        setSquareHoleProfile(null);
        markPendingDraftReady(
          'square-hole',
          response.session.sessionId,
          shouldOpenSquareHoleResult(),
        );
        markDraftReady(
          'square-hole',
          [response.session.sessionId, response.session.publishedProfileId],
          shouldOpenSquareHoleResult(),
        );
        return;
      }

      if (payload.action === 'square_hole_compile_draft') {
        try {
          const assetResponse = await squareHoleCreationClient.executeAction(
            response.session.sessionId,
            {
              action: 'square_hole_generate_visual_assets',
            },
          );
          setSession(assetResponse.session);
          const assetProfileId = assetResponse.session.draft?.profileId;
          if (!assetProfileId) {
            const shouldOpenResult = shouldOpenSquareHoleResult();
            setSquareHoleProfile(
              buildSquareHoleProfileFromSession(assetResponse.session),
            );
            markPendingDraftReady(
              'square-hole',
              assetResponse.session.sessionId,
              shouldOpenResult,
            );
            markDraftReady(
              'square-hole',
              [
                assetResponse.session.sessionId,
                assetResponse.session.publishedProfileId,
              ],
              shouldOpenResult,
            );
            if (shouldOpenResult) {
              setSelectionStage('square-hole-result');
            }
            return;
          }
          const { item } = await getSquareHoleWorkDetail(assetProfileId);
          const shouldOpenResult = shouldOpenSquareHoleResult();
          setSquareHoleProfile(item);
          setSquareHoleGenerationState((current) =>
            resolveFinishedMiniGameDraftGenerationState(
              current ?? createMiniGameDraftGenerationState('square-hole'),
              'ready',
              {
                completedAssetCount:
                  item.shapeOptions.length + item.holeOptions.length + 2,
                totalAssetCount:
                  item.shapeOptions.length + item.holeOptions.length + 2,
                error: null,
              },
            ),
          );
          await refreshSquareHoleShelf().catch(() => undefined);
          markPendingDraftReady(
            'square-hole',
            assetResponse.session.sessionId,
            shouldOpenResult,
          );
          markDraftReady(
            'square-hole',
            [assetProfileId, assetResponse.session.sessionId],
            shouldOpenResult,
          );
          if (shouldOpenResult) {
            setSelectionStage('square-hole-result');
          }
        } catch (error) {
          const shouldOpenResult = shouldOpenSquareHoleResult();
          const errorMessage = resolveSquareHoleErrorMessage(
            error,
            '生成方洞挑战图片失败。',
          );
          setSquareHoleError(errorMessage);
          setSquareHoleGenerationState((current) =>
            resolveFinishedMiniGameDraftGenerationState(
              current ?? createMiniGameDraftGenerationState('square-hole'),
              'failed',
              { error: errorMessage },
            ),
          );
          setSquareHoleProfile(
            buildSquareHoleProfileFromSession(response.session),
          );
          markPendingDraftFailed('square-hole', response.session.sessionId);
          markDraftFailed(
            'square-hole',
            [
              response.session.draft?.profileId,
              response.session.publishedProfileId,
              response.session.sessionId,
            ],
            errorMessage,
          );
          void refreshSquareHoleShelf().catch(() => undefined);
          if (shouldOpenResult) {
            setSelectionStage('square-hole-result');
          }
        }
        return;
      }

      try {
        const { item } = await getSquareHoleWorkDetail(profileId);
        const shouldOpenResult = shouldOpenSquareHoleResult();
        setSquareHoleProfile(item);
        setSquareHoleGenerationState((current) =>
          resolveFinishedMiniGameDraftGenerationState(
            current ?? createMiniGameDraftGenerationState('square-hole'),
            'ready',
            {
              completedAssetCount:
                item.shapeOptions.length + item.holeOptions.length + 2,
              totalAssetCount:
                item.shapeOptions.length + item.holeOptions.length + 2,
              error: null,
            },
          ),
        );
        await refreshSquareHoleShelf().catch(() => undefined);
        markPendingDraftReady(
          'square-hole',
          response.session.sessionId,
          shouldOpenResult,
        );
        markDraftReady(
          'square-hole',
          [profileId, response.session.sessionId],
          shouldOpenResult,
        );
        if (shouldOpenResult) {
          setSelectionStage('square-hole-result');
        }
      } catch {
        const shouldOpenResult = shouldOpenSquareHoleResult();
        setSquareHoleProfile(
          buildSquareHoleProfileFromSession(response.session),
        );
        markPendingDraftReady(
          'square-hole',
          response.session.sessionId,
          shouldOpenResult,
        );
        markDraftReady(
          'square-hole',
          [profileId, response.session.sessionId],
          shouldOpenResult,
        );
        if (shouldOpenResult) {
          setSelectionStage('square-hole-result');
        }
      }
    },
    onActionError: ({ payload, errorMessage, session }) => {
      if (
        payload.action === 'square_hole_compile_draft' ||
        payload.action === 'square_hole_generate_visual_assets'
      ) {
        setSquareHoleGenerationState((current) =>
          resolveFinishedMiniGameDraftGenerationState(
            current ?? createMiniGameDraftGenerationState('square-hole'),
            'failed',
            { error: errorMessage },
          ),
        );
        if (selectionStageRef.current === 'square-hole-generating') {
          setSelectionStage('square-hole-generating');
        }
        markPendingDraftFailed('square-hole', session.sessionId);
        markDraftFailed(
          'square-hole',
          [session.draft?.profileId, session.publishedProfileId, session.sessionId],
          errorMessage,
        );
        void refreshSquareHoleShelf().catch(() => undefined);
      }
    },
  });

  const puzzleFlow = usePlatformCreationAgentFlowController<
    PuzzleAgentSessionSnapshot,
    CreatePuzzleAgentSessionRequest,
    { session: PuzzleAgentSessionSnapshot },
    SendPuzzleAgentMessageRequest,
    PuzzleAgentActionRequest,
    {
      operation: PuzzleAgentOperationRecord;
      session: PuzzleAgentSessionSnapshot;
    }
  >({
    client: {
      createSession: createPuzzleAgentSession,
      getSession: getPuzzleAgentSession,
      streamMessage: streamPuzzleAgentMessage,
      executeAction: executePuzzleAgentAction,
      selectSession: (response) => response.session,
    },
    createPayload: {},
    workspaceStage: 'puzzle-agent-workspace',
    resultStage: 'puzzle-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.action === 'compile_puzzle_draft',
    resolveErrorMessage: resolvePuzzleErrorMessage,
    errorMessages: {
      open: '开启拼图共创工作台失败。',
      restoreMissingSession: '这份拼图草稿缺少会话信息，请重新开始创作。',
      restore: '读取拼图创作草稿失败。',
      submit: '发送拼图共创消息失败。',
      execute: '执行拼图操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      sessionController.setCreationTypeError(null);
      setPuzzleCreationError(null);
      setShowCreationTypeModal(false);
    },
    onSessionChanged: (session) => {
      writeCreationUrlState(buildPuzzleCreationUrlState(session));
    },
    onOpenError: ({ errorMessage }) => {
      sessionController.setCreationTypeError(errorMessage);
      setPuzzleCreationError(errorMessage);
    },
    onActionComplete: async ({ payload, response, session, setSession }) => {
      setPuzzleOperation(response.operation);
      setSession(response.session);
      const formPayload = buildPuzzleFormPayloadFromAction(payload);
      if (formPayload) {
        setPuzzleFormDraftPayload(formPayload);
      }

      if (
        payload.action === 'publish_puzzle_work' ||
        payload.action === 'generate_puzzle_tags' ||
        payload.action === 'compile_puzzle_draft' ||
        payload.action === 'generate_puzzle_images' ||
        payload.action === 'generate_puzzle_ui_background'
      ) {
        refreshPlatformDashboardSilently();
        await Promise.allSettled([
          refreshPuzzleShelf(),
          refreshPuzzleGallery(),
        ]);
      }

      if (payload.action === 'compile_puzzle_draft') {
        const openResult = selectionStageRef.current === 'puzzle-generating';
        if (!isPuzzleCompileActionReady(response.session)) {
          const nextPayload =
            formPayload ?? buildPuzzleFormPayloadFromSession(response.session);
          const fallbackGenerationState = createPuzzleDraftGenerationStateFromPayload(
            nextPayload,
            response.session,
          );
          const nextGenerationState = mergePuzzleSessionProgressIntoGenerationState(
            puzzleGenerationState ?? fallbackGenerationState,
            response.session,
          );
          activePuzzleGenerationSessionIdRef.current = response.session.sessionId;
          setSelectionStage('puzzle-generating');
          markDraftGenerating('puzzle', [
            response.session.sessionId,
            buildPuzzleResultWorkId(response.session.sessionId),
            response.session.publishedProfileId,
            buildPuzzleResultProfileId(response.session.sessionId),
          ]);
          markPendingDraftGenerating(
            'puzzle',
            response.session.sessionId,
            buildPendingPuzzleDraftMetadata(nextPayload),
          );
          setPuzzleGenerationState(nextGenerationState);
          setPuzzleBackgroundCompileTasks((current) => {
            const next = { ...current };
            if (session.sessionId !== response.session.sessionId) {
              delete next[session.sessionId];
            }
            next[response.session.sessionId] = {
              session: response.session,
              payload: nextPayload,
              generationState: nextGenerationState,
              error: null,
            };
            return next;
          });
          void refreshPuzzleShelf();
          return { openResult: false };
        }
        setPuzzleGenerationState((current) =>
          current
            ? resolveFinishedMiniGameDraftGenerationState(current, 'ready', {
                completedAssetCount: 1,
                totalAssetCount: 1,
              })
            : current,
        );
        const profileId =
          response.session.publishedProfileId ??
          buildPuzzleResultProfileId(response.session.sessionId);
        markPendingDraftReady('puzzle', response.session.sessionId, openResult);
        markDraftReady(
          'puzzle',
          [
            response.session.sessionId,
            buildPuzzleResultWorkId(response.session.sessionId),
            profileId,
          ],
          openResult,
        );
        void refreshPuzzleShelf();
        if (openResult && response.session.draft) {
          const draft = response.session.draft;
          const draftProfileId =
            response.session.publishedProfileId ??
            buildPuzzleResultProfileId(response.session.sessionId);
          if (!draft.coverImageSrc || !draftProfileId) {
            setPuzzleError(
              !draft.coverImageSrc
                ? '请先选择一张正式拼图图片。'
                : '这份拼图草稿缺少会话信息，请重新开始创作。',
            );
            setSelectionStage('puzzle-result');
            return { openResult: false };
          }

          try {
            const { item } = await updatePuzzleWork(draftProfileId, {
              workTitle: draft.workTitle,
              workDescription: draft.workDescription,
              levelName: draft.levelName,
              summary: draft.summary,
              themeTags: draft.themeTags,
              coverImageSrc: draft.coverImageSrc,
              coverAssetId: draft.coverAssetId,
              levels: draft.levels ?? [],
            });
            const run = startLocalPuzzleRun(item);
            setSelectedPuzzleDetail(item);
            setPuzzleRun(run);
            setPuzzleRuntimeAuthMode('default');
            setPuzzleRuntimeReturnStage('puzzle-result');
            pushPuzzleResultHistoryEntry(response.session);
            openPuzzleRuntimeStage(
              setSelectionStage,
              buildPuzzleDraftRuntimeUrlState(item, null),
            );
          } catch (error) {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '启动拼图试玩失败。'),
            );
            setSelectionStage('puzzle-result');
          }
          return { openResult: false };
        }
        return { openResult };
      }

      if (
        payload.action === 'publish_puzzle_work' &&
        response.session.publishedProfileId
      ) {
        const galleryDetail = await getPuzzleGalleryDetail(
          response.session.publishedProfileId,
        );
        setSelectedPuzzleDetail(galleryDetail.item);
        const detailEntry = mapPuzzleWorkToPublicWorkDetail(galleryDetail.item);
        setSelectedPublicWorkDetail(detailEntry);
        setPublicWorkDetailError(null);
        setSelectionStage('work-detail');
        pushAppHistoryPath(
          buildPublicWorkStagePath(
            'work-detail',
            buildPuzzlePublicWorkCode(galleryDetail.item.profileId),
          ),
        );
        openPublishShareModal({
          title: galleryDetail.item.workTitle || galleryDetail.item.levelName,
          publicWorkCode: buildPuzzlePublicWorkCode(
            galleryDetail.item.profileId,
          ),
          stage: 'puzzle-gallery-detail',
        });
      }
    },
    beforeExecuteAction: ({ payload, session }) => {
      const formPayload = buildPuzzleFormPayloadFromAction(payload);
      if (formPayload) {
        setPuzzleFormDraftPayload(formPayload);
      }

      if (payload.action !== 'compile_puzzle_draft') {
        return;
      }
      markDraftGenerating('puzzle', [
        session.sessionId,
        buildPuzzleResultWorkId(session.sessionId),
        session.publishedProfileId,
        buildPuzzleResultProfileId(session.sessionId),
      ]);
      markPendingDraftGenerating(
        'puzzle',
        session.sessionId,
        buildPendingPuzzleDraftMetadata(
          formPayload ?? buildPuzzleFormPayloadFromSession(session),
        ),
      );
      selectionStageRef.current = 'puzzle-generating';
      activePuzzleGenerationSessionIdRef.current = session.sessionId;
      setSelectionStage('puzzle-generating');
      const nextGenerationState = createPuzzleDraftGenerationStateFromPayload(
        formPayload ?? buildPuzzleFormPayloadFromSession(session),
        session,
      );
      setPuzzleGenerationState(nextGenerationState);
      setPuzzleBackgroundCompileTasks((current) => ({
        ...current,
        [session.sessionId]: {
          session,
          payload: formPayload ?? buildPuzzleFormPayloadFromSession(session),
          generationState: nextGenerationState,
          error: null,
        },
      }));
    },
    onActionError: async ({ payload, errorMessage, session, setSession }) => {
      if (payload.action !== 'compile_puzzle_draft') {
        return;
      }
      refreshPlatformDashboardSilently();
      const formPayload =
        buildPuzzleFormPayloadFromAction(payload) ??
        puzzleBackgroundCompileTasks[session.sessionId]?.payload ??
        buildPuzzleFormPayloadFromSession(session);
      const generationState =
        puzzleBackgroundCompileTasks[session.sessionId]?.generationState ??
        puzzleGenerationState ??
        createPuzzleDraftGenerationStateFromPayload(formPayload, session);
      const recovered = await recoverCompletedPuzzleDraftGeneration({
        sessionId: session.sessionId,
        payload: formPayload,
        generationState,
        setSession,
      });
      if (recovered) {
        return;
      }
      const failedGenerationState = resolveFinishedMiniGameDraftGenerationState(
        generationState,
        'failed',
        { error: errorMessage },
      );
      setPuzzleBackgroundCompileTasks((current) => ({
        ...current,
        [session.sessionId]: {
          session,
          payload: formPayload,
          generationState: failedGenerationState,
          error: errorMessage,
        },
      }));
      markPendingDraftFailed('puzzle', session.sessionId);
      markDraftFailed(
        'puzzle',
        [
          session.sessionId,
          buildPuzzleResultWorkId(session.sessionId),
          session.publishedProfileId,
          buildPuzzleResultProfileId(session.sessionId),
        ],
        errorMessage,
      );
      void refreshPuzzleShelf();
      setPuzzleGenerationState((current) =>
        current ? failedGenerationState : current,
      );
    },
  });

  const visualNovelFlow = usePlatformCreationAgentFlowController<
    VisualNovelAgentSessionSnapshot,
    CreateVisualNovelSessionRequest,
    VisualNovelSessionResponse,
    SendVisualNovelMessageRequest,
    ExecuteVisualNovelAgentActionRequest,
    VisualNovelSessionResponse
  >({
    client: {
      createSession: createVisualNovelSession,
      getSession: getVisualNovelSession,
      streamMessage: streamVisualNovelMessage,
      executeAction: executeVisualNovelAction,
      selectSession: (response) => response.session,
    },
    createPayload: {
      sourceMode: 'idea',
      seedText: null,
      sourceAssetIds: [],
    },
    workspaceStage: 'visual-novel-agent-workspace',
    resultStage: 'visual-novel-result',
    platformStage: 'platform',
    isCompileAction: (payload) => payload.kind === 'compile_work_profile',
    resolveErrorMessage: resolvePuzzleErrorMessage,
    errorMessages: {
      open: '开启视觉小说共创工作台失败。',
      restoreMissingSession: '这份视觉小说草稿缺少会话信息，请重新开始创作。',
      restore: '读取视觉小说创作草稿失败。',
      submit: '发送视觉小说共创消息失败。',
      execute: '执行视觉小说操作失败。',
    },
    enterCreateTab,
    setSelectionStage,
    onSessionOpened: () => {
      setShowCreationTypeModal(false);
    },
    onSessionChanged: (session) => {
      writeCreationUrlState(buildVisualNovelCreationUrlState(session));
    },
    onActionComplete: ({ response, setSession }) => {
      setSession(response.session);
      const openResult =
        selectionStageRef.current === 'visual-novel-generating';
      markDraftReady(
        'visual-novel',
        [response.session.draft?.profileId, response.session.sessionId],
        openResult,
      );
      void refreshVisualNovelShelf();
      return { openResult };
    },
  });

  const bigFishSession = bigFishFlow.session;
  const bigFishError = bigFishFlow.error;
  const setBigFishError = bigFishFlow.setError;
  bigFishErrorSetterRef.current = setBigFishError;
  const isBigFishBusy = bigFishFlow.isBusy;
  const streamingBigFishReplyText = bigFishFlow.streamingReplyText;
  const isStreamingBigFishReply = bigFishFlow.isStreamingReply;

  const match3dSession = match3dFlow.session;
  const match3dError = match3dFlow.error;
  const setMatch3DSession = match3dFlow.setSession;
  const setMatch3DError = match3dFlow.setError;
  match3DErrorSetterRef.current = setMatch3DError;
  const isMatch3DBusy = match3dFlow.isBusy;
  const setStreamingMatch3DReplyText = match3dFlow.setStreamingReplyText;
  const isStreamingMatch3DReply = match3dFlow.isStreamingReply;
  const setIsStreamingMatch3DReply = match3dFlow.setIsStreamingReply;

  const squareHoleSession = squareHoleFlow.session;
  const squareHoleError = squareHoleFlow.error;
  const setSquareHoleSession = squareHoleFlow.setSession;
  const setSquareHoleError = squareHoleFlow.setError;
  squareHoleErrorSetterRef.current = setSquareHoleError;
  const isSquareHoleBusy = squareHoleFlow.isBusy;
  const streamingSquareHoleReplyText = squareHoleFlow.streamingReplyText;
  const setStreamingSquareHoleReplyText = squareHoleFlow.setStreamingReplyText;
  const isStreamingSquareHoleReply = squareHoleFlow.isStreamingReply;
  const setIsStreamingSquareHoleReply = squareHoleFlow.setIsStreamingReply;

  const puzzleSession = puzzleFlow.session;
  const puzzleError = puzzleFlow.error;
  const setPuzzleSession = puzzleFlow.setSession;
  const setPuzzleError = puzzleFlow.setError;
  puzzleErrorSetterRef.current = setPuzzleError;
  const isPuzzleBusy = puzzleFlow.isBusy;
  const setIsPuzzleBusy = puzzleFlow.setIsBusy;
  const isStreamingPuzzleReply = puzzleFlow.isStreamingReply;
  const visualNovelSession = visualNovelFlow.session;
  const visualNovelError = visualNovelFlow.error;
  const setVisualNovelSession = visualNovelFlow.setSession;
  const setVisualNovelError = visualNovelFlow.setError;
  visualNovelErrorSetterRef.current = setVisualNovelError;
  const isVisualNovelBusy = visualNovelFlow.isBusy;
  const setIsVisualNovelBusy = visualNovelFlow.setIsBusy;
  const isVisualNovelStreamingReply = visualNovelFlow.isStreamingReply;
  const resetRpgSessionViewState = sessionController.resetSessionViewState;
  const setRpgGeneratedCustomWorldProfile =
    sessionController.setGeneratedCustomWorldProfile;
  const setRpgCustomWorldError = sessionController.setCustomWorldError;
  const persistRpgAgentUiState = sessionController.persistAgentUiState;
  const resetAutoSaveTrackingToIdle =
    autosaveCoordinator.resetAutoSaveTrackingToIdle;
  const preflightPuzzleDraftGeneration = useCallback(async () => {
    setPuzzleCreationError(null);
    setPuzzleError(null);
    return ensureEnoughDraftGenerationPointsFromServer(
      PUZZLE_DRAFT_GENERATION_POINT_COST,
    );
  }, [ensureEnoughDraftGenerationPointsFromServer]);
  const preflightMatch3DDraftGeneration = useCallback(async () => {
    setMatch3DError(null);
    return ensureEnoughDraftGenerationPointsFromServer(
      MATCH3D_DRAFT_GENERATION_POINT_COST,
    );
  }, [ensureEnoughDraftGenerationPointsFromServer]);
  const preflightBarkBattleDraftGeneration = useCallback(async () => {
    setBarkBattleError(null);
    return ensureEnoughDraftGenerationPointsFromServer(
      BARK_BATTLE_DRAFT_GENERATION_POINT_COST,
    );
  }, [ensureEnoughDraftGenerationPointsFromServer]);
  const draftGenerationPointNoticeDescription = draftGenerationPointNotice
    ? draftGenerationPointNotice.title === '读取泥点余额失败'
      ? '当前表单不会丢失，关闭后可继续编辑，稍后再试。'
      : '当前表单不会丢失，关闭后可继续编辑或补足泥点再继续。'
    : undefined;
  const recoverCompletedPuzzleDraftGeneration = useCallback(
    async ({
      sessionId,
      payload,
      generationState,
      setSession,
    }: {
      sessionId: string;
      payload: CreatePuzzleAgentSessionRequest;
      generationState: MiniGameDraftGenerationState;
      setSession?: (session: PuzzleAgentSessionSnapshot) => void;
    }) => {
      let latestSession: PuzzleAgentSessionSnapshot;
      try {
        const response = await getPuzzleAgentSession(sessionId);
        latestSession = normalizeRecoveredPuzzleDraftSession(response.session);
      } catch {
        return null;
      }

      if (!hasRecoverableGeneratedPuzzleDraft(latestSession)) {
        return null;
      }

      const readyGenerationState = resolveFinishedMiniGameDraftGenerationState(
        generationState,
        'ready',
        {
          completedAssetCount: 1,
          totalAssetCount: 1,
          error: null,
        },
      );
      const openResult = isViewingPuzzleGeneration(sessionId);
      const profileId =
        latestSession.publishedProfileId ??
        buildPuzzleResultProfileId(latestSession.sessionId);

      setPuzzleBackgroundCompileTasks((current) => ({
        ...current,
        [sessionId]: {
          session: latestSession,
          payload,
          generationState: readyGenerationState,
          error: null,
        },
      }));

      setSession?.(latestSession);
      setPuzzleFormDraftPayload(payload);
      setPuzzleOperation(null);
      puzzleErrorSetterRef.current(null);
      if (isViewingPuzzleGeneration(sessionId)) {
        setPuzzleGenerationState(readyGenerationState);
      }

      markPendingDraftReady('puzzle', latestSession.sessionId, openResult);
      markDraftReady(
        'puzzle',
        [
          latestSession.sessionId,
          buildPuzzleResultWorkId(latestSession.sessionId),
          profileId,
        ],
        openResult,
      );
      await refreshPuzzleShelf().catch(() => undefined);

      if (!openResult) {
        return { openResult };
      }

      const draft = latestSession.draft;
      if (!draft?.coverImageSrc || !profileId) {
        puzzleErrorSetterRef.current(
          !draft?.coverImageSrc
            ? '请先选择一张正式拼图图片。'
            : '这份拼图草稿缺少会话信息，请重新开始创作。',
        );
        setSelectionStage('puzzle-result');
        return { openResult: false };
      }

      try {
        const { item } = await updatePuzzleWork(profileId, {
          workTitle: draft.workTitle,
          workDescription: draft.workDescription,
          levelName: draft.levelName,
          summary: draft.summary,
          themeTags: draft.themeTags,
          coverImageSrc: draft.coverImageSrc,
          coverAssetId: draft.coverAssetId,
          levels: draft.levels ?? [],
        });
        const run = startLocalPuzzleRun(item);
        setSelectedPuzzleDetail(item);
        setPuzzleRun(run);
        setPuzzleRuntimeAuthMode('default');
        setPuzzleRuntimeReturnStage('puzzle-result');
        pushPuzzleResultHistoryEntry(latestSession);
        openPuzzleRuntimeStage(
          setSelectionStage,
          buildPuzzleDraftRuntimeUrlState(item, null),
        );
      } catch (error) {
        puzzleErrorSetterRef.current(
          resolvePuzzleErrorMessage(error, '启动拼图试玩失败。'),
        );
        setSelectionStage('puzzle-result');
      }

      return { openResult: false };
    },
    [
      isViewingPuzzleGeneration,
      markDraftReady,
      markPendingDraftReady,
      refreshPuzzleShelf,
      resolvePuzzleErrorMessage,
      setSelectionStage,
    ],
  );

  const activeMatch3DGenerationSessionId =
    selectionStage === 'match3d-generating'
      ? (activeMatch3DGenerationSessionIdRef.current ??
        match3dSession?.sessionId ??
        null)
      : null;
  const activeMatch3DBackgroundCompileTask = getMatch3DBackgroundCompileTask(
    activeMatch3DGenerationSessionId,
  );
  const match3dGenerationViewState =
    activeMatch3DBackgroundCompileTask?.generationState ??
    match3dGenerationState;
  const match3dGenerationViewSession =
    activeMatch3DBackgroundCompileTask?.session ?? match3dSession;
  const match3dGenerationViewPayload =
    activeMatch3DBackgroundCompileTask?.payload ?? match3dFormDraftPayload;
  const match3dGenerationViewError =
    activeMatch3DBackgroundCompileTask?.error ?? match3dError;
  const isMatch3DGenerationViewBusy =
    isMatch3DBusy ||
    isMiniGameDraftGenerating(
      activeMatch3DBackgroundCompileTask?.generationState ?? null,
    );
  const activePuzzleGenerationSessionId =
    selectionStage === 'puzzle-generating'
      ? (activePuzzleGenerationSessionIdRef.current ??
        puzzleSession?.sessionId ??
        null)
      : null;
  const activePuzzleBackgroundCompileTask = getPuzzleBackgroundCompileTask(
    activePuzzleGenerationSessionId,
  );
  const puzzleGenerationViewState =
    activePuzzleBackgroundCompileTask?.generationState ?? puzzleGenerationState;
  const puzzleGenerationViewSession =
    activePuzzleBackgroundCompileTask?.session ?? puzzleSession;
  const puzzleGenerationViewPayload =
    activePuzzleBackgroundCompileTask?.payload ?? puzzleFormDraftPayload;
  const puzzleGenerationViewError =
    activePuzzleBackgroundCompileTask?.error ?? puzzleError;
  const isPuzzleGenerationViewBusy =
    isPuzzleBusy ||
    isMiniGameDraftGenerating(
      activePuzzleBackgroundCompileTask?.generationState ?? null,
    );
  const platformBootstrapErrorForDisplay = isCreationEntryDisabledErrorMessage(
    platformBootstrap.platformError,
  )
    ? null
    : platformBootstrap.platformError;
  const [dismissedPlatformErrorDialogKey, setDismissedPlatformErrorDialogKey] =
    useState<string | null>(null);
  const [
    dismissedPlatformTaskCompletionDialogKey,
    setDismissedPlatformTaskCompletionDialogKey,
  ] = useState<string | null>(null);
  const currentPlatformErrorDialog = useMemo<
    (PlatformErrorDialogPayload & { key: string }) | null
  >(() => {
    const candidates: Array<{
      key: string;
      source: string;
      message: string | null | undefined;
    }> = [
      {
        key: pendingPlatformTaskFailureDialog?.key ?? 'draft-failure',
        source: pendingPlatformTaskFailureDialog?.source ?? '创作草稿',
        message: pendingPlatformTaskFailureDialog?.message,
      },
      {
        key: 'creation-entry-config',
        source: '创作入口配置',
        message: creationEntryConfigError,
      },
      {
        key: 'platform-bootstrap',
        source: '平台首页',
        message: platformBootstrapErrorForDisplay,
      },
      {
        key: 'rpg-creation-type',
        source: '创作入口',
        message: sessionController.creationTypeError,
      },
      {
        key: 'rpg-restore',
        source: '创作作品架',
        message: sessionController.agentWorkspaceRestoreError,
      },
      {
        key: 'rpg-result',
        source: formatPlatformErrorSource(
          'RPG 草稿',
          sessionController.agentSession?.sessionId ??
            sessionController.generatedCustomWorldProfile?.id,
        ),
        message: resultViewError,
      },
      {
        key: 'public-work-detail',
        source: formatPlatformErrorSource(
          '作品详情',
          selectedPublicWorkDetail
            ? resolvePlatformPublicWorkCode(selectedPublicWorkDetail)
            : selectedDetailEntry?.profileId,
        ),
        message: publicWorkDetailError ?? detailNavigation.detailError,
      },
      {
        key: 'big-fish',
        source: formatPlatformErrorSource(
          selectionStage === 'big-fish-runtime' ? '大鱼吃小鱼游玩' : '大鱼草稿',
          bigFishRun?.runId ?? bigFishSession?.sessionId,
        ),
        message: bigFishError,
      },
      {
        key: 'match3d',
        source: formatPlatformErrorSource(
          selectionStage === 'match3d-runtime' ? '抓大鹅游玩' : '抓大鹅草稿',
          match3dRun?.runId ??
            match3dGenerationViewSession?.sessionId ??
            match3dSession?.sessionId,
        ),
        message: match3dGenerationViewError ?? match3dError,
      },
      {
        key: 'square-hole',
        source: formatPlatformErrorSource(
          selectionStage === 'square-hole-runtime'
            ? '方洞挑战游玩'
            : '方洞挑战草稿',
          squareHoleRun?.runId ?? squareHoleSession?.sessionId,
        ),
        message: squareHoleError,
      },
      {
        key: 'jump-hop',
        source: formatPlatformErrorSource(
          selectionStage === 'jump-hop-runtime' ? '跳一跳游玩' : '跳一跳草稿',
          jumpHopRun?.runId ?? jumpHopSession?.sessionId,
        ),
        message: jumpHopError,
      },
      {
        key: 'puzzle-clear',
        source: formatPlatformErrorSource(
          selectionStage === 'puzzle-clear-runtime'
            ? '拼消消游玩'
            : selectionStage === 'puzzle-clear-generating'
              ? '拼消消草稿生成'
              : '拼消消草稿',
          puzzleClearRun?.runId ??
            puzzleClearSession?.sessionId ??
            puzzleClearWork?.summary.profileId,
        ),
        message: puzzleClearError,
      },
      {
        key: 'wooden-fish',
        source: formatPlatformErrorSource(
          selectionStage === 'wooden-fish-runtime'
            ? '敲木鱼游玩'
            : '敲木鱼草稿',
          woodenFishRun?.runId ?? woodenFishSession?.sessionId,
        ),
        message: woodenFishError,
      },
      {
        key: 'puzzle',
        source: formatPlatformErrorSource(
          selectionStage === 'puzzle-runtime' ? '拼图游玩' : '拼图草稿',
          puzzleRun?.runId ??
            puzzleGenerationViewSession?.sessionId ??
            puzzleSession?.sessionId,
        ),
        message:
          puzzleGenerationViewError ?? puzzleCreationError ?? puzzleError,
      },
      {
        key: 'puzzle-onboarding',
        source: '拼图首次创作',
        message: puzzleOnboardingError,
      },
      {
        key: 'puzzle-shelf',
        source: '拼图作品架',
        message: puzzleShelfError,
      },
      {
        key: 'visual-novel',
        source: formatPlatformErrorSource(
          selectionStage === 'visual-novel-runtime'
            ? '视觉小说游玩'
            : '视觉小说草稿',
          visualNovelRun?.runId ?? visualNovelSession?.sessionId,
        ),
        message: visualNovelError,
      },
      {
        key: 'baby-object-match',
        source: formatPlatformErrorSource(
          selectionStage === 'baby-object-match-runtime'
            ? '宝贝识物游玩'
            : '宝贝识物草稿',
          babyObjectMatchDraft?.profileId,
        ),
        message: babyObjectMatchError,
      },
      {
        key: 'bark-battle',
        source: formatPlatformErrorSource(
          selectionStage === 'bark-battle-runtime'
            ? '汪汪声浪游玩'
            : '汪汪声浪草稿',
          barkBattlePublishedConfig?.workId ?? barkBattleDraftConfig?.workId,
        ),
        message: barkBattleError,
      },
      {
        key: 'creative-agent',
        source: formatPlatformErrorSource(
          '智能创作 Agent',
          creativeAgentSession?.sessionId,
        ),
        message: creativeAgentError,
      },
      {
        key: 'rpg-generation',
        source: formatPlatformErrorSource(
          'RPG 草稿生成',
          sessionController.agentSession?.sessionId,
        ),
        message: sessionController.activeGenerationError,
      },
    ];

    for (const candidate of candidates) {
      const message = normalizePlatformErrorMessage(candidate.message);
      if (message) {
        return {
          key: candidate.key,
          source: candidate.source,
          message,
        };
      }
    }

    return null;
  }, [
    babyObjectMatchDraft?.profileId,
    babyObjectMatchError,
    barkBattleDraftConfig?.workId,
    barkBattleError,
    barkBattlePublishedConfig?.workId,
    bigFishError,
    bigFishRun?.runId,
    bigFishSession?.sessionId,
    creationEntryConfigError,
    creativeAgentError,
    creativeAgentSession?.sessionId,
    detailNavigation.detailError,
    jumpHopError,
    jumpHopRun?.runId,
    jumpHopSession?.sessionId,
    match3dError,
    match3dGenerationViewError,
    match3dGenerationViewSession?.sessionId,
    match3dRun?.runId,
    match3dSession?.sessionId,
    pendingPlatformTaskFailureDialog,
    platformBootstrapErrorForDisplay,
    publicWorkDetailError,
    puzzleClearError,
    puzzleClearRun?.runId,
    puzzleClearSession?.sessionId,
    puzzleClearWork?.summary.profileId,
    puzzleCreationError,
    puzzleError,
    puzzleGenerationViewError,
    puzzleGenerationViewSession?.sessionId,
    puzzleOnboardingError,
    puzzleRun?.runId,
    puzzleSession?.sessionId,
    puzzleShelfError,
    resultViewError,
    selectedDetailEntry?.profileId,
    selectedPublicWorkDetail,
    selectionStage,
    sessionController.activeGenerationError,
    sessionController.agentSession?.sessionId,
    sessionController.agentWorkspaceRestoreError,
    sessionController.creationTypeError,
    sessionController.generatedCustomWorldProfile?.id,
    squareHoleError,
    squareHoleRun?.runId,
    squareHoleSession?.sessionId,
    visualNovelError,
    visualNovelRun?.runId,
    visualNovelSession?.sessionId,
    woodenFishError,
    woodenFishRun?.runId,
    woodenFishSession?.sessionId,
  ]);
  const currentPlatformTaskCompletionDialog = useMemo<
    | (PlatformTaskCompletionDialogPayload & {
        key: string;
        completedAtMs: number | null;
      })
    | null
  >(
    () => pendingPlatformTaskCompletionDialog,
    [pendingPlatformTaskCompletionDialog],
  );
  const activePlatformTaskCompletionDialogDismissKey =
    buildPlatformTaskCompletionDialogDismissKey(
      currentPlatformTaskCompletionDialog,
    );
  const activePlatformTaskCompletionDialog =
    activePlatformTaskCompletionDialogDismissKey &&
    activePlatformTaskCompletionDialogDismissKey ===
      dismissedPlatformTaskCompletionDialogKey
      ? null
      : currentPlatformTaskCompletionDialog;
  const activePlatformErrorDialogDismissKey =
    buildPlatformErrorDialogDismissKey(currentPlatformErrorDialog);
  const activePlatformErrorDialog =
    activePlatformErrorDialogDismissKey &&
    activePlatformErrorDialogDismissKey === dismissedPlatformErrorDialogKey
      ? null
      : currentPlatformErrorDialog;
  const closePlatformErrorDialog = useCallback(() => {
    if (!currentPlatformErrorDialog) {
      return;
    }

    const dismissKey = buildPlatformErrorDialogDismissKey(
      currentPlatformErrorDialog,
    );
    if (dismissKey) {
      setDismissedPlatformErrorDialogKey(dismissKey);
    }

    if (currentPlatformErrorDialog.key === 'creation-entry-config') {
      setCreationEntryConfigError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'platform-bootstrap') {
      platformBootstrap.setPlatformError(null);
      return;
    }
    if (currentPlatformErrorDialog.key.startsWith('draft-failure:')) {
      setPendingPlatformTaskFailureDialog(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'rpg-creation-type') {
      sessionController.setCreationTypeError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'rpg-restore') {
      return;
    }
    if (
      currentPlatformErrorDialog.key === 'rpg-result' ||
      currentPlatformErrorDialog.key === 'rpg-generation'
    ) {
      autosaveCoordinator.setCustomWorldAutoSaveError(null);
      sessionController.setCustomWorldError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'public-work-detail') {
      setPublicWorkDetailError(null);
      detailNavigation.setDetailError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'big-fish') {
      setBigFishError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'match3d') {
      setMatch3DError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'square-hole') {
      setSquareHoleError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'jump-hop') {
      setJumpHopError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'puzzle-clear') {
      setPuzzleClearError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'wooden-fish') {
      setWoodenFishError(null);
      return;
    }
    if (
      currentPlatformErrorDialog.key === 'puzzle' ||
      currentPlatformErrorDialog.key === 'puzzle-onboarding' ||
      currentPlatformErrorDialog.key === 'puzzle-shelf'
    ) {
      setPuzzleCreationError(null);
      setPuzzleOnboardingError(null);
      setPuzzleShelfError(null);
      setPuzzleError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'visual-novel') {
      setVisualNovelError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'baby-object-match') {
      setBabyObjectMatchError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'bark-battle') {
      setBarkBattleError(null);
      return;
    }
    if (currentPlatformErrorDialog.key === 'creative-agent') {
      setCreativeAgentError(null);
    }
  }, [
    autosaveCoordinator,
    currentPlatformErrorDialog,
    detailNavigation,
    platformBootstrap,
    sessionController,
    setBigFishError,
    setMatch3DError,
    setPendingPlatformTaskFailureDialog,
    setPuzzleError,
    setSquareHoleError,
    setVisualNovelError,
  ]);
  const closePlatformTaskCompletionDialog = useCallback(() => {
    if (!currentPlatformTaskCompletionDialog) {
      return;
    }

    const dismissKey = buildPlatformTaskCompletionDialogDismissKey(
      currentPlatformTaskCompletionDialog,
    );
    if (dismissKey) {
      setDismissedPlatformTaskCompletionDialogKey(dismissKey);
    }
    setPendingPlatformTaskCompletionDialog(null);
  }, [currentPlatformTaskCompletionDialog]);
  const shouldPollPuzzleGenerationSession =
    selectionStage === 'puzzle-generating' &&
    activePuzzleGenerationSessionId != null &&
    isMiniGameDraftGenerating(puzzleGenerationViewState);

  useEffect(() => {
    if (
      !shouldPollPuzzleGenerationSession ||
      !activePuzzleGenerationSessionId
    ) {
      return undefined;
    }

    let isDisposed = false;

    const pollPuzzleDraftGenerationSession = async () => {
      try {
        const { session: latestSession } = await getPuzzleAgentSession(
          activePuzzleGenerationSessionId,
        );
        if (isDisposed) {
          return;
        }

        if (hasRecoverableGeneratedPuzzleDraft(latestSession)) {
          const payload =
            puzzleGenerationViewPayload ??
            buildPuzzleFormPayloadFromSession(latestSession);
          const generationState =
            puzzleGenerationViewState ??
            createPuzzleDraftGenerationStateFromPayload(payload, latestSession);
          await recoverCompletedPuzzleDraftGeneration({
            sessionId: latestSession.sessionId,
            payload,
            generationState,
            setSession: setPuzzleSession,
          });
          return;
        }

        setPuzzleSession(latestSession);
        setPuzzleBackgroundCompileTasks((current) => {
          const task = current[activePuzzleGenerationSessionId];
          if (!task) {
            return current;
          }

          return {
            ...current,
            [activePuzzleGenerationSessionId]: {
              ...task,
              session: latestSession,
              generationState: mergePuzzleSessionProgressIntoGenerationState(
                task.generationState,
                latestSession,
              ),
            },
          };
        });
        setPuzzleGenerationState((current) =>
          current
            ? mergePuzzleSessionProgressIntoGenerationState(
                current,
                latestSession,
              )
            : current,
        );
      } catch {
        // 中文注释：拼图长 action 仍以主请求结果为准；轮询失败只保留当前进度展示。
      }
    };

    void pollPuzzleDraftGenerationSession();
    const timerId = window.setInterval(() => {
      void pollPuzzleDraftGenerationSession();
    }, 3_000);

    return () => {
      isDisposed = true;
      window.clearInterval(timerId);
    };
  }, [
    activePuzzleGenerationSessionId,
    puzzleGenerationViewPayload,
    puzzleGenerationViewState,
    recoverCompletedPuzzleDraftGeneration,
    shouldPollPuzzleGenerationSession,
    setPuzzleSession,
  ]);

  const match3DGeneratingSessionId =
    selectionStage === 'match3d-generating' ? match3dSession?.sessionId : null;

  useEffect(() => {
    puzzleRunRef.current = puzzleRun;
  }, [puzzleRun]);

  useEffect(() => {
    if (
      selectionStage !== 'match3d-generating' ||
      !isMatch3DBusy ||
      !match3DGeneratingSessionId
    ) {
      return undefined;
    }

    let isDisposed = false;

    const pollMatch3DDraftAssets = async () => {
      try {
        const { session: latestSession } =
          await match3dCreationClient.getSession(match3DGeneratingSessionId);
        if (isDisposed) {
          return;
        }

        setMatch3DSession(latestSession);
        const profileId =
          latestSession.draft?.profileId ?? latestSession.publishedProfileId;
        if (!profileId) {
          return;
        }

        const { item } = await getMatch3DWorkDetail(profileId);
        if (isDisposed) {
          return;
        }

        const normalizedItem = normalizeMatch3DWorkForRuntimeUi(item);
        setMatch3DProfile(normalizedItem);
        setMatch3DGenerationState((current) =>
          resolveMatch3DGenerationStateFromAssets(
            current,
            normalizedItem.generatedItemAssets,
          ),
        );
      } catch {
        // 中文注释：生成长 action 仍在执行，轮询只是进度旁路；失败时保留主请求结果。
      }
    };

    void pollMatch3DDraftAssets();
    const timerId = window.setInterval(() => {
      void pollMatch3DDraftAssets();
    }, 3_000);

    return () => {
      isDisposed = true;
      window.clearInterval(timerId);
    };
  }, [
    isMatch3DBusy,
    match3DGeneratingSessionId,
    selectionStage,
    setMatch3DSession,
    setMatch3DProfile,
    setMatch3DGenerationState,
  ]);

  const openBigFishAgentWorkspace = useCallback(async () => {
    markCreationFlowReturnToCreate();
    setBigFishRun(null);
    await bigFishFlow.openWorkspace();
  }, [bigFishFlow, markCreationFlowReturnToCreate]);

  const openSquareHoleAgentWorkspace = useCallback(async () => {
    markCreationFlowReturnToCreate();
    setSquareHoleSession(null);
    setSquareHoleProfile(null);
    setSquareHoleRun(null);
    setSquareHoleError(null);
    setSquareHoleGenerationState(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setStreamingSquareHoleReplyText('');
    setIsStreamingSquareHoleReply(false);
    await squareHoleFlow.openWorkspace();
  }, [
    setIsStreamingSquareHoleReply,
    setSquareHoleError,
    setSquareHoleProfile,
    setSquareHoleRun,
    setSquareHoleSession,
    setStreamingSquareHoleReplyText,
    squareHoleFlow,
    markCreationFlowReturnToCreate,
  ]);

  const openMatch3DWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setMatch3DRun(null);
    setMatch3DProfile(null);
    setMatch3DRuntimeProfile(null);
    setMatch3DGenerationState(null);
    setMatch3DError(null);
    setStreamingMatch3DReplyText('');
    setIsStreamingMatch3DReply(false);
    enterCreateTab();
    setShowCreationTypeModal(false);
    setSelectionStage('match3d-agent-workspace');
  }, [
    enterCreateTab,
    setIsStreamingMatch3DReply,
    setMatch3DError,
    setSelectionStage,
    setStreamingMatch3DReplyText,
    markCreationFlowReturnToCreate,
  ]);

  const openJumpHopWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setJumpHopError(null);
    setJumpHopSession(null);
    setJumpHopWork(null);
    setJumpHopRun(null);
    setJumpHopRuntimeRequestOptions(null);
    setJumpHopGenerationState(null);
    enterCreateTab();
    setShowCreationTypeModal(false);
    setSelectionStage('jump-hop-workspace');
  }, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);

  const openPuzzleClearWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setPuzzleClearError(null);
    setPuzzleClearSession(null);
    setPuzzleClearWork(null);
    setPuzzleClearRun(null);
    setPuzzleClearGenerationState(null);
    enterCreateTab();
    setShowCreationTypeModal(false);
    setSelectionStage('puzzle-clear-workspace');
  }, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);

  const openWoodenFishWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setWoodenFishError(null);
    setWoodenFishSession(null);
    setWoodenFishWork(null);
    setWoodenFishRun(null);
    setWoodenFishGenerationState(null);
    enterCreateTab();
    setShowCreationTypeModal(false);
    setSelectionStage('wooden-fish-workspace');
  }, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);

  const openPuzzleWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setPuzzleSession(null);
    setPuzzleFormDraftPayload(null);
    setPuzzleOperation(null);
    setPuzzleGenerationState(null);
    setPuzzleRun(null);
    setSelectedPuzzleDetail(null);
    setPuzzleRuntimeAuthMode('default');
    activePuzzleGenerationSessionIdRef.current = null;
    enterCreateTab();
    setShowCreationTypeModal(false);
    setPuzzleCreationError(null);
    setPuzzleError(null);
    setSelectionStage('puzzle-agent-workspace');
  }, [
    enterCreateTab,
    setPuzzleSession,
    setPuzzleCreationError,
    setPuzzleError,
    setSelectionStage,
    markCreationFlowReturnToCreate,
  ]);

  const openBarkBattleWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    setBarkBattleDraftConfig(null);
    setBarkBattlePublishedConfig(null);
    setBarkBattleRuntimeMode('draft');
    setBarkBattleRuntimeReturnStage('platform');
    setBarkBattleError(null);
    setBarkBattleGenerationPartialFailed(false);
    setIsBarkBattleBusy(false);
    enterCreateTab();
    setShowCreationTypeModal(false);
    selectionStageRef.current = 'bark-battle-workspace';
    setSelectionStage('bark-battle-workspace');
  }, [enterCreateTab, markCreationFlowReturnToCreate, setSelectionStage]);

  const openVisualNovelWorkspace = useCallback(() => {
    markCreationFlowReturnToCreate();
    enterCreateTab();
    setShowCreationTypeModal(false);
    setVisualNovelError(null);
    setSelectionStage('visual-novel-agent-workspace');
  }, [
    enterCreateTab,
    markCreationFlowReturnToCreate,
    setSelectionStage,
    setVisualNovelError,
  ]);

  const openBabyObjectMatchWorkspace = useCallback(() => {
    if (!isBabyObjectMatchVisible) {
      sessionController.setCreationTypeError(EDUTAINMENT_HIDDEN_MESSAGE);
      return;
    }

    markCreationFlowReturnToCreate();
    enterCreateTab();
    setShowCreationTypeModal(false);
    setBabyObjectMatchError(null);
    setSelectionStage('baby-object-match-workspace');
  }, [
    enterCreateTab,
    isBabyObjectMatchVisible,
    markCreationFlowReturnToCreate,
    sessionController,
    setBabyObjectMatchError,
    setSelectionStage,
  ]);

  const leaveCreativeAgentWorkspace = useCallback(() => {
    const sessionId = creativeAgentSession?.sessionId?.trim();
    if (sessionId && creativeAgentSession?.stage !== 'target_ready') {
      void cancelCreativeAgentSession(sessionId).catch(() => undefined);
    }
    setCreativeAgentSession(null);
    setCreativeAgentEvents([]);
    setActiveCreativeAgentSessionId(null);
    setCreativeAgentError(null);
    setIsCreativeAgentBusy(false);
    setIsCreativeAgentStreaming(false);
    setCreativeDraftEditError(null);
    setIsCreativeDraftEditBusy(false);
    markCreationFlowReturnToCreate();
    enterCreateTab();
    setSelectionStage('platform');
  }, [
    creativeAgentSession,
    enterCreateTab,
    markCreationFlowReturnToCreate,
    setSelectionStage,
  ]);

  const openCreativeAgentWorkspace = useCallback(async () => {
    if (isCreativeAgentBusy || isCreativeAgentStreaming) {
      return;
    }

    setPuzzleRun(null);
    markCreationFlowReturnToCreate();
    setPuzzleRuntimeAuthMode('default');
    setPuzzleOperation(null);
    setPuzzleGenerationState(null);
    setPuzzleFormDraftPayload(null);
    setCreativeAgentError(null);
    setCreativeAgentEvents([]);
    setIsCreativeAgentBusy(true);

    try {
      const response = await createCreativeAgentSession({
        entryContext: 'creation_home',
      });
      setCreativeAgentSession(response.session);
      setActiveCreativeAgentSessionId(response.session.sessionId);
      enterCreateTab();
      setShowCreationTypeModal(false);
      setSelectionStage('creative-agent-workspace');
    } catch (error) {
      setCreativeAgentError(
        resolvePuzzleErrorMessage(error, '开启智能创作工作区失败。'),
      );
    } finally {
      setIsCreativeAgentBusy(false);
    }
  }, [
    enterCreateTab,
    isCreativeAgentBusy,
    isCreativeAgentStreaming,
    markCreationFlowReturnToCreate,
    resolvePuzzleErrorMessage,
    setSelectionStage,
  ]);

  const createPuzzleDraftFromForm = useCallback(
    async (payload: CreatePuzzleAgentSessionRequest) => {
      setPuzzleFormDraftPayload(payload);
      setPuzzleCreationError(null);
      setPuzzleError(null);
      const shouldConsumePuzzleDraftPoints = payload.aiRedraw !== false;

      if (
        shouldConsumePuzzleDraftPoints &&
        !(await preflightPuzzleDraftGeneration())
      ) {
        return;
      }

      let nextSession: PuzzleAgentSessionSnapshot;
      try {
        const response = await createPuzzleAgentSession(payload);
        nextSession = response.session;
      } catch (error) {
        const errorMessage = resolvePuzzleErrorMessage(
          error,
          '开启拼图创作工作台失败。',
        );
        setPuzzleCreationError(errorMessage);
        setPuzzleError(errorMessage);
        return;
      }

      const generationState = createPuzzleDraftGenerationStateFromPayload(
        payload,
        nextSession,
      );
      setPuzzleBackgroundCompileTasks((current) => ({
        ...current,
        [nextSession.sessionId]: {
          session: nextSession,
          payload,
          generationState,
          error: null,
        },
      }));
      puzzleFlow.setSession(nextSession);
      setPuzzleGenerationState(generationState);
      markDraftGenerating('puzzle', [
        nextSession.sessionId,
        buildPuzzleResultWorkId(nextSession.sessionId),
        nextSession.publishedProfileId,
        buildPuzzleResultProfileId(nextSession.sessionId),
      ]);
      markPendingDraftGenerating(
        'puzzle',
        nextSession.sessionId,
        buildPendingPuzzleDraftMetadata(payload),
      );
      if (shouldConsumePuzzleDraftPoints) {
        adjustProfileWalletBalanceLocally(-PUZZLE_DRAFT_GENERATION_POINT_COST);
      }
      selectionStageRef.current = 'puzzle-generating';
      activePuzzleGenerationSessionIdRef.current = nextSession.sessionId;
      setSelectionStage('puzzle-generating');

      try {
        const actionPayload = buildPuzzleCompileActionFromFormPayload(payload);
        const response = await executePuzzleAgentAction(
          nextSession.sessionId,
          actionPayload,
        );
        setPuzzleOperation(response.operation);
        const openResult = isViewingPuzzleGeneration(nextSession.sessionId);
        const readyGenerationState =
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'ready',
            {
              completedAssetCount: 1,
              totalAssetCount: 1,
            },
          );
        setPuzzleBackgroundCompileTasks((current) => ({
          ...current,
          [nextSession.sessionId]: {
            session: response.session,
            payload,
            generationState: readyGenerationState,
            error: null,
          },
        }));
        if (isViewingPuzzleGeneration(nextSession.sessionId)) {
          puzzleFlow.setSession(response.session);
          setPuzzleGenerationState(readyGenerationState);
        }

        const profileId =
          response.session.publishedProfileId ??
          buildPuzzleResultProfileId(response.session.sessionId);
        markPendingDraftReady('puzzle', response.session.sessionId, openResult);
        markDraftReady(
          'puzzle',
          [
            response.session.sessionId,
            buildPuzzleResultWorkId(response.session.sessionId),
            profileId,
          ],
          openResult,
        );
        void refreshPuzzleShelf();

        if (openResult && response.session.draft) {
          const draft = response.session.draft;
          const draftProfileId =
            response.session.publishedProfileId ??
            buildPuzzleResultProfileId(response.session.sessionId);
          if (!draft.coverImageSrc || !draftProfileId) {
            setPuzzleError(
              !draft.coverImageSrc
                ? '请先选择一张正式拼图图片。'
                : '这份拼图草稿缺少会话信息，请重新开始创作。',
            );
            setSelectionStage('puzzle-result');
            return;
          }

          try {
            const { item } = await updatePuzzleWork(draftProfileId, {
              workTitle: draft.workTitle,
              workDescription: draft.workDescription,
              levelName: draft.levelName,
              summary: draft.summary,
              themeTags: draft.themeTags,
              coverImageSrc: draft.coverImageSrc,
              coverAssetId: draft.coverAssetId,
              levels: draft.levels ?? [],
            });
            const run = startLocalPuzzleRun(item);
            setSelectedPuzzleDetail(item);
            setPuzzleRun(run);
            setPuzzleRuntimeAuthMode('default');
            setPuzzleRuntimeReturnStage('puzzle-result');
            pushPuzzleResultHistoryEntry(response.session);
            openPuzzleRuntimeStage(
              setSelectionStage,
              buildPuzzleDraftRuntimeUrlState(item, null),
            );
          } catch (error) {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '启动拼图试玩失败。'),
            );
            setSelectionStage('puzzle-result');
          }
        }
      } catch (error) {
        const errorMessage = resolvePuzzleErrorMessage(
          error,
          '执行拼图操作失败。',
        );
        const recovered = await recoverCompletedPuzzleDraftGeneration({
          sessionId: nextSession.sessionId,
          payload,
          generationState,
          setSession: puzzleFlow.setSession,
        });
        if (recovered) {
          return;
        }
        if (shouldConsumePuzzleDraftPoints) {
          adjustProfileWalletBalanceLocally(PUZZLE_DRAFT_GENERATION_POINT_COST);
        }
        const failedGenerationState =
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          );
        const openFailure = isViewingPuzzleGeneration(nextSession.sessionId);
        setPuzzleBackgroundCompileTasks((current) => ({
          ...current,
          [nextSession.sessionId]: {
            session: nextSession,
            payload,
            generationState: failedGenerationState,
            error: errorMessage,
          },
        }));
        markPendingDraftFailed('puzzle', nextSession.sessionId);
        markDraftFailed(
          'puzzle',
          [
            nextSession.sessionId,
            buildPuzzleResultWorkId(nextSession.sessionId),
            nextSession.publishedProfileId,
            buildPuzzleResultProfileId(nextSession.sessionId),
          ],
          errorMessage,
          !openFailure,
        );
        void refreshPuzzleShelf();
        if (openFailure) {
          setPuzzleError(errorMessage);
          setPuzzleGenerationState(failedGenerationState);
        }
      } finally {
        refreshPlatformDashboardSilently();
      }
    },
    [
      adjustProfileWalletBalanceLocally,
      markDraftGenerating,
      markDraftFailed,
      markDraftReady,
      markPendingDraftFailed,
      markPendingDraftGenerating,
      markPendingDraftReady,
      isViewingPuzzleGeneration,
      preflightPuzzleDraftGeneration,
      puzzleFlow,
      refreshPuzzleShelf,
      recoverCompletedPuzzleDraftGeneration,
      refreshPlatformDashboardSilently,
      resolvePuzzleErrorMessage,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const createMatch3DDraftFromForm = useCallback(
    async (payload: CreateMatch3DSessionRequest) => {
      setMatch3DFormDraftPayload(payload);
      setMatch3DError(null);
      setStreamingMatch3DReplyText('');
      setIsStreamingMatch3DReply(false);

      if (!(await preflightMatch3DDraftGeneration())) {
        return;
      }

      let nextSession: Match3DAgentSessionSnapshot;
      try {
        const response = await match3dCreationClient.createSession(payload);
        nextSession = response.session;
      } catch (error) {
        setMatch3DError(
          resolveMatch3DErrorMessage(error, '开启抓大鹅共创工作台失败。'),
        );
        return;
      }

      const generationState = createMiniGameDraftGenerationState(
        'match3d',
        resolveMiniGameDraftGenerationStartedAtMs(nextSession.updatedAt),
      );
      setMatch3DBackgroundCompileTasks((current) => ({
        ...current,
        [nextSession.sessionId]: {
          session: nextSession,
          payload,
          generationState,
          error: null,
        },
      }));
      setMatch3DSession(nextSession);
      setMatch3DProfile(null);
      setMatch3DRuntimeProfile(null);
      setMatch3DRun(null);
      setMatch3DGenerationState(generationState);
      markDraftGenerating('match3d', [
        nextSession.draft?.profileId,
        nextSession.publishedProfileId,
        nextSession.sessionId,
      ]);
      markPendingDraftGenerating(
        'match3d',
        nextSession.sessionId,
        buildPendingMatch3DDraftMetadata(payload),
      );
      adjustProfileWalletBalanceLocally(-MATCH3D_DRAFT_GENERATION_POINT_COST);
      selectionStageRef.current = 'match3d-generating';
      activeMatch3DGenerationSessionIdRef.current = nextSession.sessionId;
      setSelectionStage('match3d-generating');

      try {
        const response = await match3dCreationClient.executeAction(
          nextSession.sessionId,
          {
            action: 'match3d_compile_draft',
            generateClickSound: payload.generateClickSound,
          },
        );
        const openResult = isViewingMatch3DGeneration(nextSession.sessionId);
        const readyGenerationState =
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'ready',
            {
              completedAssetCount:
                response.session.draft?.generatedItemAssets?.length ?? 5,
              totalAssetCount:
                response.session.draft?.generatedItemAssets?.length ?? 5,
            },
          );
        setMatch3DBackgroundCompileTasks((current) => ({
          ...current,
          [nextSession.sessionId]: {
            session: response.session,
            payload,
            generationState: readyGenerationState,
            error: null,
          },
        }));
        if (isViewingMatch3DGeneration(nextSession.sessionId)) {
          setMatch3DSession(response.session);
          setMatch3DGenerationState(readyGenerationState);
        }

        const profileId = response.session.draft?.profileId;
        if (!profileId) {
          if (isViewingMatch3DGeneration(nextSession.sessionId)) {
            setMatch3DProfile(null);
            setMatch3DRuntimeProfile(null);
          }
          return;
        }

        let runtimeProfile: Match3DWorkProfile | null = null;
        try {
          const { item } = await getMatch3DWorkDetail(profileId);
          runtimeProfile = normalizeMatch3DWorkForRuntimeUi({
            ...item,
            generatedItemAssets: mergeMatch3DGeneratedItemAssetsForRuntime(
              response.session.draft?.generatedItemAssets,
              item.generatedItemAssets,
            ),
          });
          if (isViewingMatch3DGeneration(nextSession.sessionId)) {
            setMatch3DProfile(runtimeProfile);
          }
          await refreshMatch3DShelf().catch(() => undefined);
        } catch {
          runtimeProfile = buildMatch3DProfileFromSession(response.session);
          if (isViewingMatch3DGeneration(nextSession.sessionId)) {
            setMatch3DProfile(runtimeProfile);
          }
        }

        markPendingDraftReady(
          'match3d',
          response.session.sessionId,
          openResult,
        );
        markDraftReady(
          'match3d',
          [profileId, response.session.sessionId],
          openResult,
        );
        if (openResult && runtimeProfile) {
          try {
            await preloadMatch3DGeneratedRuntimeAssets(
              runtimeProfile.generatedItemAssets,
              runtimeProfile.generatedBackgroundAsset,
              { expireSeconds: 300 },
            );
            const { run } = await match3dRuntimeAdapter.startRun(
              runtimeProfile.profileId,
            );
            setMatch3DRuntimeProfile(runtimeProfile);
            setMatch3DRun(run);
            setMatch3DProfile(runtimeProfile);
            setMatch3DRuntimeReturnStage('match3d-result');
            setSelectionStage('match3d-runtime');
          } catch (error) {
            setMatch3DError(
              resolveMatch3DErrorMessage(error, '启动抓大鹅玩法失败。'),
            );
            setSelectionStage('match3d-result');
          }
        }
      } catch (error) {
        const errorMessage = resolveMatch3DErrorMessage(
          error,
          '执行抓大鹅操作失败。',
        );
        if (isBackgroundGenerationStillRunningMessage(errorMessage)) {
          try {
            const { session: latestSession } =
              await match3dCreationClient.getSession(nextSession.sessionId);
            markPendingDraftGenerating('match3d', latestSession.sessionId);
            markDraftGenerating('match3d', [
              latestSession.draft?.profileId,
              latestSession.publishedProfileId,
              latestSession.sessionId,
            ]);
            setMatch3DBackgroundCompileTasks((current) => ({
              ...current,
              [nextSession.sessionId]: {
                session: latestSession,
                payload,
                generationState,
                error: errorMessage,
              },
            }));
            if (isViewingMatch3DGeneration(nextSession.sessionId)) {
              setMatch3DError(errorMessage);
              setMatch3DSession(latestSession);
              setMatch3DGenerationState(generationState);
              const profileId =
                latestSession.draft?.profileId ??
                latestSession.publishedProfileId;
              if (profileId) {
                const { item } = await getMatch3DWorkDetail(profileId);
                setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(item));
              }
            }
            await refreshMatch3DShelf().catch(() => undefined);
            return;
          } catch {
            await refreshMatch3DShelf().catch(() => undefined);
          }
        }
        adjustProfileWalletBalanceLocally(MATCH3D_DRAFT_GENERATION_POINT_COST);
        const failedGenerationState =
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          );
        const openFailure = isViewingMatch3DGeneration(nextSession.sessionId);
        setMatch3DBackgroundCompileTasks((current) => ({
          ...current,
          [nextSession.sessionId]: {
            session: nextSession,
            payload,
            generationState: failedGenerationState,
            error: errorMessage,
          },
        }));
        markPendingDraftFailed('match3d', nextSession.sessionId);
        markDraftFailed(
          'match3d',
          [
            nextSession.draft?.profileId,
            nextSession.publishedProfileId,
            nextSession.sessionId,
          ],
          errorMessage,
          !openFailure,
        );
        if (openFailure) {
          setMatch3DError(errorMessage);
          setMatch3DGenerationState(failedGenerationState);
        }
        try {
          const { session: latestSession } =
            await match3dCreationClient.getSession(nextSession.sessionId);
          markDraftFailed(
            'match3d',
            [
              latestSession.draft?.profileId,
              latestSession.publishedProfileId,
              latestSession.sessionId,
            ],
            errorMessage,
            !openFailure,
          );
          setMatch3DBackgroundCompileTasks((current) => ({
            ...current,
            [nextSession.sessionId]: {
              session: latestSession,
              payload,
              generationState: failedGenerationState,
              error: errorMessage,
            },
          }));
          if (openFailure) {
            setMatch3DSession(latestSession);
            const profileId =
              latestSession.draft?.profileId ??
              latestSession.publishedProfileId;
            if (profileId) {
              const { item } = await getMatch3DWorkDetail(profileId);
              setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(item));
            }
          }
          await refreshMatch3DShelf().catch(() => undefined);
        } catch {
          await refreshMatch3DShelf().catch(() => undefined);
        }
      } finally {
        refreshPlatformDashboardSilently();
      }
    },
    [
      adjustProfileWalletBalanceLocally,
      match3dRuntimeAdapter,
      isViewingMatch3DGeneration,
      markDraftGenerating,
      markDraftFailed,
      markDraftReady,
      markPendingDraftFailed,
      markPendingDraftGenerating,
      markPendingDraftReady,
      preflightMatch3DDraftGeneration,
      refreshMatch3DShelf,
      refreshPlatformDashboardSilently,
      resolveMatch3DErrorMessage,
      setIsStreamingMatch3DReply,
      setMatch3DError,
      setMatch3DProfile,
      setMatch3DRun,
      setMatch3DSession,
      setSelectionStage,
      setStreamingMatch3DReplyText,
    ],
  );

  const createVisualNovelDraftFromForm = useCallback(
    async (payload: VisualNovelEntryFormPayload) => {
      setVisualNovelFormDraftPayload(payload);
      setVisualNovelGenerationStartedAtMs(Date.now());
      setVisualNovelGenerationPhase('generating');
      setVisualNovelWork(null);
      setVisualNovelRun(null);
      setVisualNovelRuntimeReturnStage('visual-novel-result');
      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      setSelectionStage('visual-novel-generating');

      try {
        const createResponse = await createVisualNovelSession({
          sourceMode: payload.sourceMode,
          seedText: payload.seedText,
          sourceAssetIds: payload.sourceAssetIds,
        });
        setVisualNovelSession(createResponse.session);
        markDraftGenerating('visual-novel', [createResponse.session.sessionId]);
        markPendingDraftGenerating(
          'visual-novel',
          createResponse.session.sessionId,
        );
        const nextSession = await streamVisualNovelMessage(
          createResponse.session.sessionId,
          {
            clientMessageId: `visual-novel-entry-${Date.now().toString(36)}`,
            text: payload.seedText,
          },
        );
        setVisualNovelSession(nextSession);
        setVisualNovelGenerationPhase('ready');
        const openResult =
          selectionStageRef.current === 'visual-novel-generating';
        if (
          nextSession.draft?.profileId &&
          nextSession.draft.profileId !== nextSession.sessionId
        ) {
          clearPendingDraftShelfItem('visual-novel', nextSession.sessionId);
        }
        markPendingDraftReady(
          'visual-novel',
          nextSession.draft?.profileId ?? nextSession.sessionId,
          openResult,
        );
        markDraftReady(
          'visual-novel',
          [nextSession.draft?.profileId, nextSession.sessionId],
          openResult,
        );
        void refreshVisualNovelShelf();
        if (openResult) {
          setSelectionStage('visual-novel-result');
        }
      } catch (error) {
        setVisualNovelGenerationPhase('failed');
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '生成视觉小说草稿失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      markDraftReady,
      markDraftGenerating,
      markPendingDraftGenerating,
      markPendingDraftReady,
      clearPendingDraftShelfItem,
      refreshVisualNovelShelf,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      setVisualNovelSession,
    ],
  );

  const createBabyObjectMatchDraftFromForm = useCallback(
    async (payload: CreateBabyObjectMatchDraftRequest) => {
      setBabyObjectMatchFormPayload(payload);
      setBabyObjectMatchDraft(null);
      setBabyObjectMatchError(null);
      setIsBabyObjectMatchBusy(true);
      setBabyObjectMatchGenerationPhase('generating');
      setBabyObjectMatchGenerationState(
        createMiniGameDraftGenerationState('baby-object-match'),
      );
      selectionStageRef.current = 'baby-object-match-generating';
      setSelectionStage('baby-object-match-generating');

      try {
        const response = await createBabyObjectMatchDraft(payload);
        setBabyObjectMatchDraft(response.draft);
        writeCreationUrlState(
          buildBabyObjectMatchCreationUrlState(response.draft),
        );
        void refreshBabyObjectMatchShelf();
        setBabyObjectMatchGenerationPhase('ready');
        setBabyObjectMatchGenerationState((current) =>
          current
            ? resolveFinishedMiniGameDraftGenerationState(current, 'ready', {
                completedAssetCount: response.draft.itemAssets.length,
                totalAssetCount: response.draft.itemAssets.length,
              })
            : current,
        );
        const openResult =
          selectionStageRef.current === 'baby-object-match-generating';
        markDraftReady(
          'baby-object-match',
          [response.draft.profileId, response.draft.draftId],
          openResult,
        );
        if (openResult) {
          setSelectionStage('baby-object-match-result');
        }
      } catch (error) {
        const errorMessage = resolvePuzzleErrorMessage(
          error,
          '生成宝贝识物草稿失败。',
        );
        setBabyObjectMatchGenerationPhase('failed');
        setBabyObjectMatchError(errorMessage);
        setBabyObjectMatchGenerationState((current) =>
          current
            ? resolveFinishedMiniGameDraftGenerationState(current, 'failed', {
                error: errorMessage,
              })
            : current,
        );
      } finally {
        setIsBabyObjectMatchBusy(false);
      }
    },
    [
      markDraftReady,
      refreshBabyObjectMatchShelf,
      resolvePuzzleErrorMessage,
      setSelectionStage,
    ],
  );

  const savePuzzleFormDraft = useCallback(
    async (payload: CreatePuzzleAgentSessionRequest) => {
      const session = puzzleFlow.session;
      if (!session || session.stage !== 'collecting_anchors') {
        return;
      }

      setPuzzleFormDraftPayload(payload);

      try {
        const response = await executePuzzleAgentAction(session.sessionId, {
          action: 'save_puzzle_form_draft',
          promptText: payload.pictureDescription ?? null,
          workTitle: payload.workTitle,
          workDescription:
            payload.workDescription ?? payload.pictureDescription ?? '',
          pictureDescription: payload.pictureDescription ?? '',
          referenceImageSrc: payload.referenceImageSrc ?? null,
          referenceImageSrcs: payload.referenceImageSrcs ?? [],
          referenceImageAssetObjectId:
            payload.referenceImageAssetObjectId ?? null,
          referenceImageAssetObjectIds:
            payload.referenceImageAssetObjectIds ?? [],
          imageModel: payload.imageModel ?? null,
          aiRedraw: payload.aiRedraw ?? true,
        });
        setPuzzleOperation(response.operation);
        puzzleFlow.setSession(response.session);
        setPuzzleError(null);
        void refreshPuzzleShelf();
      } catch (error) {
        setPuzzleError(
          resolvePuzzleErrorMessage(error, '保存拼图表单草稿失败。'),
        );
      }
    },
    [puzzleFlow, refreshPuzzleShelf, resolvePuzzleErrorMessage, setPuzzleError],
  );

  useEffect(() => {
    if (platformBootstrap.canReadProtectedData) {
      hadReadableProtectedDataRef.current = true;
      return;
    }

    if (authUi?.user || !hadReadableProtectedDataRef.current) {
      return;
    }

    hadReadableProtectedDataRef.current = false;

    // 创作中心只展示当前登录用户的私有作品。
    // 一旦退出登录或鉴权上下文被收回，三类作品缓存必须同步清空，不能等刷新页面。
    setShowCreationTypeModal(false);
    setSelectedDetailEntry(null);
    setSelectedPublicWorkDetail(null);
    setPublicWorkDetailError(null);
    setIsPublicWorkDetailBusy(false);
    setBigFishWorks([]);
    setBigFishRun(null);
    setBigFishRuntimeShare(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRuntimeReturnStage('platform');
    setBigFishGenerationState(null);
    setBigFishError(null);
    setMatch3DSession(null);
    setMatch3DProfile(null);
    setMatch3DRuntimeProfile(null);
    setMatch3DFormDraftPayload(null);
    setMatch3DBackgroundCompileTasks({});
    activeMatch3DGenerationSessionIdRef.current = null;
    setMatch3DWorks([]);
    setMatch3DGalleryEntries([]);
    setMatch3DRun(null);
    setMatch3DRuntimeReturnStage('match3d-result');
    setMatch3DError(null);
    setStreamingMatch3DReplyText('');
    setIsStreamingMatch3DReply(false);
    setSquareHoleSession(null);
    setSquareHoleProfile(null);
    setSquareHoleWorks([]);
    setSquareHoleGalleryEntries([]);
    setSquareHoleRun(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setSquareHoleGenerationState(null);
    setSquareHoleError(null);
    setStreamingSquareHoleReplyText('');
    setIsStreamingSquareHoleReply(false);
    setCreativeAgentSession(null);
    setCreativeAgentEvents([]);
    setActiveCreativeAgentSessionId(null);
    setCreativeAgentError(null);
    setIsCreativeAgentBusy(false);
    setIsCreativeAgentStreaming(false);
    setCreativeDraftEditError(null);
    setIsCreativeDraftEditBusy(false);
    setPuzzleOperation(null);
    setPuzzleWorks([]);
    setSelectedPuzzleDetail(null);
    setPuzzleRuntimeReturnStage('puzzle-gallery-detail');
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleGenerationState(null);
    setPuzzleBackgroundCompileTasks({});
    activePuzzleGenerationSessionIdRef.current = null;
    setIsPuzzleNextLevelGenerating(false);
    setPuzzleShelfError(null);
    setPuzzleCreationError(null);
    setPuzzleError(null);
    setPuzzleClearSession(null);
    setPuzzleClearWork(null);
    setPuzzleClearWorks([]);
    setPuzzleClearGalleryEntries([]);
    setPuzzleClearRun(null);
    setPuzzleClearRuntimeReturnStage('puzzle-clear-result');
    setPuzzleClearGenerationState(null);
    setPuzzleClearError(null);
    setIsPuzzleClearBusy(false);
    setVisualNovelSession(null);
    setVisualNovelWork(null);
    setVisualNovelWorks([]);
    setVisualNovelGalleryEntries([]);
    setVisualNovelRun(null);
    setVisualNovelRuntimeReturnStage('visual-novel-result');
    setVisualNovelFormDraftPayload(null);
    setVisualNovelGenerationStartedAtMs(null);
    setVisualNovelGenerationPhase('generating');
    setVisualNovelError(null);
    setBabyObjectMatchDraft(null);
    setBabyObjectMatchDrafts([]);
    setBabyObjectMatchFormPayload(null);
    setBabyObjectMatchGenerationState(null);
    setBabyObjectMatchGenerationPhase('generating');
    setBabyObjectMatchRuntimeReturnStage('baby-object-match-result');
    setBabyObjectMatchError(null);
    setIsBabyObjectMatchBusy(false);
    setDeletingCreationWorkId(null);
    setClaimingPuzzlePointIncentiveProfileId(null);
    setPublishSharePayload(null);
    setProfilePlayStats(null);
    setProfilePlayStatsError(null);
    setIsProfilePlayStatsOpen(false);
    setDraftGenerationNotices({});
    setPendingDraftShelfItems({});
    setPendingPlatformTaskCompletionDialog(null);
    setPendingPlatformTaskFailureDialog(null);
    resetRpgSessionViewState();
    setRpgGeneratedCustomWorldProfile(null);
    setRpgCustomWorldError(null);
    persistRpgAgentUiState(null, null);
    resetAutoSaveTrackingToIdle();

    if (
      selectionStage !== 'platform' &&
      selectionStage !== 'work-detail' &&
      selectionStage !== 'detail' &&
      selectionStage !== 'agent-workspace' &&
      selectionStage !== 'big-fish-agent-workspace' &&
      selectionStage !== 'match3d-agent-workspace' &&
      selectionStage !== 'square-hole-agent-workspace' &&
      selectionStage !== 'jump-hop-workspace' &&
      selectionStage !== 'puzzle-clear-workspace' &&
      selectionStage !== 'wooden-fish-workspace' &&
      selectionStage !== 'puzzle-agent-workspace' &&
      selectionStage !== 'bark-battle-workspace' &&
      selectionStage !== 'visual-novel-agent-workspace' &&
      selectionStage !== 'baby-object-match-workspace' &&
      selectionStage !== 'creative-agent-workspace' &&
      selectionStage !== 'puzzle-gallery-detail'
    ) {
      setSelectionStage('platform');
    }
  }, [
    authUi?.user,
    platformBootstrap.canReadProtectedData,
    persistRpgAgentUiState,
    resetAutoSaveTrackingToIdle,
    resetRpgSessionViewState,
    selectionStage,
    setBigFishError,
    setIsStreamingMatch3DReply,
    setIsStreamingSquareHoleReply,
    setMatch3DError,
    setMatch3DSession,
    setPuzzleError,
    setRpgCustomWorldError,
    setRpgGeneratedCustomWorldProfile,
    setSelectionStage,
    setBabyObjectMatchError,
    setSquareHoleError,
    setSquareHoleSession,
    setStreamingMatch3DReplyText,
    setStreamingSquareHoleReplyText,
    setVisualNovelError,
    setVisualNovelSession,
  ]);

  const handleCreationHubCreateType = useCallback(
    (type: PlatformCreationTypeId) => {
      if (type === 'airp') {
        return;
      }

      if (!prepareCreationLaunch()) {
        return;
      }

      if (type === 'baby-object-match' && !isBabyObjectMatchVisible) {
        sessionController.setCreationTypeError(EDUTAINMENT_HIDDEN_MESSAGE);
        return;
      }

      if (type === 'rpg') {
        runProtectedAction(() => {
          void sessionController.openRpgAgentWorkspace();
        });
        return;
      }

      if (type === 'big-fish') {
        runProtectedAction(() => {
          void openBigFishAgentWorkspace();
        });
        return;
      }

      if (type === 'match3d') {
        runProtectedAction(() => {
          void openMatch3DWorkspace();
        });
        return;
      }

      if (type === 'square-hole') {
        runProtectedAction(() => {
          void openSquareHoleAgentWorkspace();
        });
        return;
      }

      if (type === 'jump-hop') {
        runProtectedAction(() => {
          void openJumpHopWorkspace();
        });
        return;
      }

      if (type === 'puzzle-clear') {
        runProtectedAction(() => {
          void openPuzzleClearWorkspace();
        });
        return;
      }

      if (type === 'wooden-fish') {
        runProtectedAction(() => {
          void openWoodenFishWorkspace();
        });
        return;
      }

      if (type === 'puzzle') {
        runProtectedAction(() => {
          void openPuzzleWorkspace();
        });
        return;
      }

      if (type === 'bark-battle') {
        runProtectedAction(() => {
          void openBarkBattleWorkspace();
        });
        return;
      }

      if (type === 'visual-novel') {
        runProtectedAction(() => {
          void openVisualNovelWorkspace();
        });
        return;
      }

      if (type === 'baby-object-match') {
        runProtectedAction(() => {
          void openBabyObjectMatchWorkspace();
        });
      }
    },
    [
      isBabyObjectMatchVisible,
      openBarkBattleWorkspace,
      openBigFishAgentWorkspace,
      openBabyObjectMatchWorkspace,
      openJumpHopWorkspace,
      openMatch3DWorkspace,
      openPuzzleClearWorkspace,
      prepareCreationLaunch,
      openPuzzleWorkspace,
      openSquareHoleAgentWorkspace,
      openVisualNovelWorkspace,
      openWoodenFishWorkspace,
      sessionController,
      runProtectedAction,
    ],
  );

  const leaveBigFishFlow = useCallback(() => {
    setBigFishRun(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRuntimeReturnStage('platform');
    setBigFishGenerationState(null);
    bigFishFlow.leaveFlow();
    returnToCreationFlowSource();
  }, [bigFishFlow, returnToCreationFlowSource]);

  const leaveMatch3DFlow = useCallback(() => {
    setMatch3DRun(null);
    setMatch3DRuntimeProfile(null);
    setMatch3DFormDraftPayload(null);
    setMatch3DGenerationState(null);
    setMatch3DRuntimeReturnStage('match3d-result');
    match3dFlow.leaveFlow();
    returnToCreationFlowSource();
  }, [match3dFlow, returnToCreationFlowSource, setMatch3DFormDraftPayload]);

  const leaveSquareHoleFlow = useCallback(() => {
    setSquareHoleRun(null);
    setSquareHoleRuntimeReturnStage('square-hole-result');
    setSquareHoleGenerationState(null);
    squareHoleFlow.leaveFlow();
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource, squareHoleFlow]);

  const leaveJumpHopFlow = useCallback(() => {
    setJumpHopRun(null);
    setJumpHopWork(null);
    setJumpHopRuntimeReturnStage('jump-hop-result');
    setJumpHopGenerationState(null);
    setJumpHopSession(null);
    setJumpHopRuntimeRequestOptions(null);
    setJumpHopError(null);
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource]);

  const leavePuzzleClearFlow = useCallback(() => {
    setPuzzleClearRun(null);
    setPuzzleClearWork(null);
    setPuzzleClearRuntimeReturnStage('puzzle-clear-result');
    setPuzzleClearGenerationState(null);
    setPuzzleClearSession(null);
    setPuzzleClearError(null);
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource]);

  const leaveWoodenFishFlow = useCallback(() => {
    setWoodenFishRun(null);
    setWoodenFishWork(null);
    setWoodenFishRuntimeReturnStage('wooden-fish-result');
    setWoodenFishGenerationState(null);
    setWoodenFishSession(null);
    setWoodenFishError(null);
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource]);

  const createReadyJumpHopGenerationState = useCallback(
    (state: MiniGameDraftGenerationState) =>
      resolveFinishedMiniGameDraftGenerationState(state, 'ready', {
        completedAssetCount: 2,
        totalAssetCount: 2,
      }),
    [],
  );

  const createReadyPuzzleClearGenerationState = useCallback(
    (state: MiniGameDraftGenerationState) =>
      resolveFinishedMiniGameDraftGenerationState(state, 'ready', {
        completedAssetCount: 135,
        totalAssetCount: 135,
      }),
    [],
  );

  const createReadyWoodenFishGenerationState = useCallback(
    (state: MiniGameDraftGenerationState) =>
      resolveFinishedMiniGameDraftGenerationState(state, 'ready', {
        completedAssetCount: 2,
        totalAssetCount: 2,
      }),
    [],
  );

  const leaveBarkBattleFlow = useCallback(() => {
    setBarkBattleDraftConfig(null);
    setBarkBattlePublishedConfig(null);
    setBarkBattleRuntimeMode('draft');
    setBarkBattleRuntimeReturnStage('platform');
    setBarkBattleError(null);
    setBarkBattleGenerationPartialFailed(false);
    setIsBarkBattleBusy(false);
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource]);

  const createBarkBattleGeneratingDraft = useCallback(
    async (payload: BarkBattleConfigEditorPayload) => {
      setBarkBattleError(null);
      const hasEnoughPoints = await preflightBarkBattleDraftGeneration();
      if (!hasEnoughPoints) {
        return;
      }
      setIsBarkBattleBusy(true);
      setBarkBattleGenerationPartialFailed(false);
      try {
        const draft = await createBarkBattleDraft(payload);
        setBarkBattleDraftConfig(draft);
        writeCreationUrlState(buildBarkBattleCreationUrlState(draft));
        setBarkBattlePublishedConfig(null);
        markDraftGenerating('bark-battle', [draft.workId, draft.draftId]);
        markPendingDraftGenerating(
          'bark-battle',
          draft.workId ?? draft.draftId,
        );
        updateBarkBattleWorkCaches(
          buildBarkBattleWorkSummaryFromDraft(
            draft,
            authUi?.user,
            'pending_assets',
          ),
        );
        void refreshBarkBattleShelf().catch(() => undefined);
        selectionStageRef.current = 'bark-battle-generating';
        setSelectionStage('bark-battle-generating');
      } catch (error) {
        setBarkBattleError(
          resolvePuzzleErrorMessage(error, '生成汪汪声浪草稿失败。'),
        );
      } finally {
        setIsBarkBattleBusy(false);
      }
    },
    [
      authUi?.user,
      markDraftGenerating,
      markPendingDraftGenerating,
      preflightBarkBattleDraftGeneration,
      refreshBarkBattleShelf,
      resolvePuzzleErrorMessage,
      setSelectionStage,
      updateBarkBattleWorkCaches,
    ],
  );

  const handleBarkBattleGenerationComplete = useCallback(
    (draft: BarkBattleDraftConfig, partialFailed: boolean) => {
      setBarkBattleDraftConfig(draft);
      writeCreationUrlState(buildBarkBattleCreationUrlState(draft));
      setBarkBattlePublishedConfig(null);
      setBarkBattleGenerationPartialFailed(partialFailed);
      const generationStatus = resolveBarkBattleDraftGenerationStatus(
        draft,
        partialFailed,
      );
      updateBarkBattleWorkCaches(
        buildBarkBattleWorkSummaryFromDraft(
          draft,
          authUi?.user,
          generationStatus,
        ),
      );
      markPendingDraftReady('bark-battle', draft.workId ?? draft.draftId, true);
      markDraftReady('bark-battle', [draft.workId, draft.draftId], true);
      void refreshBarkBattleShelf().catch(() => undefined);
      selectionStageRef.current = 'bark-battle-result';
      setSelectionStage('bark-battle-result');
    },
    [
      authUi?.user,
      markDraftReady,
      markPendingDraftReady,
      refreshBarkBattleShelf,
      setSelectionStage,
      updateBarkBattleWorkCaches,
    ],
  );

  const handleBarkBattleDraftChange = useCallback(
    async (draft: BarkBattleDraftConfig) => {
      setBarkBattleError(null);
      setBarkBattleGenerationPartialFailed(false);
      setBarkBattleDraftConfig(draft);
      if (!draft.draftId?.trim() || !draft.workId?.trim()) {
        return;
      }
      setIsBarkBattleBusy(true);
      try {
        const persistedDraft = await updateBarkBattleDraftConfig({
          draftId: draft.draftId,
          workId: draft.workId,
          configVersion: draft.configVersion,
          rulesetVersion: draft.rulesetVersion,
          title: draft.title,
          description: draft.description,
          themeDescription: draft.themeDescription,
          playerImageDescription: draft.playerImageDescription,
          opponentImageDescription: draft.opponentImageDescription,
          onomatopoeia: draft.onomatopoeia,
          playerCharacterImageSrc: draft.playerCharacterImageSrc,
          opponentCharacterImageSrc: draft.opponentCharacterImageSrc,
          uiBackgroundImageSrc: draft.uiBackgroundImageSrc,
          difficultyPreset: draft.difficultyPreset,
        });
        const nextDraft = {
          ...persistedDraft,
          playerCharacterImageSrc:
            draft.playerCharacterImageSrc ??
            persistedDraft.playerCharacterImageSrc,
          opponentCharacterImageSrc:
            draft.opponentCharacterImageSrc ??
            persistedDraft.opponentCharacterImageSrc,
          uiBackgroundImageSrc:
            draft.uiBackgroundImageSrc ?? persistedDraft.uiBackgroundImageSrc,
        };
        setBarkBattleDraftConfig(nextDraft);
        writeCreationUrlState(buildBarkBattleCreationUrlState(nextDraft));
        updateBarkBattleWorkCaches(
          buildBarkBattleWorkSummaryFromDraft(
            nextDraft,
            authUi?.user,
            resolveBarkBattleDraftGenerationStatus(nextDraft, false),
          ),
        );
        void refreshBarkBattleShelf().catch(() => undefined);
      } catch (error) {
        setBarkBattleError(
          resolvePuzzleErrorMessage(error, '保存汪汪声浪草稿素材失败。'),
        );
      } finally {
        setIsBarkBattleBusy(false);
      }
    },
    [
      authUi?.user,
      refreshBarkBattleShelf,
      resolvePuzzleErrorMessage,
      updateBarkBattleWorkCaches,
    ],
  );

  const buildBarkBattleDraftRuntimeConfig = useCallback(
    (draft: BarkBattleDraftConfig): BarkBattlePublishedConfig => ({
      workId: draft.workId ?? draft.draftId,
      draftId: draft.draftId,
      configVersion: draft.configVersion ?? 1,
      rulesetVersion: draft.rulesetVersion ?? 'bark-battle-ruleset-v1',
      playTypeId: 'bark-battle',
      title: draft.title,
      description: draft.description,
      themeDescription: draft.themeDescription,
      playerImageDescription: draft.playerImageDescription,
      opponentImageDescription: draft.opponentImageDescription,
      onomatopoeia: draft.onomatopoeia,
      playerCharacterImageSrc: draft.playerCharacterImageSrc,
      opponentCharacterImageSrc: draft.opponentCharacterImageSrc,
      uiBackgroundImageSrc: draft.uiBackgroundImageSrc,
      difficultyPreset: draft.difficultyPreset,
      updatedAt: draft.updatedAt,
      publishedAt: draft.updatedAt,
    }),
    [],
  );

  const testBarkBattleDraft = useCallback(
    (draft: BarkBattleDraftConfig) => {
      setBarkBattleError(null);
      setBarkBattleRuntimeMode('draft');
      setBarkBattleRuntimeReturnStage('bark-battle-result');
      setBarkBattlePublishedConfig(buildBarkBattleDraftRuntimeConfig(draft));
      selectionStageRef.current = 'bark-battle-runtime';
      setSelectionStage('bark-battle-runtime');
    },
    [buildBarkBattleDraftRuntimeConfig, setSelectionStage],
  );

  const publishBarkBattleDraft = useCallback(
    async (draft: BarkBattleDraftConfig) => {
      setBarkBattleError(null);
      const workId = draft.workId?.trim();
      if (!workId) {
        setBarkBattleError(
          '这份汪汪声浪草稿缺少作品ID，请重新生成草稿后再发布。',
        );
        return;
      }
      setIsBarkBattleBusy(true);
      try {
        const publishedSnapshot: BarkBattleConfigEditorPayload = {
          title: draft.title,
          description: draft.description,
          themeDescription: draft.themeDescription,
          playerImageDescription: draft.playerImageDescription,
          opponentImageDescription: draft.opponentImageDescription,
          onomatopoeia: draft.onomatopoeia,
          ...(draft.playerCharacterImageSrc
            ? { playerCharacterImageSrc: draft.playerCharacterImageSrc }
            : {}),
          ...(draft.opponentCharacterImageSrc
            ? { opponentCharacterImageSrc: draft.opponentCharacterImageSrc }
            : {}),
          ...(draft.uiBackgroundImageSrc
            ? { uiBackgroundImageSrc: draft.uiBackgroundImageSrc }
            : {}),
          difficultyPreset: draft.difficultyPreset,
        };
        const published = await publishBarkBattleWork({
          draftId: draft.draftId,
          workId,
          publishedSnapshot,
        });
        const publishedWithAssets: BarkBattlePublishedConfig = {
          ...published,
          playerCharacterImageSrc:
            published.playerCharacterImageSrc ?? draft.playerCharacterImageSrc,
          opponentCharacterImageSrc:
            published.opponentCharacterImageSrc ??
            draft.opponentCharacterImageSrc,
          uiBackgroundImageSrc:
            published.uiBackgroundImageSrc ?? draft.uiBackgroundImageSrc,
        };
        const publicWorkCode = buildBarkBattlePublicWorkCode(
          publishedWithAssets.workId,
        );
        setBarkBattlePublishedConfig(publishedWithAssets);
        setBarkBattleRuntimeReturnStage('work-detail');
        const publishedSummary: BarkBattleWorkSummary = {
          ...publishedWithAssets,
          ownerUserId: authUi?.user?.id ?? '',
          authorDisplayName: authUi?.user?.displayName ?? '创作者',
          summary: publishedWithAssets.description ?? '',
          status: 'published',
          generationStatus: 'ready',
          publishReady: true,
          playCount: 0,
          publishedAt: publishedWithAssets.publishedAt,
        };
        updateBarkBattleWorkCaches(publishedSummary);
        setSelectedPublicWorkDetail(
          mapBarkBattleWorkToPublicWorkDetail(publishedSummary),
        );
        selectionStageRef.current = 'work-detail';
        setSelectionStage('work-detail');
        pushAppHistoryPath(
          buildPublicWorkStagePath('work-detail', publicWorkCode),
        );
        void Promise.allSettled([
          refreshBarkBattleShelf(),
          refreshBarkBattleGallery(),
        ]);
        openPublishShareModal({
          title: publishedWithAssets.title,
          publicWorkCode,
          stage: 'work-detail',
        });
      } catch (error) {
        setBarkBattleError(
          resolvePuzzleErrorMessage(error, '发布汪汪声浪大作战作品失败。'),
        );
      } finally {
        setIsBarkBattleBusy(false);
      }
    },
    [
      authUi?.user?.displayName,
      authUi?.user?.id,
      openPublishShareModal,
      refreshBarkBattleGallery,
      refreshBarkBattleShelf,
      resolvePuzzleErrorMessage,
      setSelectionStage,
      updateBarkBattleWorkCaches,
    ],
  );

  const leavePuzzleFlow = useCallback(() => {
    setPuzzleOperation(null);
    puzzleRunRef.current = null;
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setPuzzleGenerationState(null);
    setIsPuzzleNextLevelGenerating(false);
    setActiveCreativeAgentSessionId(null);
    setCreativeDraftEditError(null);
    resetRecommendRuntimeSelection();
    clearPuzzleRuntimeUrlState();
    puzzleFlow.leaveFlow();
    returnToCreationFlowSource();
  }, [puzzleFlow, resetRecommendRuntimeSelection, returnToCreationFlowSource]);

  const leaveVisualNovelFlow = useCallback(() => {
    setVisualNovelWork(null);
    setVisualNovelRun(null);
    setVisualNovelRuntimeReturnStage('visual-novel-result');
    setVisualNovelFormDraftPayload(null);
    setVisualNovelGenerationStartedAtMs(null);
    setVisualNovelGenerationPhase('generating');
    visualNovelFlow.leaveFlow();
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource, visualNovelFlow]);

  const leaveBabyObjectMatchFlow = useCallback(() => {
    setBabyObjectMatchDraft(null);
    setBabyObjectMatchFormPayload(null);
    setBabyObjectMatchGenerationState(null);
    setBabyObjectMatchGenerationPhase('generating');
    setBabyObjectMatchError(null);
    returnToCreationFlowSource();
  }, [returnToCreationFlowSource]);

  const saveBabyObjectMatchResultDraft = useCallback(
    async (draft: BabyObjectMatchDraft) => {
      setBabyObjectMatchError(null);
      setIsBabyObjectMatchBusy(true);
      try {
        const response = await saveBabyObjectMatchDraft({ draft });
        setBabyObjectMatchDraft(response.draft);
        writeCreationUrlState(
          buildBabyObjectMatchCreationUrlState(response.draft),
        );
        void refreshBabyObjectMatchShelf();
      } catch (error) {
        setBabyObjectMatchError(
          resolvePuzzleErrorMessage(error, '保存宝贝识物草稿失败。'),
        );
      } finally {
        setIsBabyObjectMatchBusy(false);
      }
    },
    [refreshBabyObjectMatchShelf, resolvePuzzleErrorMessage],
  );

  const ensureBabyObjectMatchGeneratedAssets = useCallback(
    async (draft: BabyObjectMatchDraft) => {
      if (!hasBabyObjectMatchPlaceholderAssets(draft)) {
        return draft;
      }

      const response = await regenerateBabyObjectMatchDraftAssets(draft);
      setBabyObjectMatchDraft(response.draft);
      void refreshBabyObjectMatchShelf();

      return response.draft;
    },
    [refreshBabyObjectMatchShelf],
  );

  const regenerateBabyObjectMatchResultAssets = useCallback(
    async (draft: BabyObjectMatchDraft) => {
      setBabyObjectMatchError(null);
      setIsBabyObjectMatchBusy(true);
      try {
        await ensureBabyObjectMatchGeneratedAssets(draft);
      } catch (error) {
        setBabyObjectMatchError(
          resolvePuzzleErrorMessage(error, '重新生成宝贝识物素材失败。'),
        );
      } finally {
        setIsBabyObjectMatchBusy(false);
      }
    },
    [ensureBabyObjectMatchGeneratedAssets, resolvePuzzleErrorMessage],
  );

  const publishBabyObjectMatchResultDraft = useCallback(
    async (draft: BabyObjectMatchDraft) => {
      setBabyObjectMatchError(null);
      setIsBabyObjectMatchBusy(true);
      try {
        const generatedDraft =
          await ensureBabyObjectMatchGeneratedAssets(draft);
        const response = await publishBabyObjectMatchWork({
          draft: generatedDraft,
        });
        setBabyObjectMatchDraft(response.draft);
        void refreshBabyObjectMatchShelf();
        openPublishShareModal({
          title: response.draft.workTitle,
          publicWorkCode:
            response.publicWorkCode ||
            buildBabyObjectMatchPublicWorkCode(response.draft.profileId),
          stage: 'work-detail',
        });
      } catch (error) {
        setBabyObjectMatchError(
          resolvePuzzleErrorMessage(
            error,
            '生成宝贝识物素材失败，请重试后再发布。',
          ),
        );
      } finally {
        setIsBabyObjectMatchBusy(false);
      }
    },
    [
      ensureBabyObjectMatchGeneratedAssets,
      openPublishShareModal,
      refreshBabyObjectMatchShelf,
      resolvePuzzleErrorMessage,
    ],
  );

  const startBabyObjectMatchRuntimeFromDraft = useCallback(
    async (
      draft: BabyObjectMatchDraft,
      returnStage: BabyObjectMatchRuntimeReturnStage = 'baby-object-match-result',
      options: { embedded?: boolean } = {},
    ) => {
      setBabyObjectMatchError(null);
      setIsBabyObjectMatchBusy(true);
      try {
        const generatedDraft =
          await ensureBabyObjectMatchGeneratedAssets(draft);
        setBabyObjectMatchDraft(generatedDraft);
        setBabyObjectMatchFormPayload({
          itemAName: generatedDraft.itemNames[0],
          itemBName: generatedDraft.itemNames[1],
        });
        setBabyObjectMatchRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('baby-object-match-runtime');
          const publicWorkCode =
            generatedDraft.publicationStatus === 'published'
              ? buildBabyObjectMatchPublicWorkCode(generatedDraft.profileId)
              : null;
          if (publicWorkCode) {
            pushAppHistoryPath(
              buildPublicWorkStagePath(
                'baby-object-match-runtime',
                publicWorkCode,
              ),
            );
          }
        }
        return true;
      } catch (error) {
        const message = resolvePuzzleErrorMessage(
          error,
          '生成宝贝识物素材失败，请重试后再试玩。',
        );
        setBabyObjectMatchError(message);
        if (options.embedded) {
          setActiveRecommendRuntimeError(message);
        }
        return false;
      } finally {
        setIsBabyObjectMatchBusy(false);
      }
    },
    [
      ensureBabyObjectMatchGeneratedAssets,
      resolvePuzzleErrorMessage,
      setSelectionStage,
    ],
  );

  const startBabyLoveDrawingRuntime = useCallback(() => {
    setSelectionStage('baby-love-drawing-runtime');
    pushAppHistoryPath('/runtime/baby-love-drawing');
  }, [setSelectionStage]);

  const startChildMotionDemo = useCallback(() => {
    window.location.assign('/child-motion-demo');
  }, []);

  const resolveBabyObjectMatchRuntimeDraft = useCallback(
    async (entry: PlatformPublicGalleryCard) => {
      if (!isEdutainmentGalleryEntry(entry)) {
        return null;
      }

      return (
        babyObjectMatchDrafts.find(
          (draft) => draft.profileId === entry.profileId,
        ) ??
        (await listLocalBabyObjectMatchDrafts()).find(
          (draft) => draft.profileId === entry.profileId,
        ) ??
        null
      );
    },
    [babyObjectMatchDrafts],
  );

  const startBabyObjectMatchRuntimeFromEntry = useCallback(
    async (
      entry: PlatformPublicGalleryCard,
      returnStage: BabyObjectMatchRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      const draft = await resolveBabyObjectMatchRuntimeDraft(entry);
      if (!draft) {
        setPublicWorkDetailError(
          '当前宝贝识物作品缺少本地草稿，暂时无法进入玩法。',
        );
        return false;
      }

      return await startBabyObjectMatchRuntimeFromDraft(
        draft,
        returnStage,
        options,
      );
    },
    [resolveBabyObjectMatchRuntimeDraft, startBabyObjectMatchRuntimeFromDraft],
  );

  const saveVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const currentSession = visualNovelSession;
      if (!currentSession || !currentSession.sessionId.trim()) {
        return;
      }

      setVisualNovelError(null);
      const now = new Date().toISOString();
      // 中文注释：VN-07 的保存草稿先保证结果页编辑不会丢失；
      // 后端 work profile 编译仍由“编译草稿”显式触发，避免把本地保存伪装成正式持久化。
      setVisualNovelSession({
        ...currentSession,
        draft,
        status: draft.publishReady ? 'ready' : currentSession.status,
        updatedAt: now,
      });
      if (!visualNovelWork) {
        return;
      }

      setIsVisualNovelBusy(true);
      try {
        const response = await updateVisualNovelWork(
          visualNovelWork.summary.profileId,
          { draft },
        );
        setVisualNovelWork(response.work);
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '保存视觉小说草稿失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      setVisualNovelSession,
      visualNovelSession,
      visualNovelWork,
    ],
  );

  const compileVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const sessionId = visualNovelSession?.sessionId?.trim();
      if (!sessionId) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const response = await compileVisualNovelWorkProfile(sessionId, {
          draft,
        });
        setVisualNovelSession(response.session);
        setVisualNovelWork(response.work);
        clearPendingDraftShelfItem('visual-novel', sessionId);
        setSelectionStage('visual-novel-result');
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '编译视觉小说草稿失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      clearPendingDraftShelfItem,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      setVisualNovelSession,
      visualNovelSession?.sessionId,
    ],
  );

  const publishVisualNovelDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const profileId = draft.profileId?.trim();
      if (!profileId) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const { work } = await updateVisualNovelWork(profileId, {
          draft,
        });
        setVisualNovelWork(work);
        const publishedResponse = await publishVisualNovelWork(profileId);
        setVisualNovelWork(publishedResponse.work);
        setSelectedPublicWorkDetail(
          mapVisualNovelWorkToPublicWorkDetail(publishedResponse.work.summary),
        );
        await Promise.allSettled([
          refreshVisualNovelShelf(),
          refreshVisualNovelGallery(),
        ]);
        openPublishShareModal({
          title: publishedResponse.work.summary.title,
          publicWorkCode: buildVisualNovelPublicWorkCode(
            publishedResponse.work.summary.profileId,
          ),
          stage: 'work-detail',
        });
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '发布视觉小说作品失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      openPublishShareModal,
      refreshVisualNovelGallery,
      refreshVisualNovelShelf,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
    ],
  );

  const startVisualNovelTestRunFromDraft = useCallback(
    async (draft: VisualNovelResultDraft) => {
      const profileId =
        visualNovelWork?.summary.profileId?.trim() ?? draft.profileId?.trim();
      if (!profileId) {
        const run = createMockVisualNovelRunFromDraft(draft);
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const { run } = await startVisualNovelRun(profileId, {
          profileId,
          mode: 'test',
        });
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
      } catch (error) {
        // 中文注释：VN-07 只要求结果页进入测试运行时；真实 runtime 接口未就绪时，
        // 使用当前 draft 生成本地 test run，不扩展到正式玩家运行链路。
        setVisualNovelRun(createMockVisualNovelRunFromDraft(draft));
        setVisualNovelRuntimeReturnStage('visual-novel-result');
        setSelectionStage('visual-novel-runtime');
        setVisualNovelError(
          resolvePuzzleErrorMessage(
            error,
            '已进入本地试玩，真实运行接口暂不可用。',
          ),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      setSelectionStage,
      visualNovelWork?.summary.profileId,
    ],
  );

  const startVisualNovelRunFromProfile = useCallback(
    async (
      profileId: string,
      returnStage: VisualNovelRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      const targetProfileId = profileId.trim();
      if (!targetProfileId) {
        return false;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);

      try {
        let workDetail =
          visualNovelWork?.summary.profileId === targetProfileId
            ? visualNovelWork
            : null;
        if (!workDetail) {
          const response = await getVisualNovelWorkDetail(
            targetProfileId,
            options.embedded ? RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS : {},
          );
          workDetail = response.work;
        }
        const startRunPayload = {
          profileId: targetProfileId,
          mode: 'play' as const,
        };
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const { run } = options.embedded
          ? await startVisualNovelRun(
              targetProfileId,
              startRunPayload,
              runtimeGuestOptions,
            )
          : await startVisualNovelRun(targetProfileId, startRunPayload);
        setVisualNovelWork(workDetail);
        setVisualNovelRun(run);
        setVisualNovelRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('visual-novel-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'visual-novel-runtime',
              buildVisualNovelPublicWorkCode(targetProfileId),
            ),
          );
        }
        return true;
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '启动视觉小说试玩失败。'),
        );
        return false;
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      authUi,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      visualNovelWork,
    ],
  );

  const submitVisualNovelRuntimeAction = useCallback(
    async (payload: VisualNovelRuntimeActionRequest) => {
      if (!visualNovelRun || isVisualNovelBusy) {
        return;
      }

      if (visualNovelRun.runId.startsWith('vn-run-local')) {
        return;
      }

      setVisualNovelError(null);
      setIsVisualNovelBusy(true);
      try {
        const runtimeGuestOptions =
          activeRecommendRuntimeKind === 'visual-novel'
            ? await buildRecommendRuntimeAuthOptions(authUi, true)
            : {};
        const nextRun = await streamVisualNovelRuntimeAction(
          visualNovelRun.runId,
          payload,
          runtimeGuestOptions,
        );
        setVisualNovelRun(nextRun);
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '推进视觉小说运行失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      activeRecommendRuntimeKind,
      authUi,
      isVisualNovelBusy,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setVisualNovelError,
      visualNovelRun,
    ],
  );

  const streamCreativeAgentPayloadForSession = useCallback(
    async (
      session: CreativeAgentSessionSnapshot,
      payload: StreamCreativeAgentMessageRequest,
    ) => {
      setCreativeAgentError(null);
      setIsCreativeAgentStreaming(true);
      setCreativeAgentEvents([]);
      setCreativeAgentSession((current) =>
        current
          ? {
              ...current,
              messages: [
                ...current.messages,
                {
                  id: payload.clientMessageId,
                  role: 'user',
                  kind: 'chat',
                  text: payload.content
                    .map((part) =>
                      part.type === 'input_text' ? part.text.trim() : '参考图',
                    )
                    .filter(Boolean)
                    .join(' / '),
                  createdAt: new Date().toISOString(),
                },
              ],
            }
          : current,
      );

      try {
        const nextSession = await streamCreativeAgentMessage(
          session.sessionId,
          payload,
          {
            onEvent: (event) => {
              setCreativeAgentEvents((current) => [...current, event]);
              if (event.event === 'stage') {
                setCreativeAgentSession((current) =>
                  current
                    ? {
                        ...current,
                        stage: event.data.stage,
                        updatedAt: new Date().toISOString(),
                      }
                    : current,
                );
              }
              if (event.event === 'session') {
                setCreativeAgentSession(event.data.session);
              }
            },
          },
        );
        setCreativeAgentSession(nextSession);
        if (nextSession.targetBinding) {
          setActiveCreativeAgentSessionId(nextSession.sessionId);
        }
      } catch (error) {
        setCreativeAgentError(
          resolvePuzzleErrorMessage(error, '发送智能创作消息失败。'),
        );
      } finally {
        setIsCreativeAgentStreaming(false);
      }
    },
    [resolvePuzzleErrorMessage],
  );

  const submitCreativeAgentMessage = useCallback(
    async (payload: StreamCreativeAgentMessageRequest) => {
      if (!creativeAgentSession || isCreativeAgentStreaming) {
        return;
      }

      await streamCreativeAgentPayloadForSession(creativeAgentSession, payload);
    },
    [
      creativeAgentSession,
      isCreativeAgentStreaming,
      streamCreativeAgentPayloadForSession,
    ],
  );

  const confirmCreativeTemplateSelection = useCallback(
    async (selection: PuzzleCreativeTemplateSelection) => {
      const sessionId = creativeAgentSession?.sessionId?.trim();
      if (!sessionId || isCreativeAgentBusy || isCreativeAgentStreaming) {
        return;
      }

      setIsCreativeAgentBusy(true);
      setCreativeAgentError(null);
      try {
        const response = await confirmCreativePuzzleTemplate(sessionId, {
          selection,
        });
        setCreativeAgentSession(response.session);
        if (response.session.targetBinding) {
          setActiveCreativeAgentSessionId(response.session.sessionId);
        }
      } catch (error) {
        setCreativeAgentError(
          resolvePuzzleErrorMessage(error, '确认拼图模板失败。'),
        );
      } finally {
        setIsCreativeAgentBusy(false);
      }
    },
    [
      creativeAgentSession?.sessionId,
      isCreativeAgentBusy,
      isCreativeAgentStreaming,
      resolvePuzzleErrorMessage,
    ],
  );

  const submitBigFishMessage = bigFishFlow.submitMessage;

  const submitSquareHoleMessage = squareHoleFlow.submitMessage;

  const submitPuzzleMessage = puzzleFlow.submitMessage;

  const executeBigFishAction = bigFishFlow.executeAction;

  const executeMatch3DAction = match3dFlow.executeAction;

  const executeSquareHoleAction = squareHoleFlow.executeAction;

  const retryMatch3DDraftGeneration = useCallback(() => {
    if (match3dSession?.sessionId) {
      const retryPayload =
        match3dFormDraftPayload ??
        buildMatch3DFormPayloadFromSession(match3dSession);
      void executeMatch3DAction({
        action: 'match3d_compile_draft',
        generateClickSound: retryPayload.generateClickSound,
      });
      return;
    }

    if (match3dFormDraftPayload) {
      void createMatch3DDraftFromForm(match3dFormDraftPayload);
      return;
    }
  }, [
    createMatch3DDraftFromForm,
    executeMatch3DAction,
    match3dFormDraftPayload,
    match3dSession,
  ]);

  const retrySquareHoleAssetGeneration = useCallback(() => {
    const session = squareHoleSession;
    if (!session?.draft?.profileId) {
      void executeSquareHoleAction({
        action: 'square_hole_compile_draft',
      });
      return;
    }

    void executeSquareHoleAction({
      action: 'square_hole_generate_visual_assets',
    });
  }, [executeSquareHoleAction, squareHoleSession]);

  const compileJumpHopSession = useCallback(
    async (
      created: JumpHopSessionResponse,
      payload?: JumpHopWorkspaceCreateRequest,
    ) => {
      const generationState = createMiniGameDraftGenerationState(
        'jump-hop',
        resolveMiniGameDraftGenerationStartedAtMs(created.session.updatedAt),
      );
      setJumpHopError(null);
      setJumpHopSession(created.session);
      writeCreationUrlState(
        buildJumpHopCreationUrlState({ session: created.session }),
      );
      setJumpHopWork(null);
      setJumpHopRun(null);
      setJumpHopRuntimeRequestOptions(null);
      setJumpHopGenerationState(generationState);
      setIsJumpHopBusy(true);
      setSelectionStage('jump-hop-generating');
      markDraftGenerating('jump-hop', [
        created.session.sessionId,
        created.session.draft?.profileId,
      ]);
      markPendingDraftGenerating('jump-hop', created.session.sessionId);

      try {
        const response = await jumpHopClient.executeAction(
          created.session.sessionId,
          {
            actionType: 'compile-draft',
            themeText:
              payload?.themeText ?? created.session.draft?.themeText,
            workTitle: payload?.workTitle ?? created.session.draft?.workTitle,
            workDescription:
              payload?.workDescription ??
              created.session.draft?.workDescription,
            themeTags: payload?.themeTags ?? created.session.draft?.themeTags,
            difficulty:
              payload?.difficulty ?? created.session.draft?.difficulty,
            stylePreset:
              payload?.stylePreset ?? created.session.draft?.stylePreset,
            characterPrompt:
              payload?.characterPrompt ??
              created.session.draft?.characterPrompt,
            tilePrompt:
              payload?.tilePrompt ?? created.session.draft?.tilePrompt,
            endMoodPrompt:
              payload?.endMoodPrompt ?? created.session.draft?.endMoodPrompt,
          },
        );
        const readyState = createReadyJumpHopGenerationState(generationState);
        setJumpHopSession(response.session);
        setJumpHopWork(response.work ?? null);
        writeCreationUrlState(
          buildJumpHopCreationUrlState({
            session: response.session,
            work: response.work,
          }),
        );
        setJumpHopGenerationState(readyState);
        if (response.work) {
          setJumpHopWorks((current) => [
            response.work!.summary,
            ...current.filter(
              (item) => item.workId !== response.work!.summary.workId,
            ),
          ]);
          markPendingDraftReady('jump-hop', created.session.sessionId, false);
          markDraftReady(
            'jump-hop',
            [
              created.session.sessionId,
              response.work.summary.workId,
              response.work.summary.profileId,
            ],
            false,
          );
          void refreshJumpHopShelf().catch(() => undefined);
        }
        setSelectionStage('jump-hop-result');
      } catch (error) {
        const errorMessage = resolveRpgCreationErrorMessage(
          error,
          '生成跳一跳草稿失败。',
        );
        setJumpHopError(errorMessage);
        setJumpHopGenerationState(
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          ),
        );
        markPendingDraftFailed('jump-hop', created.session.sessionId);
        markDraftFailed(
          'jump-hop',
          [created.session.sessionId, created.session.draft?.profileId],
          errorMessage,
        );
        void refreshJumpHopShelf().catch(() => undefined);
        try {
          const latest = await jumpHopClient.getSession(
            created.session.sessionId,
          );
          markDraftFailed(
            'jump-hop',
            [latest.session.sessionId, latest.session.draft?.profileId],
            errorMessage,
          );
          setJumpHopSession(latest.session);
          setJumpHopWork(null);
          writeCreationUrlState(
            buildJumpHopCreationUrlState({ session: latest.session }),
          );
        } catch {
          setJumpHopSession(created.session);
          setJumpHopWork(null);
          writeCreationUrlState(
            buildJumpHopCreationUrlState({ session: created.session }),
          );
        }
      } finally {
        setIsJumpHopBusy(false);
      }
    },
    [
      createReadyJumpHopGenerationState,
      markDraftFailed,
      markDraftGenerating,
      markPendingDraftFailed,
      markPendingDraftGenerating,
      refreshJumpHopShelf,
      setSelectionStage,
    ],
  );

  const retryJumpHopDraftGeneration = useCallback(() => {
    if (!jumpHopSession) {
      setSelectionStage('jump-hop-workspace');
      return;
    }

    void compileJumpHopSession({ session: jumpHopSession });
  }, [compileJumpHopSession, jumpHopSession, setSelectionStage]);

  const regenerateJumpHopAsset = useCallback(
    async (actionType: 'regenerate-tiles') => {
      if (!jumpHopSession?.sessionId) {
        setSelectionStage('jump-hop-workspace');
        return;
      }

      const generationState = createMiniGameDraftGenerationState('jump-hop');
      setJumpHopError(null);
      setJumpHopGenerationState(generationState);
      setIsJumpHopBusy(true);
      setSelectionStage('jump-hop-generating');
      try {
        const response = await jumpHopClient.executeAction(
          jumpHopSession.sessionId,
          {
            actionType,
            profileId:
              jumpHopWork?.summary.profileId ?? jumpHopSession.draft?.profileId,
            themeText: jumpHopSession.draft?.themeText,
            workTitle: jumpHopSession.draft?.workTitle,
            workDescription: jumpHopSession.draft?.workDescription,
            themeTags: jumpHopSession.draft?.themeTags,
            difficulty: jumpHopSession.draft?.difficulty,
            stylePreset: jumpHopSession.draft?.stylePreset,
            characterPrompt: jumpHopSession.draft?.characterPrompt,
            tilePrompt: jumpHopSession.draft?.tilePrompt,
            endMoodPrompt: jumpHopSession.draft?.endMoodPrompt,
          },
        );
        setJumpHopSession(response.session);
        setJumpHopWork(response.work ?? jumpHopWork);
        writeCreationUrlState(
          buildJumpHopCreationUrlState({
            session: response.session,
            work: response.work ?? jumpHopWork,
          }),
        );
        setJumpHopGenerationState(
          createReadyJumpHopGenerationState(generationState),
        );
        setSelectionStage('jump-hop-result');
      } catch (error) {
        const errorMessage = resolveRpgCreationErrorMessage(
          error,
          '重新生成跳一跳地块失败。',
        );
        setJumpHopError(errorMessage);
        setJumpHopGenerationState(
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          ),
        );
      } finally {
        setIsJumpHopBusy(false);
      }
    },
    [
      createReadyJumpHopGenerationState,
      jumpHopSession?.draft,
      jumpHopSession?.sessionId,
      jumpHopWork,
      setSelectionStage,
    ],
  );

  const publishJumpHopDraft = useCallback(async () => {
    const profileId = jumpHopWork?.summary.profileId?.trim();
    if (!profileId) {
      setJumpHopError('跳一跳草稿尚未生成可发布作品。');
      setSelectionStage('jump-hop-result');
      return;
    }

    setIsJumpHopBusy(true);
    setJumpHopError(null);
    try {
      const response = await jumpHopClient.publishWork(profileId);
      setJumpHopWork(response.item);
      setJumpHopWorks((current) => [
        response.item.summary,
        ...current.filter(
          (item) => item.workId !== response.item.summary.workId,
        ),
      ]);
      void refreshJumpHopShelf().catch(() => undefined);
      openPublishShareModal({
        title: response.item.summary.workTitle || '跳一跳',
        publicWorkCode: buildJumpHopPublicWorkCode(
          response.item.summary.profileId,
        ),
        stage: 'work-detail',
      });
    } catch (error) {
      setJumpHopError(
        resolveRpgCreationErrorMessage(error, '发布跳一跳作品失败。'),
      );
      setSelectionStage('jump-hop-result');
    } finally {
      setIsJumpHopBusy(false);
    }
  }, [
    jumpHopWork?.summary.profileId,
    openPublishShareModal,
    setSelectionStage,
  ]);

  const startJumpHopTestRunFromProfile = useCallback(async () => {
    const profileId = jumpHopWork?.summary.profileId?.trim();
    if (!profileId) {
      setJumpHopError('跳一跳草稿尚未生成可试玩作品。');
      setSelectionStage('jump-hop-result');
      return;
    }

    setIsJumpHopBusy(true);
    setJumpHopError(null);
    setJumpHopRuntimeReturnStage('jump-hop-result');
    try {
      const response = await jumpHopClient.startRun(profileId, {
        runtimeMode: 'draft',
      });
      setJumpHopRun(response.run);
      setSelectionStage('jump-hop-runtime');
    } catch (error) {
      setJumpHopError(
        error instanceof Error ? error.message : '启动跳一跳试玩失败。',
      );
      setSelectionStage('jump-hop-result');
    } finally {
      setIsJumpHopBusy(false);
    }
  }, [jumpHopWork?.summary.profileId, setSelectionStage]);

  const startJumpHopRunFromProfile = useCallback(
    async (
      profileId: string,
      options: {
        embedded?: boolean;
        returnStage?: 'work-detail' | 'platform';
      } = {},
    ) => {
      const normalizedProfileId = profileId.trim();
      if (!normalizedProfileId) {
        setJumpHopError('当前跳一跳作品信息不完整，暂时无法进入玩法。');
        return false;
      }

      setIsJumpHopBusy(true);
      setJumpHopError(null);
      setJumpHopRuntimeReturnStage(options.returnStage ?? 'work-detail');
      try {
        const runtimeGuestOptions =
          options.embedded || shouldUseRecommendRuntimeGuestAuth(authUi)
            ? await buildRecommendRuntimeAuthOptions(authUi, true)
            : RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS;
        setJumpHopRuntimeRequestOptions(
          runtimeGuestOptions.runtimeGuestToken?.trim()
            ? {
                runtimeGuestToken: runtimeGuestOptions.runtimeGuestToken,
                authImpact: runtimeGuestOptions.authImpact,
                skipAuth: runtimeGuestOptions.skipAuth,
                skipRefresh: runtimeGuestOptions.skipRefresh,
                notifyAuthStateChange:
                  runtimeGuestOptions.notifyAuthStateChange,
                clearAuthOnUnauthorized:
                  runtimeGuestOptions.clearAuthOnUnauthorized,
              }
            : null,
        );
        const [detail, runResponse] = await Promise.all([
          jumpHopClient.getWorkDetail(normalizedProfileId).catch(() => null),
          jumpHopClient.startRun(normalizedProfileId, {
            ...runtimeGuestOptions,
            runtimeMode: 'published',
          }),
        ]);
        if (detail?.item) {
          setJumpHopWork(detail.item);
        }
        setJumpHopRun(runResponse.run);
        if (!options.embedded) {
          setSelectionStage('jump-hop-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'jump-hop-runtime',
              buildJumpHopPublicWorkCode(normalizedProfileId),
            ),
          );
        }
        return true;
      } catch (error) {
        setJumpHopError(
          resolveRpgCreationErrorMessage(error, '启动跳一跳玩法失败。'),
        );
        return false;
      } finally {
        setIsJumpHopBusy(false);
      }
    },
    [authUi, setSelectionStage],
  );

  const restartJumpHopRuntimeRun = useCallback(async () => {
    const runId = jumpHopRun?.runId;
    if (!runId) {
      await startJumpHopTestRunFromProfile();
      return;
    }

    setIsJumpHopBusy(true);
    setJumpHopError(null);
    try {
      const response = await jumpHopClient.restartRun(
        runId,
        jumpHopRuntimeRequestOptions ?? undefined,
      );
      setJumpHopRun(response.run);
    } catch (error) {
      setJumpHopError(
        resolveRpgCreationErrorMessage(error, '重新开始跳一跳失败。'),
      );
    } finally {
      setIsJumpHopBusy(false);
    }
  }, [
    jumpHopRun?.runId,
    jumpHopRuntimeRequestOptions,
    startJumpHopTestRunFromProfile,
  ]);

  const submitJumpHopJumpAction = useCallback(
    async (
      payload: Pick<
        JumpHopJumpRequest,
        'dragDistance' | 'dragVectorX' | 'dragVectorY'
      >,
    ) => {
      const runId = jumpHopRun?.runId;
      if (!runId) {
        return;
      }
      try {
        const response = await jumpHopClient.submitJump(
          runId,
          payload,
          jumpHopRuntimeRequestOptions ?? undefined,
        );
        setJumpHopRun(response.run);
      } catch (error) {
        setJumpHopError(
          resolveRpgCreationErrorMessage(error, '提交跳一跳失败。'),
        );
      }
    },
    [jumpHopRun?.runId, jumpHopRuntimeRequestOptions],
  );

  const compilePuzzleClearSession = useCallback(
    async (
      created: PuzzleClearSessionResponse,
      payload?: PuzzleClearWorkspaceCreateRequest,
    ) => {
      const generationState = createMiniGameDraftGenerationState(
        'puzzle-clear',
        resolveMiniGameDraftGenerationStartedAtMs(
          created.session.updatedAt ?? created.session.createdAt,
        ),
      );
      setPuzzleClearError(null);
      setPuzzleClearSession(created.session);
      writeCreationUrlState(
        buildPuzzleClearCreationUrlState({ session: created.session }),
      );
      setPuzzleClearWork(null);
      setPuzzleClearRun(null);
      setPuzzleClearGenerationState(generationState);
      setIsPuzzleClearBusy(true);
      markDraftGenerating('puzzle-clear', [created.session.sessionId]);
      markPendingDraftGenerating('puzzle-clear', created.session.sessionId);
      selectionStageRef.current = 'puzzle-clear-generating';
      setSelectionStage('puzzle-clear-generating');

      try {
        const response = await puzzleClearClient.executeAction(
          created.session.sessionId,
          {
            actionType: 'compile-draft',
            workTitle: payload?.workTitle ?? created.session.draft?.workTitle,
            workDescription:
              payload?.workDescription ??
              created.session.draft?.workDescription,
            themePrompt:
              payload?.themePrompt ?? created.session.draft?.themePrompt,
            boardBackgroundPrompt:
              payload?.boardBackgroundPrompt ??
              created.session.draft?.boardBackgroundPrompt,
            generateBoardBackground:
              payload?.generateBoardBackground ??
              created.session.draft?.generateBoardBackground,
            boardBackgroundAsset:
              payload?.boardBackgroundAsset ??
              created.session.draft?.boardBackgroundAsset,
          },
        );
        setPuzzleClearSession(response.session);
        setPuzzleClearWork(response.work ?? null);
        writeCreationUrlState(
          buildPuzzleClearCreationUrlState({
            session: response.session,
            work: response.work,
          }),
        );
        setPuzzleClearGenerationState(
          createReadyPuzzleClearGenerationState(generationState),
        );
        if (response.work) {
          setPuzzleClearWorks((current) => [
            response.work!.summary,
            ...current.filter(
              (item) =>
                item.workId !== response.work!.summary.workId &&
                item.sourceSessionId !==
                  response.work!.summary.sourceSessionId,
            ),
          ]);
          markPendingDraftReady(
            'puzzle-clear',
            created.session.sessionId,
            false,
          );
          markDraftReady(
            'puzzle-clear',
            [
              created.session.sessionId,
              response.work.summary.workId,
              response.work.summary.profileId,
              response.work.summary.sourceSessionId,
            ],
            false,
          );
          void refreshPuzzleClearShelf().catch(() => undefined);
        }
        setSelectionStage('puzzle-clear-result');
      } catch (error) {
        const errorMessage = resolveRpgCreationErrorMessage(
          error,
          '生成拼消消草稿失败。',
        );
        setPuzzleClearError(errorMessage);
        setPuzzleClearGenerationState(
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          ),
        );
        markPendingDraftFailed('puzzle-clear', created.session.sessionId);
        markDraftFailed(
          'puzzle-clear',
          [created.session.sessionId, created.session.draft?.profileId],
          errorMessage,
        );
        setPuzzleClearWorks((current) =>
          current.map((item) =>
            item.sourceSessionId === created.session.sessionId
              ? {
                  ...item,
                  generationStatus: 'failed',
                  updatedAt: new Date().toISOString(),
                }
              : item,
          ),
        );
        void refreshPuzzleClearShelf().catch(() => undefined);
        try {
          const latest = await puzzleClearClient.getSession(
            created.session.sessionId,
          );
          markDraftFailed(
            'puzzle-clear',
            [latest.session.sessionId, latest.session.draft?.profileId],
            errorMessage,
          );
          setPuzzleClearSession(latest.session);
          setPuzzleClearWork(null);
          writeCreationUrlState(
            buildPuzzleClearCreationUrlState({ session: latest.session }),
          );
        } catch {
          setPuzzleClearSession(created.session);
          setPuzzleClearWork(null);
          writeCreationUrlState(
            buildPuzzleClearCreationUrlState({ session: created.session }),
          );
        }
      } finally {
        setIsPuzzleClearBusy(false);
      }
    },
    [
      createReadyPuzzleClearGenerationState,
      markDraftFailed,
      markDraftGenerating,
      markDraftReady,
      markPendingDraftFailed,
      markPendingDraftGenerating,
      markPendingDraftReady,
      refreshPuzzleClearShelf,
      setSelectionStage,
    ],
  );

  const retryPuzzleClearDraftGeneration = useCallback(() => {
    if (!puzzleClearSession) {
      setSelectionStage('puzzle-clear-workspace');
      return;
    }

    void compilePuzzleClearSession({ session: puzzleClearSession });
  }, [compilePuzzleClearSession, puzzleClearSession, setSelectionStage]);

  const regeneratePuzzleClearAtlas = useCallback(async () => {
    if (!puzzleClearSession?.sessionId) {
      setSelectionStage('puzzle-clear-workspace');
      return;
    }

    const generationState = createMiniGameDraftGenerationState('puzzle-clear');
    setPuzzleClearError(null);
    setPuzzleClearGenerationState(generationState);
    setIsPuzzleClearBusy(true);
    selectionStageRef.current = 'puzzle-clear-generating';
    setSelectionStage('puzzle-clear-generating');
    try {
      const response = await puzzleClearClient.executeAction(
        puzzleClearSession.sessionId,
        {
          actionType: 'regenerate-atlas',
          profileId:
            puzzleClearWork?.summary.profileId ??
            puzzleClearSession.draft?.profileId,
          workTitle: puzzleClearSession.draft?.workTitle,
          workDescription: puzzleClearSession.draft?.workDescription,
          themePrompt: puzzleClearSession.draft?.themePrompt,
          boardBackgroundPrompt: puzzleClearSession.draft?.boardBackgroundPrompt,
          generateBoardBackground:
            puzzleClearSession.draft?.generateBoardBackground,
          boardBackgroundAsset: puzzleClearSession.draft?.boardBackgroundAsset,
        },
      );
      setPuzzleClearSession(response.session);
      setPuzzleClearWork(response.work ?? puzzleClearWork);
      writeCreationUrlState(
        buildPuzzleClearCreationUrlState({
          session: response.session,
          work: response.work ?? puzzleClearWork,
        }),
      );
      setPuzzleClearGenerationState(
        createReadyPuzzleClearGenerationState(generationState),
      );
      setSelectionStage('puzzle-clear-result');
    } catch (error) {
      const errorMessage = resolveRpgCreationErrorMessage(
        error,
        '重新生成拼消消图集失败。',
      );
      setPuzzleClearError(errorMessage);
      setPuzzleClearGenerationState(
        resolveFinishedMiniGameDraftGenerationState(
          generationState,
          'failed',
          { error: errorMessage },
        ),
      );
    } finally {
      setIsPuzzleClearBusy(false);
    }
  }, [
    createReadyPuzzleClearGenerationState,
    puzzleClearSession,
    puzzleClearWork,
    setSelectionStage,
  ]);

  const publishPuzzleClearDraft = useCallback(async () => {
    const profileId = puzzleClearWork?.summary.profileId?.trim();
    if (!profileId) {
      setPuzzleClearError('拼消消草稿尚未生成可发布作品。');
      setSelectionStage('puzzle-clear-result');
      return;
    }

    setIsPuzzleClearBusy(true);
    setPuzzleClearError(null);
    try {
      const response = await puzzleClearClient.publishWork(profileId);
      setPuzzleClearWork(response.item);
      setPuzzleClearWorks((current) => [
        response.item.summary,
        ...current.filter((item) => item.workId !== response.item.summary.workId),
      ]);
      void refreshPuzzleClearShelf();
      void refreshPuzzleClearGallery();
      const publicWorkCode = buildPuzzleClearPublicWorkCode(
        response.item.summary.profileId,
      );
      setSelectedPublicWorkDetail(
        mapPuzzleClearWorkToPlatformGalleryCard(response.item),
      );
      setPublicWorkDetailError(null);
      selectionStageRef.current = 'work-detail';
      setSelectionStage('work-detail');
      pushAppHistoryPath(buildPublicWorkStagePath('work-detail', publicWorkCode));
      openPublishShareModal({
        title: response.item.summary.workTitle || '拼消消',
        publicWorkCode,
        stage: 'work-detail',
      });
    } catch (error) {
      setPuzzleClearError(
        resolveRpgCreationErrorMessage(error, '发布拼消消作品失败。'),
      );
      setSelectionStage('puzzle-clear-result');
    } finally {
      setIsPuzzleClearBusy(false);
    }
  }, [
    openPublishShareModal,
    puzzleClearWork?.summary.profileId,
    refreshPuzzleClearGallery,
    refreshPuzzleClearShelf,
    setSelectionStage,
  ]);

  const startPuzzleClearTestRunFromProfile = useCallback(async () => {
    if (!puzzleClearWork) {
      setPuzzleClearError('拼消消草稿尚未生成可试玩作品。');
      setSelectionStage('puzzle-clear-result');
      return;
    }

    setPuzzleClearError(null);
    setPuzzleClearRuntimeReturnStage('puzzle-clear-result');
    setPuzzleClearRun(createPuzzleClearLocalRuntimeSnapshot(puzzleClearWork));
    setSelectionStage('puzzle-clear-runtime');
  }, [puzzleClearWork, setSelectionStage]);

  const startPuzzleClearRunFromProfile = useCallback(
    async (
      profileId: string,
      options: {
        embedded?: boolean;
        returnStage?: 'work-detail' | 'platform';
      } = {},
    ) => {
      const normalizedProfileId = profileId.trim();
      if (!normalizedProfileId) {
        setPuzzleClearError('当前拼消消作品信息不完整，暂时无法进入玩法。');
        return false;
      }

      setIsPuzzleClearBusy(true);
      setPuzzleClearError(null);
      setPuzzleClearRuntimeReturnStage(options.returnStage ?? 'work-detail');
      try {
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const [detail, runResponse] = await Promise.all([
          puzzleClearClient
            .getRuntimeWorkDetail(normalizedProfileId)
            .catch(() => null),
          puzzleClearClient.startRun(normalizedProfileId, runtimeGuestOptions),
        ]);
        if (detail?.item) {
          setPuzzleClearWork(detail.item);
        }
        setPuzzleClearRun(runResponse.run);
        if (!options.embedded) {
          setSelectionStage('puzzle-clear-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'puzzle-clear-runtime',
              buildPuzzleClearPublicWorkCode(normalizedProfileId),
            ),
          );
        }
        return true;
      } catch (error) {
        setPuzzleClearError(
          resolveRpgCreationErrorMessage(error, '启动拼消消玩法失败。'),
        );
        return false;
      } finally {
        setIsPuzzleClearBusy(false);
      }
    },
    [authUi, setSelectionStage],
  );

  const retryPuzzleClearLevelRun = useCallback(async () => {
    const runId = puzzleClearRun?.runId;
    if (!runId) {
      await startPuzzleClearTestRunFromProfile();
      return;
    }
    if (isPuzzleClearLocalRuntimeSnapshot(puzzleClearRun)) {
      if (!puzzleClearWork) {
        setPuzzleClearError('拼消消草稿尚未生成可重试作品。');
        setSelectionStage('puzzle-clear-result');
        return;
      }
      setPuzzleClearError(null);
      setPuzzleClearRun(retryPuzzleClearLocalLevel(puzzleClearRun, puzzleClearWork));
      return;
    }

    setIsPuzzleClearBusy(true);
    setPuzzleClearError(null);
    try {
      const response = await puzzleClearClient.retryLevel(runId);
      setPuzzleClearRun(response.run);
    } catch (error) {
      setPuzzleClearError(
        resolveRpgCreationErrorMessage(error, '重试拼消消关卡失败。'),
      );
    } finally {
      setIsPuzzleClearBusy(false);
    }
  }, [
    puzzleClearRun,
    puzzleClearWork,
    setSelectionStage,
    startPuzzleClearTestRunFromProfile,
  ]);

  const advancePuzzleClearLevelRun = useCallback(async () => {
    const runId = puzzleClearRun?.runId;
    if (!runId) {
      return;
    }
    if (isPuzzleClearLocalRuntimeSnapshot(puzzleClearRun)) {
      if (!puzzleClearWork) {
        setPuzzleClearError('拼消消草稿尚未生成可继续作品。');
        setSelectionStage('puzzle-clear-result');
        return;
      }
      setPuzzleClearError(null);
      setPuzzleClearRun(
        advancePuzzleClearLocalLevel(puzzleClearRun, puzzleClearWork),
      );
      return;
    }

    setIsPuzzleClearBusy(true);
    setPuzzleClearError(null);
    try {
      const response = await puzzleClearClient.advanceNextLevel(runId);
      setPuzzleClearRun(response.run);
    } catch (error) {
      setPuzzleClearError(
        resolveRpgCreationErrorMessage(error, '进入拼消消下一关失败。'),
      );
    } finally {
      setIsPuzzleClearBusy(false);
    }
  }, [puzzleClearRun, puzzleClearWork, setSelectionStage]);

  const markPuzzleClearLevelTimeUp = useCallback(async () => {
    const runId = puzzleClearRun?.runId;
    if (!runId) {
      return;
    }
    if (isPuzzleClearLocalRuntimeSnapshot(puzzleClearRun)) {
      setPuzzleClearError(null);
      setPuzzleClearRun(markPuzzleClearLocalTimeUp(puzzleClearRun));
      return;
    }

    try {
      const response = await puzzleClearClient.markTimeUp(runId);
      setPuzzleClearRun(response.run);
    } catch (error) {
      setPuzzleClearError(
        resolveRpgCreationErrorMessage(error, '同步拼消消倒计时失败。'),
      );
    }
  }, [puzzleClearRun]);

  const swapPuzzleClearCardsInRun = useCallback(
    async (payload: {
      fromRow: number;
      fromCol: number;
      toRow: number;
      toCol: number;
    }) => {
      const runId = puzzleClearRun?.runId;
      if (!runId) {
        return;
      }
      if (isPuzzleClearLocalRuntimeSnapshot(puzzleClearRun)) {
        setPuzzleClearError(null);
        setPuzzleClearRun(swapPuzzleClearLocalCards(puzzleClearRun, payload));
        return;
      }
      try {
        const response = await puzzleClearClient.swapCards(runId, payload);
        setPuzzleClearRun(response.run);
      } catch (error) {
        setPuzzleClearError(
          resolveRpgCreationErrorMessage(error, '交换拼消消卡片失败。'),
        );
      }
    },
    [puzzleClearRun],
  );

  const compileWoodenFishSession = useCallback(
    async (
      created: WoodenFishSessionResponse,
      payload?: WoodenFishWorkspaceCreateRequest,
    ) => {
      const generationState = createMiniGameDraftGenerationState(
        'wooden-fish',
        resolveMiniGameDraftGenerationStartedAtMs(
          created.session.updatedAt ?? created.session.createdAt,
        ),
      );
      setWoodenFishError(null);
      setWoodenFishSession(created.session);
      writeCreationUrlState(
        buildWoodenFishCreationUrlState({ session: created.session }),
      );
      setWoodenFishWork(null);
      setWoodenFishRun(null);
      setWoodenFishGenerationState(generationState);
      setIsWoodenFishBusy(true);
      setSelectionStage('wooden-fish-generating');
      markDraftGenerating('wooden-fish', [created.session.sessionId]);
      markPendingDraftGenerating('wooden-fish', created.session.sessionId);
      const createdAt = created.session.updatedAt ?? created.session.createdAt;
      setWoodenFishWorks((current) => [
        {
          runtimeKind: 'wooden-fish',
          workId: created.session.sessionId,
          profileId: created.session.sessionId,
          ownerUserId: created.session.ownerUserId,
          sourceSessionId: created.session.sessionId,
          workTitle:
            payload?.workTitle ?? created.session.draft?.workTitle ?? '敲木鱼',
          workDescription:
            payload?.workDescription ??
            created.session.draft?.workDescription ??
            '',
          themeTags:
            payload?.themeTags ?? created.session.draft?.themeTags ?? ['敲木鱼'],
          coverImageSrc: created.session.draft?.coverImageSrc ?? null,
          publicationStatus: 'draft',
          playCount: 0,
          updatedAt: createdAt,
          publishedAt: null,
          publishReady: false,
          generationStatus: 'generating',
        },
        ...current.filter(
          (item) => item.sourceSessionId !== created.session.sessionId,
        ),
      ]);

      try {
        const response = await woodenFishClient.executeAction(
          created.session.sessionId,
          {
            actionType: 'compile-draft',
            workTitle: payload?.workTitle ?? created.session.draft?.workTitle,
            workDescription:
              payload?.workDescription ??
              created.session.draft?.workDescription,
            themeTags: payload?.themeTags ?? created.session.draft?.themeTags,
            hitObjectPrompt:
              payload?.hitObjectPrompt ??
              created.session.draft?.hitObjectPrompt,
            hitObjectReferenceImageSrc:
              payload?.hitObjectReferenceImageSrc ??
              created.session.draft?.hitObjectReferenceImageSrc,
            hitSoundAsset:
              payload?.hitSoundAsset ?? created.session.draft?.hitSoundAsset,
            floatingWords:
              payload?.floatingWords ?? created.session.draft?.floatingWords,
          },
        );
        setWoodenFishSession(response.session);
        setWoodenFishWork(response.work ?? null);
        writeCreationUrlState(
          buildWoodenFishCreationUrlState({
            session: response.session,
            work: response.work,
          }),
        );
        setWoodenFishGenerationState(
          createReadyWoodenFishGenerationState(generationState),
        );
        if (response.work) {
          setWoodenFishWorks((current) => [
            response.work!.summary,
            ...current.filter(
              (item) =>
                item.workId !== response.work!.summary.workId &&
                item.sourceSessionId !== response.work!.summary.sourceSessionId,
            ),
          ]);
          markPendingDraftReady(
            'wooden-fish',
            created.session.sessionId,
            false,
          );
          markDraftReady(
            'wooden-fish',
            [
              created.session.sessionId,
              response.work.summary.workId,
              response.work.summary.profileId,
              response.work.summary.sourceSessionId,
            ],
            false,
          );
          void refreshWoodenFishShelf().catch(() => undefined);
        }
        setSelectionStage('wooden-fish-result');
      } catch (error) {
        const errorMessage = resolveRpgCreationErrorMessage(
          error,
          '生成敲木鱼草稿失败。',
        );
        setWoodenFishError(errorMessage);
        setWoodenFishGenerationState(
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          ),
        );
        markPendingDraftFailed('wooden-fish', created.session.sessionId);
        markDraftFailed(
          'wooden-fish',
          [created.session.sessionId, created.session.draft?.profileId],
          errorMessage,
        );
        setWoodenFishWorks((current) =>
          current.map((item) =>
            item.sourceSessionId === created.session.sessionId
              ? {
                  ...item,
                  generationStatus: 'failed',
                  updatedAt: new Date().toISOString(),
                }
              : item,
          ),
        );
        void refreshWoodenFishShelf().catch(() => undefined);
        try {
          const latest = await woodenFishClient.getSession(
            created.session.sessionId,
          );
          markDraftFailed(
            'wooden-fish',
            [latest.session.sessionId, latest.session.draft?.profileId],
            errorMessage,
          );
          setWoodenFishSession(latest.session);
          setWoodenFishWork(null);
          writeCreationUrlState(
            buildWoodenFishCreationUrlState({ session: latest.session }),
          );
        } catch {
          setWoodenFishSession(created.session);
          setWoodenFishWork(null);
          writeCreationUrlState(
            buildWoodenFishCreationUrlState({ session: created.session }),
          );
        }
      } finally {
        setIsWoodenFishBusy(false);
      }
    },
    [
      createReadyWoodenFishGenerationState,
      markDraftFailed,
      markDraftGenerating,
      markDraftReady,
      markPendingDraftFailed,
      markPendingDraftGenerating,
      markPendingDraftReady,
      refreshWoodenFishShelf,
      setSelectionStage,
    ],
  );

  const retryWoodenFishDraftGeneration = useCallback(() => {
    if (!woodenFishSession) {
      setSelectionStage('wooden-fish-workspace');
      return;
    }

    void compileWoodenFishSession({ session: woodenFishSession });
  }, [compileWoodenFishSession, setSelectionStage, woodenFishSession]);

  const regenerateWoodenFishAsset = useCallback(
    async (actionType: 'regenerate-hit-object') => {
      if (!woodenFishSession?.sessionId) {
        setSelectionStage('wooden-fish-workspace');
        return;
      }

      const generationState = createMiniGameDraftGenerationState(
        'wooden-fish',
        resolveMiniGameDraftGenerationStartedAtMs(woodenFishSession.updatedAt),
      );
      setWoodenFishError(null);
      setWoodenFishGenerationState(generationState);
      setIsWoodenFishBusy(true);
      setSelectionStage('wooden-fish-generating');
      try {
        const response = await woodenFishClient.executeAction(
          woodenFishSession.sessionId,
          {
            actionType,
            workTitle: woodenFishSession.draft?.workTitle,
            workDescription: woodenFishSession.draft?.workDescription,
            themeTags: woodenFishSession.draft?.themeTags,
            hitObjectPrompt: woodenFishSession.draft?.hitObjectPrompt,
            hitObjectReferenceImageSrc:
              woodenFishSession.draft?.hitObjectReferenceImageSrc,
            hitSoundAsset: woodenFishSession.draft?.hitSoundAsset,
            floatingWords: woodenFishSession.draft?.floatingWords,
          },
        );
        setWoodenFishSession(response.session);
        setWoodenFishWork(response.work ?? woodenFishWork);
        writeCreationUrlState(
          buildWoodenFishCreationUrlState({
            session: response.session,
            work: response.work ?? woodenFishWork,
          }),
        );
        setWoodenFishGenerationState(
          createReadyWoodenFishGenerationState(generationState),
        );
        setSelectionStage('wooden-fish-result');
      } catch (error) {
        const errorMessage = resolveRpgCreationErrorMessage(
          error,
          '重新生成敲击物图案失败。',
        );
        setWoodenFishError(errorMessage);
        setWoodenFishGenerationState(
          resolveFinishedMiniGameDraftGenerationState(
            generationState,
            'failed',
            { error: errorMessage },
          ),
        );
      } finally {
        setIsWoodenFishBusy(false);
      }
    },
    [
      createReadyWoodenFishGenerationState,
      setSelectionStage,
      woodenFishSession?.draft,
      woodenFishSession?.sessionId,
      woodenFishWork,
    ],
  );

  const updateWoodenFishWorkMeta = useCallback(
    async (payload: {
      workTitle: string;
      workDescription: string;
      themeTags: string[];
    }) => {
      const sessionId = woodenFishSession?.sessionId?.trim();
      const profileId =
        woodenFishWork?.summary.profileId?.trim() ||
        woodenFishSession?.draft?.profileId?.trim() ||
        '';
      if (!sessionId || !profileId) {
        setWoodenFishError('敲木鱼草稿尚未生成可保存作品信息。');
        setSelectionStage('wooden-fish-result');
        return false;
      }

      setIsWoodenFishBusy(true);
      setWoodenFishError(null);
      try {
        const response = await woodenFishClient.executeAction(sessionId, {
          actionType: 'update-work-meta',
          profileId,
          workTitle: payload.workTitle,
          workDescription: payload.workDescription,
          themeTags: payload.themeTags,
        });
        setWoodenFishSession(response.session);
        setWoodenFishWork(response.work ?? woodenFishWork);
        writeCreationUrlState(
          buildWoodenFishCreationUrlState({
            session: response.session,
            work: response.work ?? woodenFishWork,
          }),
        );
        return true;
      } catch (error) {
        setWoodenFishError(
          resolveRpgCreationErrorMessage(error, '保存敲木鱼作品信息失败。'),
        );
        setSelectionStage('wooden-fish-result');
        return false;
      } finally {
        setIsWoodenFishBusy(false);
      }
    },
    [
      setSelectionStage,
      woodenFishSession?.draft?.profileId,
      woodenFishSession?.sessionId,
      woodenFishWork,
    ],
  );

  const publishWoodenFishDraft = useCallback(async () => {
    const profileId = woodenFishWork?.summary.profileId?.trim();
    if (!profileId) {
      setWoodenFishError('敲木鱼草稿尚未生成可发布作品。');
      setSelectionStage('wooden-fish-result');
      return;
    }

    setIsWoodenFishBusy(true);
    setWoodenFishError(null);
    try {
      const response = await woodenFishClient.publishWork(profileId);
      setWoodenFishWork(response.item);
      void refreshWoodenFishShelf();
      void refreshWoodenFishGallery();
      openPublishShareModal({
        title: response.item.summary.workTitle || '敲木鱼',
        publicWorkCode: buildWoodenFishPublicWorkCode(
          response.item.summary.profileId,
        ),
        stage: 'work-detail',
      });
    } catch (error) {
      setWoodenFishError(
        resolveRpgCreationErrorMessage(error, '发布敲木鱼作品失败。'),
      );
      setSelectionStage('wooden-fish-result');
    } finally {
      setIsWoodenFishBusy(false);
    }
  }, [
    openPublishShareModal,
    refreshWoodenFishGallery,
    refreshWoodenFishShelf,
    setSelectionStage,
    woodenFishWork?.summary.profileId,
  ]);

  const startWoodenFishTestRunFromProfile = useCallback(async () => {
    const profileId = woodenFishWork?.summary.profileId?.trim();
    if (!profileId) {
      setWoodenFishError('敲木鱼草稿尚未生成可试玩作品。');
      setSelectionStage('wooden-fish-result');
      return;
    }

    setIsWoodenFishBusy(true);
    setWoodenFishError(null);
    setWoodenFishRuntimeReturnStage('wooden-fish-result');
    try {
      const response = await woodenFishClient.startRun(profileId);
      setWoodenFishRun(response.run);
      setSelectionStage('wooden-fish-runtime');
    } catch (error) {
      setWoodenFishError(
        error instanceof Error ? error.message : '启动敲木鱼试玩失败。',
      );
      setSelectionStage('wooden-fish-result');
    } finally {
      setIsWoodenFishBusy(false);
    }
  }, [setSelectionStage, woodenFishWork?.summary.profileId]);

  const startWoodenFishRunFromProfile = useCallback(
    async (
      profileId: string,
      options: {
        embedded?: boolean;
        returnStage?: 'work-detail' | 'platform';
      } = {},
    ) => {
      const normalizedProfileId = profileId.trim();
      if (!normalizedProfileId) {
        setWoodenFishError('当前敲木鱼作品信息不完整，暂时无法进入玩法。');
        return false;
      }

      setIsWoodenFishBusy(true);
      setWoodenFishError(null);
      setWoodenFishRuntimeReturnStage(options.returnStage ?? 'work-detail');
      try {
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const [detail, runResponse] = await Promise.all([
          woodenFishClient.getWorkDetail(normalizedProfileId).catch(() => null),
          options.embedded
            ? woodenFishClient.startRun(
                normalizedProfileId,
                runtimeGuestOptions,
              )
            : woodenFishClient.startRun(normalizedProfileId),
        ]);
        if (detail?.item) {
          setWoodenFishWork(detail.item);
        }
        setWoodenFishRun(runResponse.run);
        if (!options.embedded) {
          setSelectionStage('wooden-fish-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'wooden-fish-runtime',
              buildWoodenFishPublicWorkCode(normalizedProfileId),
            ),
          );
        }
        return true;
      } catch (error) {
        setWoodenFishError(
          resolveRpgCreationErrorMessage(error, '启动敲木鱼玩法失败。'),
        );
        return false;
      } finally {
        setIsWoodenFishBusy(false);
      }
    },
    [authUi, setSelectionStage],
  );

  const checkpointWoodenFishRuntimeRun = useCallback(
    async (payload: {
      totalTapCount: number;
      wordCounters: WoodenFishRunResponse['run']['wordCounters'];
    }) => {
      const runId = woodenFishRun?.runId;
      if (!runId) {
        return;
      }
      const response = await woodenFishClient.checkpointRun(runId, payload);
      setWoodenFishRun(response.run);
    },
    [woodenFishRun?.runId],
  );

  const executePuzzleAction = puzzleFlow.executeAction;

  const executePuzzleBackgroundAction = useCallback(
    async (payload: PuzzleAgentActionRequest) => {
      const targetSession = puzzleSession;
      if (!targetSession) {
        return;
      }

      const formPayload = buildPuzzleFormPayloadFromAction(payload);
      if (formPayload) {
        setPuzzleFormDraftPayload(formPayload);
      }

      setPuzzleError(null);
      try {
        const response = await executePuzzleAgentAction(
          targetSession.sessionId,
          payload,
        );
        setPuzzleOperation(response.operation);
        puzzleFlow.setSession(response.session);
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '执行拼图操作失败。'));
      } finally {
        if (
          payload.action === 'generate_puzzle_images' ||
          payload.action === 'generate_puzzle_ui_background'
        ) {
          refreshPlatformDashboardSilently();
        }
      }
    },
    [
      puzzleFlow,
      puzzleSession,
      refreshPlatformDashboardSilently,
      resolvePuzzleErrorMessage,
      setPuzzleError,
    ],
  );

  const retryPuzzleDraftGeneration = useCallback(() => {
    if (puzzleSession?.sessionId) {
      const retryPayload =
        puzzleFormDraftPayload ??
        buildPuzzleFormPayloadFromSession(puzzleSession);
      void executePuzzleAction(
        buildPuzzleCompileActionFromFormPayload(retryPayload),
      );
      return;
    }

    if (puzzleFormDraftPayload) {
      void createPuzzleDraftFromForm(puzzleFormDraftPayload);
    }
  }, [
    createPuzzleDraftFromForm,
    executePuzzleAction,
    puzzleFormDraftPayload,
    puzzleSession,
  ]);

  const retryVisualNovelDraftGeneration = useCallback(() => {
    if (!visualNovelFormDraftPayload) {
      setSelectionStage('visual-novel-agent-workspace');
      return;
    }

    void createVisualNovelDraftFromForm(visualNovelFormDraftPayload);
  }, [
    createVisualNovelDraftFromForm,
    setSelectionStage,
    visualNovelFormDraftPayload,
  ]);

  const retryBabyObjectMatchDraftGeneration = useCallback(() => {
    if (!babyObjectMatchFormPayload) {
      setSelectionStage('baby-object-match-workspace');
      return;
    }

    void createBabyObjectMatchDraftFromForm(babyObjectMatchFormPayload);
  }, [
    babyObjectMatchFormPayload,
    createBabyObjectMatchDraftFromForm,
    setSelectionStage,
  ]);

  const executePuzzleWorkspaceAction = useCallback(
    (payload: PuzzleAgentActionRequest) => {
      if (
        payload.action === 'compile_puzzle_draft' &&
        isEmptyPuzzleFormOnlyDraft(puzzleSession)
      ) {
        const formPayload = buildPuzzleFormPayloadFromAction(payload);
        if (formPayload) {
          void createPuzzleDraftFromForm(formPayload);
          return;
        }
      }

      if (
        payload.action === 'generate_puzzle_images' ||
        payload.action === 'generate_puzzle_ui_background'
      ) {
        void executePuzzleBackgroundAction(payload);
        return;
      }

      void executePuzzleAction(payload);
    },
    [
      createPuzzleDraftFromForm,
      executePuzzleAction,
      executePuzzleBackgroundAction,
      puzzleSession,
    ],
  );

  const openCreativeAgentTarget = useCallback(async () => {
    const binding = creativeAgentSession?.targetBinding;
    if (!binding || isPuzzleBusy) {
      return;
    }

    setIsPuzzleBusy(true);
    setPuzzleError(null);
    try {
      const response = await getPuzzleAgentSession(binding.targetSessionId);
      puzzleFlow.setSession(response.session);
      setPuzzleOperation(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      enterCreateTab();
      setActiveCreativeAgentSessionId(creativeAgentSession.sessionId);
      setCreativeDraftEditError(null);
      setSelectionStage(
        resolveCreativeAgentTargetSelectionStage(binding.targetStage),
      );
    } catch (error) {
      setCreativeAgentError(
        resolvePuzzleErrorMessage(error, '打开拼图草稿失败。'),
      );
    } finally {
      setIsPuzzleBusy(false);
    }
  }, [
    creativeAgentSession,
    enterCreateTab,
    isPuzzleBusy,
    puzzleFlow,
    resolvePuzzleErrorMessage,
    setIsPuzzleBusy,
    setPuzzleError,
    setSelectionStage,
  ]);

  const submitCreativeDraftEdit = useCallback(
    async ({
      instruction,
      currentDraft,
    }: {
      instruction: string;
      currentDraft: PuzzleResultDraft;
    }): Promise<CreativeDraftEditResult | null> => {
      const creativeSessionId =
        activeCreativeAgentSessionId ?? creativeAgentSession?.sessionId ?? null;
      const targetPuzzleSessionId = puzzleSession?.sessionId?.trim();
      if (
        !creativeSessionId ||
        !targetPuzzleSessionId ||
        isCreativeDraftEditBusy
      ) {
        return null;
      }

      setIsCreativeDraftEditBusy(true);
      setCreativeDraftEditError(null);
      try {
        const result = await streamCreativeDraftEdit(creativeSessionId, {
          clientMessageId: `creative-edit-${Date.now().toString(36)}`,
          instruction,
          targetPuzzleSessionId,
          currentDraft,
        });

        if ('puzzleSession' in result) {
          puzzleFlow.setSession(result.puzzleSession);
          setCreativeAgentSession(result.session);
          setActiveCreativeAgentSessionId(result.session.sessionId);
          return result;
        }

        setCreativeAgentSession(result);
        setActiveCreativeAgentSessionId(result.sessionId);
        const response = await getPuzzleAgentSession(targetPuzzleSessionId);
        puzzleFlow.setSession(response.session);
        return null;
      } catch (error) {
        setCreativeDraftEditError(
          resolvePuzzleErrorMessage(error, '智能修订拼图草稿失败。'),
        );
        return null;
      } finally {
        setIsCreativeDraftEditBusy(false);
      }
    },
    [
      activeCreativeAgentSessionId,
      creativeAgentSession?.sessionId,
      isCreativeDraftEditBusy,
      puzzleFlow,
      puzzleSession?.sessionId,
      resolvePuzzleErrorMessage,
    ],
  );

  useEffect(() => {
    if (selectionStage === 'big-fish-result' && !bigFishSession?.draft) {
      setSelectionStage(
        bigFishSession ? 'big-fish-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'big-fish-runtime' && !bigFishRun) {
      setSelectionStage(bigFishSession?.draft ? 'big-fish-result' : 'platform');
    }
  }, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]);

  useEffect(() => {
    if (selectionStage === 'match3d-result' && !match3dSession?.draft) {
      setSelectionStage(
        match3dSession ? 'match3d-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'match3d-runtime' && !match3dRun) {
      setSelectionStage(match3dSession?.draft ? 'match3d-result' : 'platform');
    }
  }, [match3dRun, match3dSession, selectionStage, setSelectionStage]);

  useEffect(() => {
    if (selectionStage === 'square-hole-result' && !squareHoleSession?.draft) {
      setSelectionStage(
        squareHoleSession ? 'square-hole-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'square-hole-runtime' && !squareHoleRun) {
      setSelectionStage(
        squareHoleSession?.draft ? 'square-hole-result' : 'platform',
      );
    }
  }, [selectionStage, setSelectionStage, squareHoleRun, squareHoleSession]);

  useEffect(() => {
    if (
      selectionStage === 'visual-novel-result' &&
      !visualNovelSession?.draft &&
      !visualNovelWork?.draft
    ) {
      setSelectionStage(
        visualNovelSession ? 'visual-novel-agent-workspace' : 'platform',
      );
    }
    if (selectionStage === 'visual-novel-runtime' && !visualNovelRun) {
      setSelectionStage(
        visualNovelSession?.draft || visualNovelWork?.draft
          ? 'visual-novel-result'
          : 'platform',
      );
    }
    if (selectionStage === 'visual-novel-gallery-detail' && !visualNovelWork) {
      setSelectionStage('platform');
    }
  }, [
    selectionStage,
    setSelectionStage,
    visualNovelRun,
    visualNovelSession,
    visualNovelWork,
  ]);

  useEffect(() => {
    if (
      selectionStage === 'baby-object-match-result' &&
      !babyObjectMatchDraft
    ) {
      setSelectionStage(
        babyObjectMatchFormPayload ? 'baby-object-match-workspace' : 'platform',
      );
    }
    if (
      selectionStage === 'baby-object-match-runtime' &&
      !babyObjectMatchDraft
    ) {
      setSelectionStage('platform');
    }
  }, [
    babyObjectMatchDraft,
    babyObjectMatchFormPayload,
    selectionStage,
    setSelectionStage,
  ]);

  const startBigFishRun = useCallback(async () => {
    if (!bigFishSession) {
      return;
    }

    const sessionId = bigFishSession.sessionId;
    setBigFishError(null);
    setBigFishRuntimeShare(null);
    setBigFishRuntimeWork(null);
    setBigFishRuntimeStartedAt(null);
    setBigFishRun(null);
    try {
      const { run } = await startBigFishRuntimeRun(sessionId);
      setBigFishRuntimeStartedAt(Date.now());
      setBigFishRuntimeSessionSource('draft');
      setBigFishRuntimeReturnStage('big-fish-result');
      setBigFishRun(run);
      setSelectionStage('big-fish-runtime');
      void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => {
        setBigFishError(
          resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
        );
      });
      void refreshBigFishShelf();
    } catch (error) {
      setBigFishError(
        resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
      );
    }
  }, [
    bigFishSession,
    refreshBigFishShelf,
    resolveBigFishErrorMessage,
    setBigFishError,
    setSelectionStage,
  ]);

  const restartBigFishRun = useCallback(async () => {
    if (!bigFishSession && !bigFishRun) {
      return;
    }

    const sessionId = bigFishSession?.sessionId ?? bigFishRun?.sessionId;
    if (!sessionId) {
      return;
    }

    setBigFishError(null);
    if (bigFishSession) {
      setBigFishRuntimeShare(null);
      setBigFishRuntimeReturnStage('big-fish-result');
    }
    setBigFishRuntimeStartedAt(null);
    setBigFishRun(null);
    try {
      const { run } = await startBigFishRuntimeRun(sessionId);
      setBigFishRuntimeStartedAt(Date.now());
      setBigFishRuntimeSessionSource(bigFishSession ? 'draft' : 'work');
      setBigFishRuntimeReturnStage(
        bigFishSession ? 'big-fish-result' : bigFishRuntimeReturnStage,
      );
      setBigFishRun(run);
      setSelectionStage('big-fish-runtime');
      void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => {
        setBigFishError(
          resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
        );
      });
    } catch (error) {
      setBigFishError(
        resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
      );
    }
  }, [
    bigFishRun,
    bigFishRuntimeReturnStage,
    bigFishSession,
    resolveBigFishErrorMessage,
    setBigFishError,
    setSelectionStage,
  ]);

  const startPuzzleRunFromProfile = useCallback(
    async (
      profileId: string,
      returnStage: PuzzleRuntimeReturnStage = 'work-detail',
      detailItem?: PuzzleWorkSummary,
      mirrorErrorToPublicDetail = false,
      levelId?: string | null,
      options: { embedded?: boolean; authMode?: PuzzleRuntimeAuthMode } = {},
    ) => {
      const normalizedLevelId = levelId?.trim() ?? '';
      const startKey = `${profileId}:${normalizedLevelId}`;
      if (isPuzzleBusy || puzzleStartInFlightKeyRef.current === startKey) {
        return false;
      }

      puzzleStartInFlightKeyRef.current = startKey;
      setIsPuzzleBusy(true);
      setPuzzleError(null);

      try {
        const item =
          detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
        const startRunPayload = {
          profileId: item.profileId,
          levelId: normalizedLevelId || null,
        };
        const canUseRuntimeGuestAuth =
          options.embedded || options.authMode === 'isolated';
        const useRuntimeGuestAuth =
          canUseRuntimeGuestAuth && shouldUseRecommendRuntimeGuestAuth(authUi);
        const runtimeGuestOptions = useRuntimeGuestAuth
          ? await buildRecommendRuntimeGuestOptions()
          : {};
        const authMode = useRuntimeGuestAuth ? 'isolated' : 'default';
        const runtimeAuthOptions = useRuntimeGuestAuth
          ? runtimeGuestOptions
          : canUseRuntimeGuestAuth
            ? RECOMMEND_PUZZLE_BACKGROUND_AUTH_OPTIONS
            : {};
        const { run } =
          authMode === 'isolated'
            ? await startPuzzleRun(startRunPayload, runtimeGuestOptions)
            : await startPuzzleRun(startRunPayload, runtimeAuthOptions);
        setSelectedPuzzleDetail(item);
        setPuzzleRun(run);
        setPuzzleRuntimeAuthMode(authMode);
        setPuzzleRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          openPuzzleRuntimeStage(
            setSelectionStage,
            authMode === 'isolated'
              ? buildPuzzlePublishedRuntimeUrlState(item, levelId)
              : buildPuzzleDraftRuntimeUrlState(item, levelId),
          );
        }
        return true;
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleGalleryEntries((current) =>
            current.filter((entry) => entry.profileId !== profileId),
          );
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          returnPlatformHomeAfterMissingWork();
          return false;
        }

        const message = resolvePuzzleErrorMessage(error, '启动拼图玩法失败。');
        setPuzzleError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        if (puzzleStartInFlightKeyRef.current === startKey) {
          puzzleStartInFlightKeyRef.current = null;
        }
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      authUi,
      resolvePuzzleErrorMessage,
      returnPlatformHomeAfterMissingWork,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const startMatch3DRunFromProfile = useCallback(
    async (
      profile: Match3DWorkProfile | Match3DWorkSummary,
      returnStage: 'match3d-result' | 'work-detail' = 'match3d-result',
      mirrorErrorToPublicDetail = false,
      options: { embedded?: boolean; itemTypeCountOverride?: number } = {},
    ) => {
      if (isMatch3DBusy) {
        return false;
      }

      match3dFlow.setIsBusy(true);
      setMatch3DError(null);

      try {
        let runtimeProfile: Match3DWorkProfile | Match3DWorkSummary = profile;
        if (
          !hasMatch3DRuntimeAsset(profile.generatedItemAssets) ||
          !hasMatch3DRuntimeBackgroundAsset(profile)
        ) {
          try {
            const { item } = await getMatch3DWorkDetail(profile.profileId);
            runtimeProfile = normalizeMatch3DWorkForRuntimeUi({
              ...item,
              generatedItemAssets: mergeMatch3DGeneratedItemAssetsForRuntime(
                item.generatedItemAssets,
                profile.generatedItemAssets,
              ),
            });
          } catch {
            // 中文注释：详情补读只为拿完整生成素材；失败时继续按摘要开局，避免推荐流卡死。
          }
        }
        runtimeProfile = promoteMatch3DGeneratedBackgroundAsset({
          ...runtimeProfile,
          generatedItemAssets: normalizeMatch3DGeneratedItemAssetsForRuntime(
            runtimeProfile.generatedItemAssets,
          ),
        });
        await preloadMatch3DGeneratedRuntimeAssets(
          runtimeProfile.generatedItemAssets,
          runtimeProfile.generatedBackgroundAsset,
          { expireSeconds: 300 },
        );
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const runtimeOptions = {
          ...runtimeGuestOptions,
          ...(typeof options.itemTypeCountOverride === 'number'
            ? { itemTypeCountOverride: options.itemTypeCountOverride }
            : {}),
        };
        const activeRuntimeAdapter = resolveMatch3DRuntimeAdapter(
          runtimeProfile.profileId,
        );
        const { run } = await activeRuntimeAdapter.startRun(
          runtimeProfile.profileId,
          runtimeOptions,
        );
        // 中文注释：运行态必须锁定本次启动时的完整 profile，避免首次切屏渲染读到旧草稿 state。
        setMatch3DRuntimeProfile(runtimeProfile);
        setMatch3DRun(run);
        setMatch3DProfile(runtimeProfile);
        setMatch3DRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('match3d-runtime');
        }
        if (
          !options.embedded &&
          runtimeProfile.publicationStatus === 'published'
        ) {
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'work-detail',
              buildMatch3DPublicWorkCode(runtimeProfile.profileId),
            ),
          );
        }
        return true;
      } catch (error) {
        const message = resolveMatch3DErrorMessage(
          error,
          '启动抓大鹅玩法失败。',
        );
        setMatch3DError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        match3dFlow.setIsBusy(false);
      }
    },
    [
      isMatch3DBusy,
      authUi,
      match3dFlow,
      resolveMatch3DErrorMessage,
      resolveMatch3DRuntimeAdapter,
      setMatch3DError,
      setSelectionStage,
    ],
  );

  const startSquareHoleRunFromProfile = useCallback(
    async (
      profile: SquareHoleWorkProfile | SquareHoleWorkSummary,
      returnStage: SquareHoleRuntimeReturnStage = 'square-hole-result',
      mirrorErrorToPublicDetail = false,
      options: { embedded?: boolean } = {},
    ) => {
      if (isSquareHoleBusy) {
        return false;
      }

      squareHoleFlow.setIsBusy(true);
      setSquareHoleError(null);

      try {
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const { run } = options.embedded
          ? await startSquareHoleRun(profile.profileId, runtimeGuestOptions)
          : await startSquareHoleRun(profile.profileId);
        setSquareHoleRun(run);
        setSquareHoleRuntimeReturnStage(returnStage);
        if (!options.embedded) {
          setSelectionStage('square-hole-runtime');
        }
        if (!options.embedded && profile.publicationStatus === 'published') {
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'work-detail',
              buildSquareHolePublicWorkCode(profile.profileId),
            ),
          );
        }
        return true;
      } catch (error) {
        const message = resolveSquareHoleErrorMessage(
          error,
          '启动方洞挑战玩法失败。',
        );
        setSquareHoleError(message);
        if (mirrorErrorToPublicDetail) {
          setPublicWorkDetailError(message);
        }
        return false;
      } finally {
        squareHoleFlow.setIsBusy(false);
      }
    },
    [
      isSquareHoleBusy,
      authUi,
      resolveSquareHoleErrorMessage,
      setSelectionStage,
      setSquareHoleError,
      squareHoleFlow,
    ],
  );

  const _buildPuzzleTestWork = useCallback(
    (draft: PuzzleResultDraft) => {
      const profileId =
        puzzleSession?.publishedProfileId ??
        `draft-${puzzleSession?.sessionId ?? 'puzzle'}-test`;
      const now = new Date().toISOString();

      return {
        workId: `test-${profileId}`,
        profileId,
        ownerUserId: authUi?.user?.id ?? 'current-user',
        sourceSessionId: puzzleSession?.sessionId ?? null,
        authorDisplayName: authUi?.user?.displayName ?? '玩家',
        workTitle: draft.workTitle || draft.levelName,
        workDescription: draft.workDescription || draft.summary,
        levelName: draft.levelName,
        summary: draft.summary,
        themeTags: draft.themeTags,
        coverImageSrc: draft.coverImageSrc,
        coverAssetId: draft.coverAssetId,
        publicationStatus: 'draft',
        updatedAt: now,
        publishedAt: null,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
        pointIncentiveTotalHalfPoints: 0,
        pointIncentiveClaimedPoints: 0,
        pointIncentiveTotalPoints: 0,
        pointIncentiveClaimablePoints: 0,
        publishReady: Boolean(puzzleSession?.resultPreview?.publishReady),
        levels: draft.levels,
      } satisfies PuzzleWorkSummary;
    },
    [
      authUi?.user?.displayName,
      authUi?.user?.id,
      puzzleSession?.publishedProfileId,
      puzzleSession?.resultPreview?.publishReady,
      puzzleSession?.sessionId,
    ],
  );

  const startPuzzleTestRunFromDraft = useCallback(
    async (
      draft: PuzzleResultDraft,
      options: { levelId?: string | null } = {},
    ) => {
      if (isPuzzleBusy) {
        return false;
      }
      if (!draft.coverImageSrc) {
        setPuzzleError('请先选择一张正式拼图图片。');
        return false;
      }
      const profileId =
        puzzleSession?.publishedProfileId ??
        buildPuzzleResultProfileId(puzzleSession?.sessionId);
      if (!profileId) {
        setPuzzleError('这份拼图草稿缺少会话信息，请重新开始创作。');
        return false;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        const { item } = await updatePuzzleWork(profileId, {
          workTitle: draft.workTitle,
          workDescription: draft.workDescription,
          levelName: draft.levelName,
          summary: draft.summary,
          themeTags: draft.themeTags,
          coverImageSrc: draft.coverImageSrc,
          coverAssetId: draft.coverAssetId,
          levels: draft.levels ?? [],
        });
        const run = startLocalPuzzleRun(item, options.levelId ?? null);
        setSelectedPuzzleDetail(item);
        setPuzzleRun(run);
        setPuzzleRuntimeAuthMode('default');
        setPuzzleRuntimeReturnStage('puzzle-result');
        pushPuzzleResultHistoryEntry(puzzleSession);
        openPuzzleRuntimeStage(
          setSelectionStage,
          buildPuzzleDraftRuntimeUrlState(item, options.levelId ?? null),
        );
        return true;
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '启动拼图试玩失败。'));
        return false;
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleSession,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const submitBigFishInput = useCallback(
    async (payload: SubmitBigFishInputRequest) => {
      if (
        !bigFishRun ||
        bigFishRun.status !== 'running' ||
        bigFishInputInFlightRef.current
      ) {
        return;
      }

      bigFishInputInFlightRef.current = true;
      try {
        const runtimeGuestOptions =
          activeRecommendRuntimeKind === 'big-fish'
            ? await buildRecommendRuntimeAuthOptions(authUi, true)
            : {};
        const { run } = await submitBigFishRuntimeInput(
          bigFishRun.runId,
          payload,
          runtimeGuestOptions,
        );
        setBigFishRun(run);
      } catch (error) {
        setBigFishError(
          resolveBigFishErrorMessage(error, '同步大鱼吃小鱼输入失败。'),
        );
      } finally {
        bigFishInputInFlightRef.current = false;
      }
    },
    [
      activeRecommendRuntimeKind,
      authUi,
      bigFishRun,
      resolveBigFishErrorMessage,
      setBigFishError,
    ],
  );

  const reportBigFishObservedPlayTime = useCallback(() => {
    const sessionId = bigFishRun?.sessionId?.trim();
    if (!sessionId || !bigFishRuntimeStartedAt) {
      return;
    }

    const elapsedMs = Math.max(1_000, Date.now() - bigFishRuntimeStartedAt);
    setBigFishRuntimeStartedAt(null);
    const reportPromise =
      activeRecommendRuntimeKind === 'big-fish'
        ? buildRecommendRuntimeAuthOptions(authUi, true).then(
            (runtimeAuthOptions) =>
              recordBigFishPlay(sessionId, { elapsedMs }, runtimeAuthOptions),
          )
        : recordBigFishPlay(sessionId, { elapsedMs });
    void reportPromise.catch((error) => {
      setBigFishError(
        resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩时长失败。'),
      );
    });
  }, [
    activeRecommendRuntimeKind,
    authUi,
    bigFishRun?.sessionId,
    bigFishRuntimeStartedAt,
    resolveBigFishErrorMessage,
    setBigFishError,
  ]);

  const swapPuzzlePiecesInRun = useCallback(
    async (payload: { firstPieceId: string; secondPieceId: string }) => {
      if (!puzzleRun || isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        setPuzzleRun(
          swapLocalPuzzlePieces(
            puzzleRun,
            payload,
            isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
          ),
        );
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '交换拼图块失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleRun,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );

  const dragPuzzlePiece = useCallback(
    async (payload: {
      pieceId: string;
      targetRow: number;
      targetCol: number;
    }) => {
      if (!puzzleRun || isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      try {
        setPuzzleRun(
          dragLocalPuzzlePiece(
            puzzleRun,
            payload,
            isLocalPuzzleRun(puzzleRun) ? selectedPuzzleDetail : null,
          ),
        );
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '拖动拼图块失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      puzzleRun,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );

  useEffect(() => {
    if (selectionStage !== 'puzzle-runtime' || !puzzleRun?.currentLevel) {
      return;
    }
    if (puzzleRun.currentLevel.status !== 'playing') {
      return;
    }

    const timerId = window.setInterval(() => {
      // 中文注释：拼图运行态的棋盘交互和倒计时展示都在前端即时裁决，超时后只刷新本地失败态。
      setPuzzleRun((currentRun) =>
        currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
      );
    }, 250);

    return () => window.clearInterval(timerId);
  }, [puzzleRun, selectionStage]);

  useEffect(() => {
    if (selectionStage !== 'puzzle-runtime' || puzzleRun) {
      return;
    }
    if (!hasPuzzleRuntimeUrlStateValue(initialPuzzleRuntimeUrlState)) {
      return;
    }
    const runtimeUrlStateKey = buildPuzzleRuntimeUrlStateKey(
      initialPuzzleRuntimeUrlState,
    );
    if (
      handledPuzzleRuntimeUrlStateKeyRef.current === runtimeUrlStateKey ||
      isPuzzleBusy
    ) {
      return;
    }

    handledPuzzleRuntimeUrlStateKeyRef.current = runtimeUrlStateKey;
    let cancelled = false;

    const restorePuzzleRuntime = async () => {
      const runtimeProfileId = normalizeCreationUrlValue(
        initialPuzzleRuntimeUrlState.runtimeProfileId,
      );
      const runtimeSessionId = normalizeCreationUrlValue(
        initialPuzzleRuntimeUrlState.runtimeSessionId,
      );
      const runtimeLevelId = normalizeCreationUrlValue(
        initialPuzzleRuntimeUrlState.runtimeLevelId,
      );
      const publicWorkCode = normalizeCreationUrlValue(
        initialPuzzleRuntimeUrlState.publicWorkCode,
      );
      const runtimeMode = initialPuzzleRuntimeUrlState.mode ?? null;
      const isPublishedRuntime = runtimeMode === 'published';
      if (
        !isPublishedRuntime &&
        (platformBootstrap.isLoadingPlatform ||
          !platformBootstrap.canReadProtectedData)
      ) {
        handledPuzzleRuntimeUrlStateKeyRef.current = null;
        return;
      }
      const fallbackStage: PuzzleRuntimeReturnStage =
        runtimeMode === 'published' ? 'work-detail' : 'puzzle-result';

      const candidateItems = isPublishedRuntime
        ? puzzleGalleryEntries.length > 0
          ? puzzleGalleryEntries
          : await refreshPuzzleGallery()
        : puzzleWorks.length > 0
          ? puzzleWorks
          : (await listPuzzleWorks().catch(() => ({ items: [] }))).items;
      const targetItem =
        runtimeProfileId || runtimeSessionId || publicWorkCode
          ? (candidateItems.find(
              (item) =>
                item.profileId === runtimeProfileId ||
                item.sourceSessionId === runtimeSessionId ||
                (publicWorkCode
                  ? isSamePuzzlePublicWorkCode(publicWorkCode, item.profileId)
                  : false),
            ) ?? null)
          : null;

      if (!targetItem) {
        if (runtimeSessionId) {
          try {
            const { session } = await getPuzzleAgentSession(runtimeSessionId);
            const restoredWork = buildPuzzleRuntimeWorkFromSession(session, {
              userId: authUi?.user?.id ?? 'current-user',
              displayName: authUi?.user?.displayName ?? '玩家',
            });
            if (restoredWork && !cancelled) {
              setSelectedPuzzleDetail(restoredWork);
              setPuzzleRun(
                startLocalPuzzleRun(restoredWork, runtimeLevelId ?? null),
              );
              setPuzzleRuntimeAuthMode('default');
              setPuzzleRuntimeReturnStage(fallbackStage);
              openPuzzleRuntimeStage(
                setSelectionStage,
                buildPuzzleDraftRuntimeUrlState(restoredWork, runtimeLevelId),
              );
              return;
            }
          } catch {
            // 读不到草稿时继续回退到平台页，避免卡在空运行态。
          }
        }

        if (!cancelled) {
          setPuzzleError(
            runtimeMode === 'published'
              ? '这份拼图运行态缺少可恢复作品，请返回作品详情重新进入。'
              : '这份拼图试玩缺少可恢复草稿，请返回结果页重新进入。',
          );
          setPuzzleRuntimeAuthMode('default');
          setPuzzleRun(null);
          setSelectionStage('platform');
          pushAppHistoryPath('/');
        }
        return;
      }

      if (cancelled) {
        return;
      }

      setSelectedPuzzleDetail(targetItem);
      setPuzzleRun(startLocalPuzzleRun(targetItem, runtimeLevelId ?? null));
      setPuzzleRuntimeAuthMode(isPublishedRuntime ? 'isolated' : 'default');
      setPuzzleRuntimeReturnStage(fallbackStage);
      openPuzzleRuntimeStage(
        setSelectionStage,
        isPublishedRuntime
          ? buildPuzzlePublishedRuntimeUrlState(targetItem, runtimeLevelId)
          : buildPuzzleDraftRuntimeUrlState(targetItem, runtimeLevelId),
      );
    };

    void restorePuzzleRuntime();

    return () => {
      cancelled = true;
    };
  }, [
    authUi?.user?.displayName,
    authUi?.user?.id,
    initialPuzzleRuntimeUrlState,
    isPuzzleBusy,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.isLoadingPlatform,
    puzzleGalleryEntries,
    puzzleRun,
    puzzleRuntimeReturnStage,
    puzzleWorks,
    refreshPuzzleGallery,
    selectionStage,
    setSelectionStage,
  ]);

  const setPuzzleRuntimePaused = useCallback(
    async (paused: boolean) => {
      if (!puzzleRun?.currentLevel) {
        return;
      }

      setPuzzleRun((currentRun) =>
        currentRun ? setLocalPuzzlePaused(currentRun, paused) : currentRun,
      );
    },
    [puzzleRun],
  );

  const syncPuzzleRuntimeTimeout = useCallback(async () => {
    if (
      !puzzleRun?.currentLevel ||
      puzzleRun.currentLevel.status === 'cleared'
    ) {
      return;
    }

    setPuzzleRun((currentRun) =>
      currentRun ? refreshLocalPuzzleTimer(currentRun) : currentRun,
    );
  }, [puzzleRun]);

  const usePuzzleProp = useCallback(
    async (propKind: PuzzleRuntimePropKind) => {
      if (!puzzleRun?.currentLevel) {
        return null;
      }
      const canUseProp =
        propKind === 'extendTime'
          ? puzzleRun.currentLevel.status !== 'cleared'
          : puzzleRun.currentLevel.status === 'playing';
      if (!canUseProp) {
        return null;
      }

      const currentRun = puzzleRunRef.current ?? puzzleRun;
      if (!currentRun.currentLevel) {
        return null;
      }
      const nextRun =
        propKind === 'extendTime'
          ? extendLocalPuzzleTime(currentRun)
          : propKind === 'freezeTime'
            ? applyLocalPuzzleFreezeTime(currentRun)
            : setLocalPuzzlePaused(currentRun, propKind === 'reference');
      puzzleRunRef.current = nextRun;
      setPuzzleRun(nextRun);
      return nextRun;
    },
    [puzzleRun],
  );

  const restartPuzzleCurrentLevel = useCallback(async () => {
    const currentRun = puzzleRunRef.current ?? puzzleRun;
    const currentLevel = currentRun?.currentLevel ?? null;
    if (!currentRun || !currentLevel || isPuzzleBusy) {
      return;
    }

    setPuzzleError(null);
    setIsPuzzleBusy(true);
    try {
      if (isLocalPuzzleRun(currentRun)) {
        const nextRun = restartLocalPuzzleLevel(currentRun);
        puzzleRunRef.current = nextRun;
        setPuzzleRun(nextRun);
        return;
      }

      const detailItem =
        selectedPuzzleDetail?.profileId === currentLevel.profileId
          ? selectedPuzzleDetail
          : await getPuzzleGalleryDetail(currentLevel.profileId).then(
              (response) => response.item,
            );
      const startRunPayload = {
        profileId: currentLevel.profileId,
        levelId: resolvePuzzleRestartLevelId(currentRun, detailItem),
      };
      const runtimeGuestOptions =
        puzzleRuntimeAuthMode === 'isolated'
          ? await buildRecommendRuntimeGuestOptions()
          : {};
      const { run } =
        puzzleRuntimeAuthMode === 'isolated'
          ? await startPuzzleRun(startRunPayload, runtimeGuestOptions)
          : await startPuzzleRun(startRunPayload);
      setSelectedPuzzleDetail(detailItem);
      puzzleRunRef.current = run;
      setPuzzleRun(run);
    } catch (error) {
      setPuzzleError(
        resolvePuzzleErrorMessage(error, '重新开始拼图关卡失败。'),
      );
    } finally {
      setIsPuzzleBusy(false);
    }
  }, [
    isPuzzleBusy,
    puzzleRun,
    puzzleRuntimeAuthMode,
    resolvePuzzleErrorMessage,
    selectedPuzzleDetail,
    setIsPuzzleBusy,
    setPuzzleError,
  ]);

  const resumePuzzleSaveArchive = useCallback(
    async (entry: ProfileSaveArchiveSummary) => {
      if (isPuzzleBusy) {
        return;
      }

      setIsPuzzleBusy(true);
      setPuzzleError(null);
      platformBootstrap.setSaveError(null);

      try {
        const resumedArchive = await resumePuzzleProfileSaveArchiveRaw(
          entry.worldKey,
        );
        platformBootstrap.setSaveEntries((currentEntries) =>
          currentEntries.map((currentEntry) =>
            currentEntry.worldKey === resumedArchive.entry.worldKey
              ? resumedArchive.entry
              : currentEntry,
          ),
        );
        const gameState = resumedArchive.snapshot.gameState;
        const profileId =
          typeof gameState.currentProfileId === 'string' &&
          gameState.currentProfileId.trim()
            ? gameState.currentProfileId
            : typeof gameState.entryProfileId === 'string' &&
                gameState.entryProfileId.trim()
              ? gameState.entryProfileId
              : (entry.profileId ?? entry.worldKey.replace(/^puzzle:/u, ''));
        const levelId =
          typeof gameState.currentLevelId === 'string' &&
          gameState.currentLevelId.trim()
            ? gameState.currentLevelId
            : null;
        const item =
          selectedPuzzleDetail?.profileId === profileId
            ? selectedPuzzleDetail
            : await getPuzzleGalleryDetail(profileId).then(
                (response) => response.item,
              );
        await startPuzzleRunFromProfile(
          item.profileId,
          'platform',
          item,
          false,
          levelId,
        );
      } catch (error) {
        platformBootstrap.setSaveError(
          resolvePuzzleErrorMessage(error, '恢复拼图存档失败。'),
        );
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      platformBootstrap,
      selectedPuzzleDetail,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      startPuzzleRunFromProfile,
    ],
  );

  useEffect(() => {
    const currentLevel = puzzleRun?.currentLevel ?? null;
    if (!puzzleRun || !currentLevel || currentLevel.status !== 'cleared') {
      return;
    }
    if (currentLevel.elapsedMs === null) {
      return;
    }
    if ((currentLevel.leaderboardEntries ?? []).length > 0) {
      return;
    }

    const submitKey = [
      puzzleRun.runId,
      currentLevel.profileId,
      currentLevel.levelId ?? currentLevel.levelIndex,
      currentLevel.gridSize,
      currentLevel.elapsedMs,
    ].join(':');
    if (submittedPuzzleLeaderboardKeysRef.current.has(submitKey)) {
      return;
    }
    submittedPuzzleLeaderboardKeysRef.current.add(submitKey);
    setIsPuzzleLeaderboardBusy(true);

    const payload: SubmitPuzzleLeaderboardRequest = {
      profileId: currentLevel.profileId,
      gridSize: currentLevel.gridSize,
      elapsedMs: currentLevel.elapsedMs,
      nickname: authUi?.user?.displayName?.trim() || '玩家',
    };

    if (isLocalPuzzleRun(puzzleRun)) {
      setPuzzleRun(submitLocalPuzzleLeaderboard(puzzleRun, payload.nickname));
      setIsPuzzleLeaderboardBusy(false);
      return;
    }

    const submitLeaderboardPromise =
      puzzleRuntimeAuthMode === 'isolated'
        ? buildRecommendRuntimeGuestOptions().then((runtimeGuestOptions) =>
            submitPuzzleLeaderboard(
              puzzleRun.runId,
              payload,
              runtimeGuestOptions,
            ),
          )
        : submitPuzzleLeaderboard(puzzleRun.runId, payload);

    void submitLeaderboardPromise
      .then(({ run }) => {
        setPuzzleRun((currentRun) => {
          if (!currentRun) {
            return currentRun;
          }
          return mergePuzzleServiceRuntimeState(currentRun, run);
        });
        void platformBootstrap.refreshSaveArchives();
      })
      .catch((error) => {
        setPuzzleError(
          resolvePuzzleErrorMessage(error, '提交拼图排行榜失败。'),
        );
      })
      .finally(() => {
        setIsPuzzleLeaderboardBusy(false);
      });
  }, [
    authUi?.user?.displayName,
    platformBootstrap,
    puzzleRun,
    puzzleRuntimeAuthMode,
    resolvePuzzleErrorMessage,
    setPuzzleError,
  ]);

  const advancePuzzleLevel = useCallback(
    async (_target?: { profileId?: string; levelId?: string | null }) => {
      if (!puzzleRun || isPuzzleBusy || isPuzzleLeaderboardBusy) {
        return;
      }
      const currentLevel = puzzleRun.currentLevel;
      if (!currentLevel || currentLevel.status !== 'cleared') {
        return;
      }

      setIsPuzzleBusy(true);
      setIsPuzzleNextLevelGenerating(true);
      setPuzzleError(null);

      try {
        if (isLocalPuzzleRun(puzzleRun)) {
          const nextRun = advanceLocalPuzzleLevel(
            puzzleRun,
            selectedPuzzleDetail,
            _target,
          );
          setPuzzleRun(nextRun);
          return;
        }

        const runtimeGuestOptions =
          puzzleRuntimeAuthMode === 'isolated'
            ? await buildRecommendRuntimeGuestOptions()
            : {};
        const targetProfileId = _target?.profileId?.trim() ?? '';
        if (puzzleRun.nextLevelMode === 'similarWorks' && targetProfileId) {
          const itemPromise =
            selectedPuzzleDetail?.profileId === targetProfileId
              ? Promise.resolve(selectedPuzzleDetail)
              : getPuzzleGalleryDetail(targetProfileId).then(
                  (response) => response.item,
                );
          const advancePromise =
            puzzleRuntimeAuthMode === 'isolated'
              ? advancePuzzleNextLevel(
                  puzzleRun.runId,
                  {
                    targetProfileId,
                  },
                  runtimeGuestOptions,
                )
              : advancePuzzleNextLevel(puzzleRun.runId, {
                  targetProfileId,
                });
          const [{ run }, item] = await Promise.all([
            advancePromise,
            itemPromise,
          ]);
          setSelectedPuzzleDetail(item);
          setPuzzleRun(run);
          openPuzzleRuntimeStage(
            setSelectionStage,
            buildPuzzlePublishedRuntimeUrlState(
              item,
              run.currentLevel?.levelId ?? null,
            ),
          );
          return;
        }

        const { run } =
          puzzleRuntimeAuthMode === 'isolated'
            ? await advancePuzzleNextLevel(
                puzzleRun.runId,
                {},
                runtimeGuestOptions,
              )
            : await advancePuzzleNextLevel(puzzleRun.runId, {});
        const nextProfileId = run.currentLevel?.profileId?.trim() ?? '';
        if (
          nextProfileId &&
          selectedPuzzleDetail?.profileId !== nextProfileId
        ) {
          const item = await getPuzzleGalleryDetail(nextProfileId).then(
            (response) => response.item,
          );
          const nextRecommendEntry = mapPuzzleWorkToPlatformGalleryCard(item);
          setPuzzleGalleryEntries((current) => {
            const nextEntries = current.filter(
              (entry) => entry.profileId !== item.profileId,
            );
            nextEntries.push(item);
            return nextEntries;
          });
          setSelectedPuzzleDetail(item);
          setActiveRecommendEntryKey(
            getPlatformPublicGalleryEntryKey(nextRecommendEntry),
          );
        }
        setPuzzleRun(run);
      } catch (error) {
        setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
      } finally {
        setIsPuzzleNextLevelGenerating(false);
        setIsPuzzleBusy(false);
      }
    },
    [
      isPuzzleBusy,
      isPuzzleLeaderboardBusy,
      activeRecommendRuntimeKind,
      puzzleRun,
      puzzleRuntimeReturnStage,
      puzzleRuntimeAuthMode,
      setActiveRecommendEntryKey,
      setPuzzleGalleryEntries,
      resolvePuzzleErrorMessage,
      selectedPuzzleDetail,
      setIsPuzzleBusy,
      setPuzzleError,
    ],
  );
  const remodelCurrentPuzzleRuntimeWork = useCallback(
    (profileId: string) => {
      const targetProfileId = profileId.trim();
      if (!targetProfileId || isPublicWorkDetailBusy || isPuzzleBusy) {
        return;
      }

      runProtectedAction(async () => {
        setIsPublicWorkDetailBusy(true);
        setIsPuzzleBusy(true);
        setPuzzleError(null);
        setPublicWorkDetailError(null);

        void remixPuzzleGalleryWork(targetProfileId)
          .then((response) => {
            resetRecommendRuntimeSelection();
            puzzleFlow.setSession(response.session);
            setPuzzleOperation(null);
            setPuzzleRun(null);
            setPuzzleRuntimeAuthMode('default');
            enterCreateTab();
            setSelectionStage('puzzle-result');
          })
          .catch((error) => {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '改造拼图作品失败。'),
            );
          })
          .finally(() => {
            setIsPublicWorkDetailBusy(false);
            setIsPuzzleBusy(false);
          });
      });
    },
    [
      enterCreateTab,
      isPublicWorkDetailBusy,
      isPuzzleBusy,
      puzzleFlow,
      resolvePuzzleErrorMessage,
      resetRecommendRuntimeSelection,
      runProtectedAction,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const saveAndExitRecommendPuzzleRuntime = useCallback(async () => {
    if (activeRecommendRuntimeKind !== 'puzzle') {
      return;
    }

    const currentRun = puzzleRunRef.current;
    if (!currentRun) {
      setActiveRecommendRuntimeKind(null);
      return;
    }

    // 中文注释：推荐页嵌入拼图的“保存并退出”沿用现有运行态语义；
    // 正式 run 的每次交换/拖动/通关已写回后端，退出时只收口暂停和本地快照。
    const closedRun = currentRun.currentLevel
      ? setLocalPuzzlePaused(currentRun, false)
      : currentRun;
    puzzleRunRef.current = null;
    setPuzzleRun(null);
    setPuzzleRuntimeAuthMode('default');
    setActiveRecommendRuntimeKind(null);

    if (closedRun.currentLevel) {
      setPuzzleError(null);
    }
  }, [activeRecommendRuntimeKind, setPuzzleError]);

  const leaveAgentWorkspace = useCallback(() => {
    sessionController.resetSessionViewState();
    sessionController.setGeneratedCustomWorldProfile(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.persistAgentUiState(
      sessionController.activeAgentSessionId,
      null,
    );
    returnToCreationFlowSource();
  }, [autosaveCoordinator, returnToCreationFlowSource, sessionController]);

  const leaveAgentDraftGeneration = useCallback(() => {
    if (sessionController.isActiveGenerationRunning) {
      return;
    }

    sessionController.setAgentDraftGenerationStartedAt(null);
    sessionController.setCustomWorldGenerationViewSource(null);
    setSelectionStage('agent-workspace');
  }, [sessionController, setSelectionStage]);

  const leaveAgentDraftResult = useCallback(() => {
    sessionController.suppressAgentDraftResultAutoOpen();
    sessionController.setGeneratedCustomWorldProfile(null);
    sessionController.setCustomWorldError(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.setCustomWorldGenerationViewSource(null);
    sessionController.setCustomWorldResultViewSource(null);
    returnToCreationFlowSource();
  }, [autosaveCoordinator, returnToCreationFlowSource, sessionController]);

  const leaveCustomWorldResult = useCallback(() => {
    sessionController.setGeneratedCustomWorldProfile(null);
    sessionController.setCustomWorldError(null);
    autosaveCoordinator.resetAutoSaveTrackingToIdle();
    sessionController.setCustomWorldGenerationViewSource(null);
    sessionController.setCustomWorldResultViewSource(null);
    if (shouldReturnToDraftShelf()) {
      returnToCreationFlowSource();
      return;
    }
    setSelectionStage(selectedDetailEntry ? 'detail' : 'platform');
  }, [
    autosaveCoordinator,
    returnToCreationFlowSource,
    selectedDetailEntry,
    sessionController,
    setSelectionStage,
    shouldReturnToDraftShelf,
  ]);

  const handleStartSelectedWorld = useCallback(() => {
    if (!selectedDetailEntry) {
      return;
    }

    runProtectedAction(() => {
      handleCustomWorldSelect(selectedDetailEntry.profile);
    });
  }, [handleCustomWorldSelect, runProtectedAction, selectedDetailEntry]);

  const handleDeleteLibraryEntry = useCallback(
    (entry: CustomWorldLibraryEntry<CustomWorldProfile>) => {
      if (!entry.profileId || deletingCreationWorkId) {
        return;
      }

      requestDeleteCreationWork({
        id: entry.profileId,
        title: entry.worldName,
        detail: '删除后会从你的作品列表和公开广场中移除。',
        run: () => {
          setDeletingCreationWorkId(entry.profileId);
          platformBootstrap.setPlatformError(null);

          void deleteRpgEntryWorldProfile(entry.profileId)
            .then(async (entries) => {
              platformBootstrap.setSavedCustomWorldEntries(entries);
              await platformBootstrap.refreshCustomWorldWorks().catch(() => []);
              await platformBootstrap.refreshPublishedGallery().catch(() => []);
            })
            .catch((error) => {
              platformBootstrap.setPlatformError(
                resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [deletingCreationWorkId, platformBootstrap, requestDeleteCreationWork],
  );

  const handleDeletePublishedWork = useCallback(
    (work: (typeof creationHubItems)[number]) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('rpg', [
        work.workId,
        work.sessionId,
        work.profileId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.title,
        detail:
          work.status === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          platformBootstrap.setPlatformError(null);

          const deleteTask =
            work.sourceType === 'published_profile' && work.profileId
              ? deleteRpgEntryWorldProfile(work.profileId).then(
                  async (entries) => {
                    platformBootstrap.setSavedCustomWorldEntries(entries);
                    await platformBootstrap
                      .refreshCustomWorldWorks()
                      .catch(() => []);
                  },
                )
              : work.sourceType === 'agent_session' && work.sessionId
                ? deleteRpgCreationAgentSession(work.sessionId).then(
                    (items) => {
                      platformBootstrap.setCustomWorldWorkEntries(items);
                    },
                  )
                : Promise.reject(new Error('当前 RPG 作品缺少可删除 ID。'));

          void deleteTask
            .then(async () => {
              markDraftNoticeSeen(noticeKeys);
              await platformBootstrap.refreshPublishedGallery().catch(() => []);
            })
            .catch((error) => {
              platformBootstrap.setPlatformError(
                resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      platformBootstrap,
      requestDeleteCreationWork,
    ],
  );

  const handleDeleteBigFishWork = useCallback(
    (work: BigFishWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('big-fish', [
        work.workId,
        work.sourceSessionId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.title,
        detail:
          work.status === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setBigFishError(null);

          void deleteBigFishWork(work.sourceSessionId)
            .then(async (response) => {
              markDraftNoticeSeen(noticeKeys);
              setBigFishWorks(response.items);
              await refreshBigFishGallery().catch(() => []);
            })
            .catch((error) => {
              setBigFishError(
                resolveBigFishErrorMessage(error, '删除大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshBigFishGallery,
      requestDeleteCreationWork,
      resolveBigFishErrorMessage,
      setBigFishError,
    ],
  );

  const handleDeletePuzzleWork = useCallback(
    (work: PuzzleWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('puzzle', [
        work.workId,
        work.profileId,
        work.sourceSessionId,
        buildPuzzleResultWorkId(work.sourceSessionId),
        buildPuzzleResultProfileId(work.sourceSessionId),
      ]);

      const displayName =
        work.workTitle?.trim() || work.levelName.trim() || '未命名拼图';
      requestDeleteCreationWork({
        id: work.workId,
        title: displayName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setPuzzleFormDraftPayload(null);
          setPuzzleError(null);

          void deletePuzzleWork(work.profileId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setPuzzleWorks(response.items);
              void refreshPuzzleGallery();
            })
            .catch((error) => {
              setPuzzleError(
                resolvePuzzleErrorMessage(error, '删除拼图作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshPuzzleGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setPuzzleError,
    ],
  );

  const handleDeleteMatch3DWork = useCallback(
    (work: Match3DWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('match3d', [
        work.workId,
        work.profileId,
        work.sourceSessionId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.gameName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setMatch3DFormDraftPayload(null);
          setMatch3DError(null);

          void deleteMatch3DWork(work.profileId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setMatch3DWorks(mapMatch3DWorksForRuntimeUi(response.items));
              void refreshMatch3DGallery();
            })
            .catch((error) => {
              setMatch3DError(
                resolveMatch3DErrorMessage(error, '删除抓大鹅作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshMatch3DGallery,
      requestDeleteCreationWork,
      resolveMatch3DErrorMessage,
      setMatch3DFormDraftPayload,
      setMatch3DError,
    ],
  );

  const handleDeleteSquareHoleWork = useCallback(
    (work: SquareHoleWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('square-hole', [
        work.workId,
        work.profileId,
        work.sourceSessionId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.gameName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setSquareHoleError(null);

          void deleteSquareHoleWork(work.profileId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setSquareHoleWorks(response.items);
              void refreshSquareHoleGallery();
            })
            .catch((error) => {
              setSquareHoleError(
                resolveSquareHoleErrorMessage(error, '删除方洞挑战作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshSquareHoleGallery,
      requestDeleteCreationWork,
      resolveSquareHoleErrorMessage,
      setSquareHoleError,
    ],
  );

  const handleDeleteJumpHopWork = useCallback(
    (work: JumpHopWorkSummaryResponse) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('jump-hop', [
        work.workId,
        work.profileId,
        work.sourceSessionId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.workTitle || '未命名跳一跳',
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setJumpHopError(null);

          void jumpHopClient
            .deleteWork(work.profileId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setJumpHopWorks(response.items);
              void refreshJumpHopGallery();
            })
            .catch((error) => {
              setJumpHopError(
                resolvePuzzleErrorMessage(error, '删除跳一跳作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshJumpHopGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setJumpHopError,
    ],
  );

  const handleDeleteWoodenFishWork = useCallback(
    (work: WoodenFishWorkSummaryResponse) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('wooden-fish', [
        work.workId,
        work.profileId,
        work.sourceSessionId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.workTitle || '未命名敲木鱼',
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setWoodenFishError(null);

          void woodenFishClient
            .deleteWork(work.profileId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setWoodenFishWorks(response.items);
              void refreshWoodenFishGallery();
            })
            .catch((error) => {
              setWoodenFishError(
                resolvePuzzleErrorMessage(error, '删除敲木鱼作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshWoodenFishGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setWoodenFishError,
    ],
  );

  const handleDeleteBarkBattleWork = useCallback(
    (work: BarkBattleWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('bark-battle', [
        work.workId,
        work.draftId,
      ]);

      requestDeleteCreationWork({
        id: work.workId,
        title: work.title || '未命名汪汪声浪',
        detail:
          work.status === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.workId);
          setBarkBattleError(null);

          void deleteBarkBattleWork(work.workId)
            .then((response) => {
              markDraftNoticeSeen(noticeKeys);
              setBarkBattleWorks(mergeBarkBattleWorksByWorkId(response.items));
              void refreshBarkBattleGallery();
            })
            .catch((error) => {
              setBarkBattleError(
                resolveBarkBattleErrorMessage(error, '删除汪汪声浪作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshBarkBattleGallery,
      requestDeleteCreationWork,
      resolveBarkBattleErrorMessage,
      setBarkBattleError,
    ],
  );

  const handleDeleteVisualNovelWork = useCallback(
    (work: VisualNovelWorkSummary) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('visual-novel', [
        work.profileId,
      ]);

      requestDeleteCreationWork({
        id: work.profileId,
        title: work.title || '未命名视觉小说',
        detail:
          work.publishStatus === 'published'
            ? '删除后会从你的作品列表和公开广场中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.profileId);
          setVisualNovelError(null);

          void deleteVisualNovelWork(work.profileId)
            .then(async (response) => {
              markDraftNoticeSeen(noticeKeys);
              setVisualNovelWorks(response.works);
              await refreshVisualNovelGallery();
            })
            .catch((error) => {
              setVisualNovelError(
                resolvePuzzleErrorMessage(error, '删除视觉小说作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      deletingCreationWorkId,
      markDraftNoticeSeen,
      refreshVisualNovelGallery,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      setVisualNovelError,
    ],
  );

  const handleDeleteBabyObjectMatchWork = useCallback(
    (work: BabyObjectMatchDraft) => {
      if (deletingCreationWorkId) {
        return;
      }
      const noticeKeys = collectDraftNoticeKeys('baby-object-match', [
        work.profileId,
        work.draftId,
      ]);
      const displayName = work.workTitle.trim() || work.templateName;

      requestDeleteCreationWork({
        id: work.profileId,
        title: displayName,
        detail:
          work.publicationStatus === 'published'
            ? '删除后会从你的作品列表和寓教于乐板块中移除。'
            : '删除后会从你的作品列表中移除。',
        run: () => {
          setDeletingCreationWorkId(work.profileId);
          setBabyObjectMatchError(null);

          void deleteLocalBabyObjectMatchDraft(work.profileId)
            .then((nextDrafts) => {
              markDraftNoticeSeen(noticeKeys);
              setBabyObjectMatchDrafts(nextDrafts);
              setBabyObjectMatchDraft((current) =>
                current?.profileId === work.profileId ? null : current,
              );
              if (
                babyObjectMatchDraft?.profileId === work.profileId &&
                (selectionStage === 'baby-object-match-result' ||
                  selectionStage === 'baby-object-match-runtime')
              ) {
                enterCreateTab();
                setSelectionStage('platform');
              }
            })
            .catch((error) => {
              setBabyObjectMatchError(
                resolvePuzzleErrorMessage(error, '删除宝贝识物作品失败。'),
              );
            })
            .finally(() => {
              setDeletingCreationWorkId(null);
            });
        },
      });
    },
    [
      babyObjectMatchDraft?.profileId,
      deletingCreationWorkId,
      enterCreateTab,
      markDraftNoticeSeen,
      requestDeleteCreationWork,
      resolvePuzzleErrorMessage,
      selectionStage,
      setSelectionStage,
    ],
  );

  const clearSelectedPublicWorkAuthor = useCallback(() => {
    publicWorkAuthorRequestKeyRef.current += 1;
    setSelectedPublicWorkAuthor(null);
  }, []);

  const loadSelectedPublicWorkAuthor = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      const requestKey = publicWorkAuthorRequestKeyRef.current + 1;
      publicWorkAuthorRequestKeyRef.current = requestKey;
      setSelectedPublicWorkAuthor(null);

      void resolvePublicWorkAuthorSummary(entry)
        .then((author) => {
          if (publicWorkAuthorRequestKeyRef.current === requestKey) {
            setSelectedPublicWorkAuthor(author);
          }
        })
        .catch(() => {
          if (publicWorkAuthorRequestKeyRef.current === requestKey) {
            setSelectedPublicWorkAuthor(null);
          }
        });
    },
    [],
  );

  const openPublicWorkDetail = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (!canExposePublicWork(entry)) {
        setSelectedPublicWorkDetail(null);
        setPublicWorkDetailError(EDUTAINMENT_HIDDEN_MESSAGE);
        setSelectionStage('platform');
        return;
      }

      setSelectedPublicWorkDetail(entry);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');
      if (entry.publicWorkCode?.trim()) {
        pushAppHistoryPath(
          buildPublicWorkStagePath('work-detail', entry.publicWorkCode),
        );
      }
    },
    [setSelectionStage],
  );

  const handleClaimPuzzlePointIncentive = useCallback(
    (work: PuzzleWorkSummary) => {
      if (claimingPuzzlePointIncentiveProfileId) {
        return;
      }

      runProtectedAction(() => {
        setClaimingPuzzlePointIncentiveProfileId(work.profileId);
        setPuzzleError(null);

        void claimPuzzleWorkPointIncentive(work.profileId)
          .then((response) => {
            const updatedWork = response.item;
            setPuzzleWorks((current) =>
              current.map((item) => mergePuzzleWorkSummary(item, updatedWork)),
            );
            setPuzzleGalleryEntries((current) =>
              current.map((item) => mergePuzzleWorkSummary(item, updatedWork)),
            );
            setSelectedPuzzleDetail((current) =>
              current ? mergePuzzleWorkSummary(current, updatedWork) : current,
            );
            syncUpdatedPublicWorkDetail(
              mapPuzzleWorkToPublicWorkDetail(updatedWork),
            );
            void platformBootstrap.refreshProfileDashboard();
          })
          .catch((error) => {
            setPuzzleError(
              resolvePuzzleErrorMessage(error, '领取拼图积分激励失败。'),
            );
          })
          .finally(() => {
            setClaimingPuzzlePointIncentiveProfileId(null);
          });
      });
    },
    [
      claimingPuzzlePointIncentiveProfileId,
      platformBootstrap,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      setPuzzleError,
      syncUpdatedPublicWorkDetail,
    ],
  );

  const likePublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(() => {
        setIsPublicWorkDetailBusy(true);
        setPublicWorkDetailError(null);

        if (isBigFishGalleryEntry(entry)) {
          void likeBigFishGalleryWork(entry.profileId)
            .then((response) => {
              const updatedWork = response.items.find(
                (item) => item.sourceSessionId === entry.profileId,
              );
              if (!updatedWork) {
                return;
              }
              setBigFishGalleryEntries((current) =>
                current.map((item) =>
                  mergeBigFishWorkSummary(item, updatedWork),
                ),
              );
              setBigFishWorks((current) =>
                current.map((item) =>
                  mergeBigFishWorkSummary(item, updatedWork),
                ),
              );
              syncUpdatedPublicWorkDetail(
                mapBigFishWorkToPublicWorkDetail(updatedWork),
              );
              setBigFishRuntimeWork((current) =>
                current
                  ? mergeBigFishWorkSummary(current, updatedWork)
                  : current,
              );
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveBigFishErrorMessage(error, '点赞大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          void likePuzzleGalleryWork(entry.profileId)
            .then((response) => {
              const updatedWork = response.item;
              setPuzzleGalleryEntries((current) =>
                current.map((item) =>
                  mergePuzzleWorkSummary(item, updatedWork),
                ),
              );
              setPuzzleWorks((current) =>
                current.map((item) =>
                  mergePuzzleWorkSummary(item, updatedWork),
                ),
              );
              setSelectedPuzzleDetail((current) =>
                current
                  ? mergePuzzleWorkSummary(current, updatedWork)
                  : current,
              );
              syncUpdatedPublicWorkDetail(
                mapPuzzleWorkToPublicWorkDetail(updatedWork),
              );
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolvePuzzleErrorMessage(error, '点赞拼图作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isCustomWorldGalleryEntry(entry)) {
          void likeRpgEntryWorldGallery(entry.ownerUserId, entry.profileId)
            .then((updatedEntry) => {
              setSelectedDetailEntry((current) =>
                current?.profileId === updatedEntry.profileId
                  ? updatedEntry
                  : current,
              );
              platformBootstrap.setPublishedGalleryEntries((current) =>
                current.map((item) =>
                  item.profileId === updatedEntry.profileId
                    ? updatedEntry
                    : item,
                ),
              );
              syncUpdatedPublicWorkDetail(
                mapRpgGalleryCardToPublicWorkDetail(updatedEntry),
              );
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveRpgCreationErrorMessage(error, '点赞 RPG 作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (
          isPuzzleClearGalleryEntry(entry) ||
          isJumpHopGalleryEntry(entry) ||
          isWoodenFishGalleryEntry(entry) ||
          isMatch3DGalleryEntry(entry) ||
          isEdutainmentGalleryEntry(entry) ||
          isBarkBattleGalleryEntry(entry) ||
          isSquareHoleGalleryEntry(entry) ||
          isVisualNovelGalleryEntry(entry)
        ) {
          setPublicWorkDetailError(
            resolveUnsupportedPublicWorkActionMessage(entry, '点赞'),
          );
          setIsPublicWorkDetailBusy(false);
          return;
        }

        setPublicWorkDetailError('未知公开作品类型，无法点赞。');
        setIsPublicWorkDetailBusy(false);
      });
    },
    [
      isPublicWorkDetailBusy,
      platformBootstrap,
      resolveBigFishErrorMessage,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      syncUpdatedPublicWorkDetail,
    ],
  );

  useEffect(() => {
    const detailEntry =
      selectionStage === 'work-detail'
        ? selectedPublicWorkDetail
        : selectionStage === 'detail' &&
            selectedDetailEntry &&
            selectedDetailEntry.visibility !== 'draft'
          ? mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)
          : null;

    if (!detailEntry) {
      clearSelectedPublicWorkAuthor();
      return;
    }

    loadSelectedPublicWorkAuthor(detailEntry);
  }, [
    clearSelectedPublicWorkAuthor,
    loadSelectedPublicWorkAuthor,
    selectedDetailEntry,
    selectedPublicWorkDetail,
    selectionStage,
  ]);

  const openRpgPublicWorkDetail = useCallback(
    async (entry: CustomWorldGalleryCard) => {
      if (!canExposePublicWork(entry)) {
        setSelectedPublicWorkDetail(null);
        setPublicWorkDetailError(EDUTAINMENT_HIDDEN_MESSAGE);
        setSelectionStage('platform');
        return;
      }

      setIsPublicWorkDetailBusy(true);
      setPublicWorkDetailError(null);
      clearSelectedPublicWorkAuthor();
      setSelectedPublicWorkDetail(entry);
      setSelectionStage('work-detail');

      try {
        const detailEntry =
          await detailNavigation.loadGalleryDetailEntry(entry);
        setSelectedDetailEntry(detailEntry);
        const detailCard = mapRpgGalleryCardToPublicWorkDetail(detailEntry);
        if (!canExposePublicWork(detailCard)) {
          setSelectedDetailEntry(null);
          setSelectedPublicWorkDetail(null);
          setPublicWorkDetailError(EDUTAINMENT_HIDDEN_MESSAGE);
          setSelectionStage('platform');
          return;
        }

        setSelectedPublicWorkDetail(detailCard);
        if (detailEntry.publicWorkCode?.trim()) {
          pushAppHistoryPath(
            buildPublicWorkStagePath('work-detail', detailEntry.publicWorkCode),
          );
        }
      } catch (error) {
        setSelectedPublicWorkDetail(entry);
        setPublicWorkDetailError(
          resolveRpgCreationErrorMessage(error, '读取作品详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      clearSelectedPublicWorkAuthor,
      detailNavigation,
      setSelectedDetailEntry,
      setSelectionStage,
    ],
  );

  const openPuzzlePublicWorkDetail = useCallback(
    async (
      profileId: string,
      returnTarget: PuzzleDetailReturnTarget = {
        tab: platformBootstrap.platformTab,
      },
    ) => {
      setIsPuzzleBusy(true);
      setIsPublicWorkDetailBusy(true);
      setPuzzleError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const { item } = await getPuzzleGalleryDetail(profileId);
        const detailEntry = mapPuzzleWorkToPublicWorkDetail(item);
        if (!canExposePublicWork(detailEntry)) {
          setSelectedPuzzleDetail(null);
          setPublicWorkDetailError(EDUTAINMENT_HIDDEN_MESSAGE);
          setSelectionStage('platform');
          return;
        }

        setSelectedPuzzleDetail(item);
        setPuzzleDetailReturnTarget(returnTarget);
        openPublicWorkDetail(detailEntry);
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleGalleryEntries((current) =>
            current.filter((entry) => entry.profileId !== profileId),
          );
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          returnPlatformHomeAfterMissingWork();
          return;
        }

        setPublicWorkDetailError(
          resolvePuzzleErrorMessage(error, '读取拼图详情失败。'),
        );
      } finally {
        setIsPuzzleBusy(false);
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      platformBootstrap.platformTab,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const openMatch3DPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setMatch3DError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          match3dGalleryEntries.length > 0
            ? match3dGalleryEntries
            : await refreshMatch3DGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到抓大鹅作品。');
        }

        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
      } catch (error) {
        setPublicWorkDetailError(
          resolveMatch3DErrorMessage(error, '读取抓大鹅详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      match3dGalleryEntries,
      openPublicWorkDetail,
      refreshMatch3DGallery,
      resolveMatch3DErrorMessage,
      setMatch3DError,
      setSelectionStage,
    ],
  );

  const openSquareHolePublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setSquareHoleError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          squareHoleGalleryEntries.length > 0
            ? squareHoleGalleryEntries
            : await refreshSquareHoleGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到方洞挑战作品。');
        }

        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(matchedEntry));
      } catch (error) {
        setPublicWorkDetailError(
          resolveSquareHoleErrorMessage(error, '读取方洞挑战详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      refreshSquareHoleGallery,
      resolveSquareHoleErrorMessage,
      setSelectionStage,
      setSquareHoleError,
      squareHoleGalleryEntries,
    ],
  );

  const openVisualNovelPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setVisualNovelError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const entries =
          visualNovelGalleryEntries.length > 0
            ? visualNovelGalleryEntries
            : await refreshVisualNovelGallery();
        const matchedEntry = entries.find(
          (entry) => entry.profileId === profileId,
        );

        if (!matchedEntry) {
          throw new Error('未找到视觉小说作品。');
        }

        openPublicWorkDetail(
          mapVisualNovelWorkToPublicWorkDetail(matchedEntry),
        );
      } catch (error) {
        setPublicWorkDetailError(
          resolvePuzzleErrorMessage(error, '读取视觉小说详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [
      openPublicWorkDetail,
      refreshVisualNovelGallery,
      resolvePuzzleErrorMessage,
      setSelectionStage,
      setVisualNovelError,
      visualNovelGalleryEntries,
    ],
  );

  const openJumpHopPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setJumpHopError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const detail = await jumpHopClient.getWorkDetail(profileId);
        setJumpHopWork(detail.item);
        openPublicWorkDetail(mapJumpHopWorkToPublicWorkDetail(detail.item));
      } catch (error) {
        setPublicWorkDetailError(
          resolveRpgCreationErrorMessage(error, '读取跳一跳详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [openPublicWorkDetail, setJumpHopError, setSelectionStage],
  );

  const openJumpHopDraft = useCallback(
    async (item: JumpHopWorkSummaryResponse) => {
      const noticeIds = [item.workId, item.profileId, item.sourceSessionId];
      const hasFailedNotice = isDraftNoticeFailed('jump-hop', noticeIds);
      const sessionId = normalizeCreationUrlValue(item.sourceSessionId);
      markDraftNoticeSeen(collectDraftNoticeKeys('jump-hop', noticeIds));

      if (item.publicationStatus === 'published') {
        void openJumpHopPublicWorkDetail(item.profileId);
        return;
      }

      setJumpHopError(null);
      setPublicWorkDetailError(null);
      setIsJumpHopBusy(true);
      if (
        hasFailedNotice &&
        sessionId === jumpHopSession?.sessionId &&
        jumpHopGenerationState?.phase === 'failed'
      ) {
        enterCreateTab();
        setSelectionStage('jump-hop-generating');
        setIsJumpHopBusy(false);
        return;
      }

      if (item.generationStatus === 'generating' && !hasFailedNotice) {
        const pendingSession = buildJumpHopPendingSession(item);
        setJumpHopSession(pendingSession);
        setJumpHopRun(null);
        setJumpHopWork(null);
        setJumpHopGenerationState(
          createMiniGameDraftGenerationState(
            'jump-hop',
            resolveMiniGameDraftGenerationStartedAtMs(item.updatedAt),
          ),
        );
        writeCreationUrlState(
          buildJumpHopCreationUrlState({ session: pendingSession }),
        );
        enterCreateTab();
        setSelectionStage('jump-hop-generating');
        setIsJumpHopBusy(false);
        return;
      }

      try {
        const detail = await jumpHopClient.getWorkDetail(item.profileId, {
          audience: 'creation',
        });
        setJumpHopSession(null);
        setJumpHopRun(null);
        setJumpHopWork(detail.item);
        setJumpHopRuntimeReturnStage('jump-hop-result');
        enterCreateTab();
        pushAppHistoryPath('/creation/jump-hop/result');
        writeCreationUrlState(
          buildJumpHopCreationUrlState({ work: detail.item }),
        );
        setSelectionStage('jump-hop-result');
      } catch (error) {
        setJumpHopError(
          resolveRpgCreationErrorMessage(error, '读取跳一跳草稿失败。'),
        );
      } finally {
        setIsJumpHopBusy(false);
      }
    },
    [
      enterCreateTab,
      isDraftNoticeFailed,
      jumpHopGenerationState?.phase,
      jumpHopSession?.sessionId,
      markDraftNoticeSeen,
      openJumpHopPublicWorkDetail,
      setSelectionStage,
      writeCreationUrlState,
    ],
  );

  const openPuzzleClearPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setPuzzleClearError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const detail = await puzzleClearClient.getRuntimeWorkDetail(profileId);
        setPuzzleClearWork(detail.item);
        openPublicWorkDetail(mapPuzzleClearWorkToPlatformGalleryCard(detail.item));
      } catch (error) {
        setPublicWorkDetailError(
          resolveRpgCreationErrorMessage(error, '读取拼消消详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [openPublicWorkDetail, setSelectionStage],
  );

  const openPuzzleClearDraft = useCallback(
    async (item: PuzzleClearWorkSummaryResponse) => {
      markDraftNoticeSeen(
        collectDraftNoticeKeys('puzzle-clear', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      );

      if (item.publicationStatus === 'published') {
        void openPuzzleClearPublicWorkDetail(item.profileId);
        return;
      }

      setPuzzleClearError(null);
      setPublicWorkDetailError(null);
      setIsPuzzleClearBusy(true);
      if (item.generationStatus === 'generating') {
        const pendingSession = buildPuzzleClearPendingSession(item);
        setPuzzleClearSession(pendingSession);
        setPuzzleClearRun(null);
        setPuzzleClearWork(null);
        writeCreationUrlState(
          buildPuzzleClearCreationUrlState({ session: pendingSession }),
        );
        enterCreateTab();
        setSelectionStage('puzzle-clear-generating');
        setIsPuzzleClearBusy(false);
        return;
      }
      try {
        const detail = await puzzleClearClient.getWorkDetail(item.profileId);
        const recoveredSession = buildPuzzleClearSessionFromWorkDetail(
          detail.item,
          item,
        );
        setPuzzleClearSession(recoveredSession);
        setPuzzleClearRun(null);
        setPuzzleClearWork(detail.item);
        setPuzzleClearRuntimeReturnStage('puzzle-clear-result');
        writeCreationUrlState(
          buildPuzzleClearCreationUrlState({
            session: recoveredSession,
            work: detail.item,
          }),
        );
        enterCreateTab();
        setSelectionStage('puzzle-clear-result');
      } catch (error) {
        setPuzzleClearError(
          resolveRpgCreationErrorMessage(error, '读取拼消消草稿失败。'),
        );
        enterCreateTab();
        setSelectionStage('puzzle-clear-generating');
      } finally {
        setIsPuzzleClearBusy(false);
      }
    },
    [
      enterCreateTab,
      markDraftNoticeSeen,
      openPuzzleClearPublicWorkDetail,
      setSelectionStage,
    ],
  );

  const openWoodenFishPublicWorkDetail = useCallback(
    async (profileId: string) => {
      setIsPublicWorkDetailBusy(true);
      setWoodenFishError(null);
      setPublicWorkDetailError(null);
      setSelectionStage('work-detail');

      try {
        const detail = await woodenFishClient.getWorkDetail(profileId);
        setWoodenFishWork(detail.item);
        openPublicWorkDetail(mapWoodenFishWorkToPublicWorkDetail(detail.item));
      } catch (error) {
        setPublicWorkDetailError(
          resolveRpgCreationErrorMessage(error, '读取敲木鱼详情失败。'),
        );
      } finally {
        setIsPublicWorkDetailBusy(false);
      }
    },
    [openPublicWorkDetail, setSelectionStage],
  );

  const openWoodenFishDraft = useCallback(
    async (item: WoodenFishWorkSummaryResponse) => {
      const noticeIds = [item.workId, item.profileId, item.sourceSessionId];
      const hasFailedNotice = isDraftNoticeFailed('wooden-fish', noticeIds);
      const sessionId =
        normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
      markDraftNoticeSeen(collectDraftNoticeKeys('wooden-fish', noticeIds));

      if (item.publicationStatus === 'published') {
        void openWoodenFishPublicWorkDetail(item.profileId);
        return;
      }

      setWoodenFishError(null);
      setPublicWorkDetailError(null);
      setIsWoodenFishBusy(true);
      if (
        hasFailedNotice &&
        sessionId === woodenFishSession?.sessionId &&
        woodenFishGenerationState?.phase === 'failed'
      ) {
        enterCreateTab();
        setSelectionStage('wooden-fish-generating');
        setIsWoodenFishBusy(false);
        return;
      }

      if (item.generationStatus === 'generating' && !hasFailedNotice) {
        const pendingSession = buildWoodenFishPendingSession(item);
        setWoodenFishSession(pendingSession);
        setWoodenFishRun(null);
        setWoodenFishWork(null);
        setWoodenFishGenerationState(
          createMiniGameDraftGenerationState(
            'wooden-fish',
            resolveMiniGameDraftGenerationStartedAtMs(item.updatedAt),
          ),
        );
        writeCreationUrlState(
          buildWoodenFishCreationUrlState({ session: pendingSession }),
        );
        enterCreateTab();
        setSelectionStage('wooden-fish-generating');
        setIsWoodenFishBusy(false);
        return;
      }
      try {
        const detail = await woodenFishClient.getWorkDetail(item.profileId);
        const recoveredSession = buildWoodenFishSessionFromWorkDetail(
          detail.item,
          item,
        );
        setWoodenFishSession(recoveredSession);
        setWoodenFishRun(null);
        setWoodenFishWork(detail.item);
        setWoodenFishRuntimeReturnStage('wooden-fish-result');
        writeCreationUrlState(
          buildWoodenFishCreationUrlState({
            session: recoveredSession,
            work: detail.item,
          }),
        );
        enterCreateTab();
        setSelectionStage('wooden-fish-result');
      } catch (error) {
        setWoodenFishError(
          resolveRpgCreationErrorMessage(error, '读取敲木鱼草稿失败。'),
        );
        enterCreateTab();
        setSelectionStage(
          hasFailedNotice ? 'wooden-fish-workspace' : 'wooden-fish-generating',
        );
      } finally {
        setIsWoodenFishBusy(false);
      }
    },
    [
      enterCreateTab,
      isDraftNoticeFailed,
      markDraftNoticeSeen,
      openWoodenFishPublicWorkDetail,
      woodenFishGenerationState?.phase,
      woodenFishSession?.sessionId,
      writeCreationUrlState,
      setSelectionStage,
    ],
  );

  const openPublicGalleryDetail = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isBigFishGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isPuzzleGalleryEntry(entry)) {
        void openPuzzlePublicWorkDetail(entry.profileId, {
          tab: platformBootstrap.platformTab,
        });
        return;
      }

      if (isPuzzleClearGalleryEntry(entry)) {
        void openPuzzleClearPublicWorkDetail(entry.profileId);
        return;
      }

      if (isMatch3DGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isSquareHoleGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isJumpHopGalleryEntry(entry)) {
        void openJumpHopPublicWorkDetail(entry.profileId);
        return;
      }

      if (isWoodenFishGalleryEntry(entry)) {
        void openWoodenFishPublicWorkDetail(entry.profileId);
        return;
      }

      if (isVisualNovelGalleryEntry(entry)) {
        void openVisualNovelPublicWorkDetail(entry.profileId);
        return;
      }

      if (isBarkBattleGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isEdutainmentGalleryEntry(entry)) {
        openPublicWorkDetail(entry);
        return;
      }

      if (isCustomWorldGalleryEntry(entry)) {
        void openRpgPublicWorkDetail(entry);
        return;
      }

      setPublicWorkDetailError('未知公开作品类型，无法打开作品详情。');
    },
    [
      openPuzzlePublicWorkDetail,
      openPublicWorkDetail,
      openPuzzleClearPublicWorkDetail,
      openJumpHopPublicWorkDetail,
      openWoodenFishPublicWorkDetail,
      openRpgPublicWorkDetail,
      openVisualNovelPublicWorkDetail,
      platformBootstrap.platformTab,
    ],
  );

  const openRecommendGalleryDetail = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      runProtectedAction(() => {
        openPublicGalleryDetail(entry);
      });
    },
    [openPublicGalleryDetail, runProtectedAction],
  );
  const openPuzzleDetail = useCallback(
    async (
      profileId: string,
      returnTarget: PuzzleDetailReturnTarget = {
        tab: platformBootstrap.platformTab,
      },
    ) => {
      setIsPuzzleBusy(true);
      setPuzzleError(null);

      try {
        const { item } = await getPuzzleGalleryDetail(profileId);
        setSelectedPuzzleDetail(item);
        setPuzzleDetailReturnTarget(returnTarget);
        setSelectionStage('puzzle-gallery-detail');
        pushAppHistoryPath(
          buildPublicWorkStagePath(
            'puzzle-gallery-detail',
            buildPuzzlePublicWorkCode(item.profileId),
          ),
        );
      } catch (error) {
        if (isMissingPuzzleWorkError(error)) {
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          setPlatformTab('home');
          setSelectionStage('platform');
          if (!maybeAlertWorkNotFoundAndReturnHome()) {
            pushAppHistoryPath('/');
          }
          return;
        }

        setPuzzleError(resolvePuzzleErrorMessage(error, '读取拼图详情失败。'));
      } finally {
        setIsPuzzleBusy(false);
      }
    },
    [
      platformBootstrap.platformTab,
      resolvePuzzleErrorMessage,
      setIsPuzzleBusy,
      setPuzzleError,
      returnPlatformHomeAfterMissingWork,
    ],
  );

  const openPuzzleDraft = useCallback(
    async (item: PuzzleWorkSummary) => {
      const noticeKeys = collectDraftNoticeKeys('puzzle', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
        buildPuzzleResultWorkId(item.sourceSessionId),
        buildPuzzleResultProfileId(item.sourceSessionId),
      ]);
      const failedNotice = getDraftGenerationNotice(noticeKeys);
      const isPersistedFailed = isPersistedDraftFailed(item.generationStatus);
      const hasGeneratingNotice = isDraftNoticeGenerating('puzzle', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
        buildPuzzleResultWorkId(item.sourceSessionId),
        buildPuzzleResultProfileId(item.sourceSessionId),
      ]);
      const hasFailedNotice = isDraftNoticeFailed('puzzle', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
        buildPuzzleResultWorkId(item.sourceSessionId),
        buildPuzzleResultProfileId(item.sourceSessionId),
      ]);
      const noticeErrorMessage =
        failedNotice?.status === 'failed'
          ? (failedNotice.message ?? buildDraftFailedShelfSummary('puzzle'))
          : buildDraftFailedShelfSummary('puzzle');
      const isMarkedGenerating =
        !hasFailedNotice &&
        ((hasGeneratingNotice && !resolvePuzzleWorkCoverImageSrc(item)) ||
          isPersistedPuzzleDraftGenerating(item));
      setPuzzleOperation(null);
      setPuzzleRun(null);
      setPuzzleRuntimeAuthMode('default');
      setSelectedPuzzleDetail(null);
      if (!item.sourceSessionId?.trim()) {
        if (item.publicationStatus === 'published') {
          await openPuzzleDetail(item.profileId, { tab: 'create' });
          return;
        }

        setPuzzleError('这份拼图草稿缺少会话信息，请重新开始创作。');
        return;
      }

      const backgroundTask = getPuzzleBackgroundCompileTask(
        item.sourceSessionId,
      );
      const activeGenerationState =
        backgroundTask?.generationState ?? puzzleGenerationViewState;
      const failedGenerationState =
        backgroundTask?.generationState.phase === 'failed'
          ? backgroundTask.generationState
          : item.sourceSessionId === puzzleSession?.sessionId &&
              activeGenerationState?.phase === 'failed'
            ? activeGenerationState
            : hasFailedNotice || isPersistedFailed
              ? createFailedMiniGameDraftGenerationStateForRestoredDraft(
                  'puzzle',
                  item.updatedAt,
                  noticeErrorMessage,
                  { puzzleAiRedraw: true },
                )
              : null;

      if ((hasFailedNotice || isPersistedFailed) && failedGenerationState) {
        let failedSession = backgroundTask?.session ?? null;
        let failedPayload = backgroundTask?.payload ?? null;
        const failedError =
          backgroundTask?.error ?? failedNotice?.message ?? noticeErrorMessage;
        if (!failedSession) {
          try {
            const { session: latestSession } = await getPuzzleAgentSession(
              item.sourceSessionId,
            );
            failedSession = latestSession;
            failedPayload = buildPuzzleFormPayloadFromSession(latestSession);
          } catch {
            failedPayload = buildPuzzleFormPayloadFromWork(item);
          }
        }
        if (!failedPayload) {
          failedPayload = failedSession
            ? buildPuzzleFormPayloadFromSession(failedSession)
            : buildPuzzleFormPayloadFromWork(item);
        }
        if (backgroundTask) {
          puzzleFlow.setSession(backgroundTask.session);
        } else if (failedSession) {
          puzzleFlow.setSession(failedSession);
        }
        setPuzzleFormDraftPayload(failedPayload);
        setPuzzleError(failedError);
        if (failedSession) {
          setPuzzleBackgroundCompileTasks((current) => ({
            ...current,
            [failedSession!.sessionId]: {
              session: failedSession!,
              payload: failedPayload!,
              generationState: failedGenerationState,
              error: failedError,
            },
          }));
        }
        enterCreateTab();
        selectionStageRef.current = 'puzzle-generating';
        activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
        setPuzzleGenerationState(failedGenerationState);
        setSelectionStage('puzzle-generating');
        return;
      }

      if (
        item.sourceSessionId === puzzleSession?.sessionId &&
        isMiniGameDraftGenerating(activeGenerationState)
      ) {
        if (!activeGenerationState) {
          return;
        }
        const rebasedGenerationState =
          rebaseMiniGameDraftGenerationStateForDisplay(activeGenerationState);
        enterCreateTab();
        selectionStageRef.current = 'puzzle-generating';
        activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
        setPuzzleGenerationState(rebasedGenerationState);
        if (backgroundTask) {
          setPuzzleBackgroundCompileTasks((current) => ({
            ...current,
            [backgroundTask.session.sessionId]: {
              ...backgroundTask,
              generationState: rebasedGenerationState,
            },
          }));
        }
        setSelectionStage('puzzle-generating');
        return;
      }

      if (
        backgroundTask &&
        isMiniGameDraftGenerating(backgroundTask.generationState)
      ) {
        const rebasedTask =
          rebaseMiniGameDraftBackgroundCompileTaskForDisplay(backgroundTask);
        puzzleFlow.setSession(rebasedTask.session);
        setPuzzleFormDraftPayload(rebasedTask.payload);
        setPuzzleGenerationState(rebasedTask.generationState);
        setPuzzleBackgroundCompileTasks((current) => ({
          ...current,
          [rebasedTask.session.sessionId]: rebasedTask,
        }));
        if (backgroundTask.error) {
          setPuzzleError(backgroundTask.error);
        }
        enterCreateTab();
        selectionStageRef.current = 'puzzle-generating';
        activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
        setSelectionStage('puzzle-generating');
        return;
      }

      if (isMarkedGenerating) {
        try {
          const { session: latestSession } = await getPuzzleAgentSession(
            item.sourceSessionId,
          );
          const payload = buildPuzzleFormPayloadFromSession(latestSession);
          const startedAtMs = resolveMiniGameDraftGenerationStartedAtMs(
            latestSession.updatedAt,
          );
          const baseGenerationState =
            createMiniGameDraftGenerationStateForRestoredDraft(
              'puzzle',
              {
                puzzleAiRedraw: payload.aiRedraw ?? true,
                puzzleProgressPercent:
                  latestSession.draft && !latestSession.draft.formDraft
                    ? latestSession.progressPercent
                    : undefined,
              },
              startedAtMs,
            );
          const generationState = mergePuzzleSessionProgressIntoGenerationState(
            baseGenerationState,
            latestSession,
          );
          puzzleFlow.setSession(latestSession);
          setPuzzleFormDraftPayload(payload);
          setPuzzleGenerationState(generationState);
          setPuzzleBackgroundCompileTasks((current) => ({
            ...current,
            [latestSession.sessionId]: {
              session: latestSession,
              payload,
              generationState,
              error: null,
            },
          }));
          enterCreateTab();
          selectionStageRef.current = 'puzzle-generating';
          activePuzzleGenerationSessionIdRef.current = item.sourceSessionId;
          setSelectionStage('puzzle-generating');
          return;
        } catch (error) {
          setPuzzleError(
            resolvePuzzleErrorMessage(error, '读取拼图创作草稿失败。'),
          );
          await refreshPuzzleShelf().catch(() => undefined);
          return;
        }
      }

      markDraftNoticeSeen(noticeKeys);

      const restoredSession = await puzzleFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshPuzzleShelf().catch(() => undefined);
        return;
      }

      if (isPuzzleFormOnlyDraft(restoredSession)) {
        setPuzzleFormDraftPayload(
          buildPuzzleFormPayloadFromSession(restoredSession),
        );
        setSelectionStage('puzzle-agent-workspace');
      } else {
        setPuzzleFormDraftPayload(null);
      }
    },
    [
      enterCreateTab,
      getPuzzleBackgroundCompileTask,
      getDraftGenerationNotice,
      isDraftNoticeFailed,
      isDraftNoticeGenerating,
      markDraftNoticeSeen,
      openPuzzleDetail,
      puzzleFlow,
      puzzleGenerationViewState,
      puzzleSession?.sessionId,
      refreshPuzzleShelf,
      resolvePuzzleErrorMessage,
      setPuzzleError,
      setSelectionStage,
    ],
  );

  const openMatch3DDraft = useCallback(
    async (
      item: Match3DWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      const noticeKeys = collectDraftNoticeKeys('match3d', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
      ]);
      const hasUnreadReadyNotice = isDraftNoticeReadyUnread('match3d', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
      ]);
      setMatch3DRun(null);
      setMatch3DError(null);
      setMatch3DProfile(null);
      setMatch3DRuntimeProfile(null);

      if (item.publicationStatus === 'published' && !options.forceDraft) {
        markDraftNoticeSeen(noticeKeys);
        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(item));
        return;
      }

      if (!item.sourceSessionId?.trim()) {
        markDraftNoticeSeen(noticeKeys);
        setMatch3DError('这份抓大鹅草稿缺少会话信息，请重新开始创作。');
        return;
      }

      const failedNotice = getDraftGenerationNotice(noticeKeys);
      const hasFailedNotice = isDraftNoticeFailed('match3d', [
        item.workId,
        item.profileId,
        item.sourceSessionId,
      ]);
      const noticeErrorMessage =
        failedNotice?.status === 'failed'
          ? (failedNotice.message ?? buildDraftFailedShelfSummary('match3d'))
          : buildDraftFailedShelfSummary('match3d');
      const isMarkedGenerating =
        !hasFailedNotice &&
        (isDraftNoticeGenerating('match3d', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]) ||
          isPersistedDraftGenerating(item.generationStatus));

      const backgroundTask = getMatch3DBackgroundCompileTask(
        item.sourceSessionId,
      );
      const activeGenerationState =
        backgroundTask?.generationState ?? match3dGenerationViewState;
      const failedGenerationState =
        backgroundTask?.generationState.phase === 'failed'
          ? backgroundTask.generationState
          : item.sourceSessionId === match3dSession?.sessionId &&
              activeGenerationState?.phase === 'failed'
            ? activeGenerationState
            : hasFailedNotice
              ? createFailedMiniGameDraftGenerationStateForRestoredDraft(
                  'match3d',
                  item.updatedAt,
                  noticeErrorMessage,
                )
              : null;

      if (hasUnreadReadyNotice) {
        try {
          const { session: latestSession } =
            await match3dCreationClient.getSession(item.sourceSessionId);
          setMatch3DSession(latestSession);
          setMatch3DFormDraftPayload(null);
          const profileId = latestSession.draft?.profileId ?? item.profileId;
          const { item: profile } = await getMatch3DWorkDetail(profileId);
          const normalizedProfile = normalizeMatch3DWorkForRuntimeUi(profile);
          match3dFlow.setIsBusy(false);
          const started = await startMatch3DRunFromProfile(
            normalizedProfile,
            'match3d-result',
          );
          if (!started) {
            setMatch3DProfile(normalizedProfile);
            enterCreateTab();
            setSelectionStage('match3d-result');
          }
          markDraftNoticeSeen(noticeKeys);
          return;
        } catch (error) {
          setMatch3DError(
            resolveMatch3DErrorMessage(error, '启动抓大鹅试玩失败。'),
          );
          markDraftNoticeSeen(noticeKeys);
          await refreshMatch3DShelf().catch(() => undefined);
          return;
        }
      }

      if (failedGenerationState) {
        let failedSession = backgroundTask?.session ?? null;
        let failedPayload = backgroundTask?.payload ?? null;
        const failedError =
          backgroundTask?.error ?? failedNotice?.message ?? noticeErrorMessage;
        if (!failedSession) {
          try {
            const { session: latestSession } =
              await match3dCreationClient.getSession(item.sourceSessionId);
            failedSession = latestSession;
            failedPayload = buildMatch3DFormPayloadFromSession(latestSession);
          } catch {
            failedPayload = buildMatch3DFormPayloadFromWork(item);
          }
        }
        if (!failedPayload) {
          failedPayload = failedSession
            ? buildMatch3DFormPayloadFromSession(failedSession)
            : buildMatch3DFormPayloadFromWork(item);
        }
        if (backgroundTask) {
          setMatch3DSession(backgroundTask.session);
        } else if (failedSession) {
          setMatch3DSession(failedSession);
        }
        setMatch3DFormDraftPayload(failedPayload);
        setMatch3DError(failedError);
        if (failedSession) {
          setMatch3DBackgroundCompileTasks((current) => ({
            ...current,
            [failedSession!.sessionId]: {
              session: failedSession!,
              payload: failedPayload!,
              generationState: failedGenerationState,
              error: failedError,
            },
          }));
        }
        enterCreateTab();
        selectionStageRef.current = 'match3d-generating';
        activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
        setMatch3DGenerationState(failedGenerationState);
        setSelectionStage('match3d-generating');
        return;
      }

      if (
        item.sourceSessionId === match3dSession?.sessionId &&
        isMiniGameDraftGenerating(activeGenerationState)
      ) {
        if (!activeGenerationState) {
          return;
        }
        const rebasedGenerationState =
          rebaseMiniGameDraftGenerationStateForDisplay(activeGenerationState);
        enterCreateTab();
        selectionStageRef.current = 'match3d-generating';
        activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
        setMatch3DGenerationState(rebasedGenerationState);
        if (backgroundTask) {
          setMatch3DBackgroundCompileTasks((current) => ({
            ...current,
            [backgroundTask.session.sessionId]: {
              ...backgroundTask,
              generationState: rebasedGenerationState,
            },
          }));
        }
        setSelectionStage('match3d-generating');
        return;
      }

      if (
        backgroundTask &&
        isMiniGameDraftGenerating(backgroundTask.generationState)
      ) {
        const rebasedTask =
          rebaseMiniGameDraftBackgroundCompileTaskForDisplay(backgroundTask);
        setMatch3DSession(rebasedTask.session);
        setMatch3DFormDraftPayload(rebasedTask.payload);
        setMatch3DGenerationState(rebasedTask.generationState);
        setMatch3DBackgroundCompileTasks((current) => ({
          ...current,
          [rebasedTask.session.sessionId]: rebasedTask,
        }));
        if (backgroundTask.error) {
          setMatch3DError(backgroundTask.error);
        }
        enterCreateTab();
        selectionStageRef.current = 'match3d-generating';
        activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
        setSelectionStage('match3d-generating');
        return;
      }

      if (isMarkedGenerating) {
        try {
          const { session: latestSession } =
            await match3dCreationClient.getSession(item.sourceSessionId);
          setMatch3DSession(latestSession);
          setMatch3DFormDraftPayload(null);
          setMatch3DProfile(null);
          const generationState =
            createMiniGameDraftGenerationStateForRestoredDraft(
              'match3d',
              undefined,
              resolveMiniGameDraftGenerationStartedAtMs(
                latestSession.updatedAt,
              ),
            );
          setMatch3DGenerationState(generationState);
          enterCreateTab();
          selectionStageRef.current = 'match3d-generating';
          activeMatch3DGenerationSessionIdRef.current = item.sourceSessionId;
          setSelectionStage('match3d-generating');
          return;
        } catch (error) {
          setMatch3DError(
            resolveMatch3DErrorMessage(error, '读取抓大鹅创作草稿失败。'),
          );
          await refreshMatch3DShelf().catch(() => undefined);
          return;
        }
      }

      markDraftNoticeSeen(noticeKeys);

      const restoredSession = await match3dFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshMatch3DShelf().catch(() => undefined);
        return;
      }

      setMatch3DFormDraftPayload(null);

      try {
        const profileId = restoredSession.draft?.profileId ?? item.profileId;
        const { item: profile } = await getMatch3DWorkDetail(profileId);
        setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(profile));
      } catch (error) {
        setMatch3DProfile(buildMatch3DProfileFromSession(restoredSession));
        setMatch3DError(
          resolveMatch3DErrorMessage(error, '读取抓大鹅作品详情失败。'),
        );
      }
    },
    [
      enterCreateTab,
      getMatch3DBackgroundCompileTask,
      getDraftGenerationNotice,
      isDraftNoticeFailed,
      isDraftNoticeGenerating,
      isDraftNoticeReadyUnread,
      markDraftNoticeSeen,
      match3dFlow,
      match3dGenerationViewState,
      match3dSession?.sessionId,
      openPublicWorkDetail,
      refreshMatch3DShelf,
      resolveMatch3DErrorMessage,
      setMatch3DFormDraftPayload,
      setMatch3DError,
      setMatch3DSession,
      setSelectionStage,
      startMatch3DRunFromProfile,
    ],
  );

  const openSquareHoleDraft = useCallback(
    async (
      item: SquareHoleWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      setSquareHoleRun(null);
      setSquareHoleError(null);
      setSquareHoleProfile(null);
      markDraftNoticeSeen(
        collectDraftNoticeKeys('square-hole', [
          item.workId,
          item.profileId,
          item.sourceSessionId,
        ]),
      );

      if (item.publicationStatus === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(item));
        return;
      }

      if (!item.sourceSessionId?.trim()) {
        setSquareHoleError('这份方洞挑战草稿缺少会话信息，请重新开始创作。');
        return;
      }

      if (
        item.sourceSessionId === squareHoleSession?.sessionId &&
        isMiniGameDraftGenerating(squareHoleGenerationState)
      ) {
        enterCreateTab();
        selectionStageRef.current = 'square-hole-generating';
        setSelectionStage('square-hole-generating');
        return;
      }

      if (!isMiniGameDraftReady(squareHoleGenerationState)) {
        setSquareHoleGenerationState(null);
      }
      const restoredSession = await squareHoleFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshSquareHoleShelf().catch(() => undefined);
        return;
      }

      try {
        const profileId = restoredSession.draft?.profileId ?? item.profileId;
        const { item: profile } = await getSquareHoleWorkDetail(profileId);
        setSquareHoleProfile(profile);
      } catch (error) {
        setSquareHoleProfile(
          buildSquareHoleProfileFromSession(restoredSession),
        );
        setSquareHoleError(
          resolveSquareHoleErrorMessage(error, '读取方洞挑战作品详情失败。'),
        );
      }
    },
    [
      enterCreateTab,
      markDraftNoticeSeen,
      openPublicWorkDetail,
      refreshSquareHoleShelf,
      resolveSquareHoleErrorMessage,
      setSquareHoleError,
      setSelectionStage,
      squareHoleGenerationState,
      squareHoleFlow,
      squareHoleSession?.sessionId,
    ],
  );

  const openBigFishDraft = useCallback(
    async (item: BigFishWorkSummary) => {
      setBigFishRun(null);
      markDraftNoticeSeen(
        collectDraftNoticeKeys('big-fish', [item.workId, item.sourceSessionId]),
      );
      if (
        item.sourceSessionId === bigFishSession?.sessionId &&
        isMiniGameDraftGenerating(bigFishGenerationState)
      ) {
        enterCreateTab();
        selectionStageRef.current = 'big-fish-generating';
        setSelectionStage('big-fish-generating');
        return;
      }
      const restoredSession = await bigFishFlow.restoreDraft(
        item.sourceSessionId,
      );
      if (!restoredSession) {
        await refreshBigFishShelf().catch(() => undefined);
      }
    },
    [
      bigFishFlow,
      bigFishGenerationState,
      bigFishSession?.sessionId,
      enterCreateTab,
      markDraftNoticeSeen,
      refreshBigFishShelf,
      setSelectionStage,
    ],
  );

  const openBarkBattleDraft = useCallback(
    (item: BarkBattleWorkSummary, options: { forceDraft?: boolean } = {}) => {
      markDraftNoticeSeen(
        collectDraftNoticeKeys('bark-battle', [item.workId, item.draftId]),
      );
      setBarkBattleError(null);
      setBarkBattleGenerationPartialFailed(false);
      setBarkBattlePublishedConfig(null);

      if (item.status === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapBarkBattleWorkToPublicWorkDetail(item));
        return;
      }

      const nextDraft: BarkBattleDraftConfig = {
        draftId: item.draftId ?? item.workId,
        workId: item.workId,
        title: item.title,
        description: item.summary,
        themeDescription: item.themeDescription,
        playerImageDescription: item.playerImageDescription,
        opponentImageDescription: item.opponentImageDescription,
        onomatopoeia: item.onomatopoeia,
        playerCharacterImageSrc: item.playerCharacterImageSrc ?? undefined,
        opponentCharacterImageSrc: item.opponentCharacterImageSrc ?? undefined,
        uiBackgroundImageSrc: item.uiBackgroundImageSrc ?? undefined,
        difficultyPreset: item.difficultyPreset,
        configVersion: 1,
        rulesetVersion: 'bark-battle-ruleset-v1',
        updatedAt: item.updatedAt,
      };
      setBarkBattleDraftConfig(nextDraft);
      enterCreateTab();
      selectionStageRef.current = isPersistedBarkBattleDraftGenerating(item)
        ? 'bark-battle-generating'
        : 'bark-battle-result';
      setSelectionStage(selectionStageRef.current);
      writeCreationUrlState(buildBarkBattleCreationUrlState(nextDraft));
    },
    [
      enterCreateTab,
      markDraftNoticeSeen,
      openPublicWorkDetail,
      setSelectionStage,
    ],
  );

  const openVisualNovelDraft = useCallback(
    async (
      item: VisualNovelWorkSummary,
      options: { forceDraft?: boolean } = {},
    ) => {
      if (item.publishStatus === 'published' && !options.forceDraft) {
        openPublicWorkDetail(mapVisualNovelWorkToPublicWorkDetail(item));
        return;
      }

      markDraftNoticeSeen(
        collectDraftNoticeKeys('visual-novel', [item.profileId]),
      );
      if (
        item.profileId === visualNovelSession?.sessionId &&
        visualNovelGenerationPhase === 'generating'
      ) {
        enterCreateTab();
        selectionStageRef.current = 'visual-novel-generating';
        setSelectionStage('visual-novel-generating');
        return;
      }
      if (
        item.profileId === visualNovelSession?.sessionId &&
        visualNovelSession.draft
      ) {
        enterCreateTab();
        setSelectionStage('visual-novel-result');
        return;
      }

      setVisualNovelWork(null);
      setVisualNovelRun(null);
      setVisualNovelRuntimeReturnStage('visual-novel-result');
      setVisualNovelError(null);
      setIsVisualNovelBusy(true);

      try {
        const { work } = await getVisualNovelWorkDetail(item.profileId);
        setVisualNovelWork(work);
        setVisualNovelSession(mapVisualNovelWorkDetailToSession(work));
        enterCreateTab();
        setSelectionStage('visual-novel-result');
      } catch (error) {
        setVisualNovelError(
          resolvePuzzleErrorMessage(error, '读取视觉小说作品详情失败。'),
        );
      } finally {
        setIsVisualNovelBusy(false);
      }
    },
    [
      enterCreateTab,
      openPublicWorkDetail,
      markDraftNoticeSeen,
      resolvePuzzleErrorMessage,
      setIsVisualNovelBusy,
      setSelectionStage,
      setVisualNovelError,
      setVisualNovelSession,
      visualNovelGenerationPhase,
      visualNovelSession,
    ],
  );

  const openBabyObjectMatchDraft = useCallback(
    (draft: BabyObjectMatchDraft) => {
      markDraftNoticeSeen(
        collectDraftNoticeKeys('baby-object-match', [
          draft.profileId,
          draft.draftId,
        ]),
      );
      setBabyObjectMatchDraft(draft);
      setBabyObjectMatchFormPayload({
        itemAName: draft.itemNames[0],
        itemBName: draft.itemNames[1],
      });
      setBabyObjectMatchGenerationState(null);
      setBabyObjectMatchGenerationPhase('ready');
      setBabyObjectMatchError(null);
      enterCreateTab();
      setSelectionStage('baby-object-match-result');
      writeCreationUrlState(buildBabyObjectMatchCreationUrlState(draft));
    },
    [enterCreateTab, markDraftNoticeSeen, setSelectionStage],
  );

  useEffect(() => {
    if (handledInitialCreationUrlStateRef.current) {
      return;
    }
    if (!isCreationRestorePath(window.location.pathname)) {
      handledInitialCreationUrlStateRef.current = true;
      return;
    }
    if (!hasCreationUrlStateValue(initialCreationUrlState)) {
      handledInitialCreationUrlStateRef.current = true;
      return;
    }
    if (platformBootstrap.isLoadingPlatform) {
      return;
    }
    if (!platformBootstrap.canReadProtectedData) {
      return;
    }

    handledInitialCreationUrlStateRef.current = true;

    const restoreCreationUrlState = async () => {
      const path = window.location.pathname;
      const sessionId = normalizeCreationUrlValue(
        initialCreationUrlState.sessionId,
      );
      const profileId = normalizeCreationUrlValue(
        initialCreationUrlState.profileId,
      );
      const draftId = normalizeCreationUrlValue(
        initialCreationUrlState.draftId,
      );
      const workId = normalizeCreationUrlValue(initialCreationUrlState.workId);

      if (path.startsWith('/creation/big-fish')) {
        const targetSessionId =
          sessionId ?? workId?.replace(/^big-fish-work-/u, '');
        if (targetSessionId) {
          const matchedWork =
            (bigFishWorks.length > 0
              ? bigFishWorks
              : (await listBigFishWorks().catch(() => ({ items: [] }))).items
            ).find(
              (item) =>
                item.sourceSessionId === targetSessionId ||
                item.workId === workId,
            ) ?? null;
          if (matchedWork) {
            await openBigFishDraft(matchedWork);
            return;
          }
          await bigFishFlow.restoreDraft(targetSessionId);
        }
        return;
      }

      if (path.startsWith('/creation/match3d')) {
        const matchedWork =
          (match3dWorks.length > 0
            ? match3dWorks
            : mapMatch3DWorksForRuntimeUi(
                (await listMatch3DWorks().catch(() => ({ items: [] }))).items,
              )
          ).find(
            (item) =>
              item.sourceSessionId === sessionId ||
              item.profileId === profileId ||
              item.workId === workId,
          ) ?? null;
        if (matchedWork) {
          await openMatch3DDraft(matchedWork, { forceDraft: true });
          return;
        }
        if (sessionId) {
          await match3dFlow.restoreDraft(sessionId);
        }
        return;
      }

      if (path.startsWith('/creation/square-hole')) {
        const matchedWork =
          (squareHoleWorks.length > 0
            ? squareHoleWorks
            : (await listSquareHoleWorks().catch(() => ({ items: [] }))).items
          ).find(
            (item) =>
              item.sourceSessionId === sessionId ||
              item.profileId === profileId ||
              item.workId === workId,
          ) ?? null;
        if (matchedWork) {
          await openSquareHoleDraft(matchedWork, { forceDraft: true });
          return;
        }
        if (sessionId) {
          await squareHoleFlow.restoreDraft(sessionId);
        }
        return;
      }

      if (
        path.startsWith('/creation/puzzle') &&
        !path.startsWith('/creation/puzzle-clear')
      ) {
        const matchedWork =
          (puzzleWorks.length > 0
            ? puzzleWorks
            : (await listPuzzleWorks().catch(() => ({ items: [] }))).items
          ).find(
            (item) =>
              item.sourceSessionId === sessionId ||
              item.profileId === profileId ||
              item.workId === workId,
          ) ?? null;
        if (matchedWork) {
          await openPuzzleDraft(matchedWork);
          return;
        }
        if (sessionId) {
          await puzzleFlow.restoreDraft(sessionId);
        }
        return;
      }

      if (path.startsWith('/creation/puzzle-clear')) {
        const matchedWork =
          (
            puzzleClearWorks.length > 0
              ? puzzleClearWorks
              : (await puzzleClearClient
                  .listWorks()
                  .catch(() => ({ items: [] }))).items
          ).find(
            (item) =>
              item.sourceSessionId === sessionId ||
              item.profileId === profileId ||
              item.workId === workId,
          ) ?? null;
        if (matchedWork) {
          await openPuzzleClearDraft(matchedWork);
          return;
        }
        if (sessionId) {
          const { session } = await puzzleClearClient.getSession(sessionId);
          setPuzzleClearSession(session);
          setPuzzleClearWork(null);
          enterCreateTab();
          setSelectionStage(
            path.includes('/generating')
              ? 'puzzle-clear-generating'
              : session.draft
                ? 'puzzle-clear-result'
                : 'puzzle-clear-workspace',
          );
        }
        return;
      }

      if (path.startsWith('/creation/visual-novel')) {
        const matchedWork =
          (visualNovelWorks.length > 0
            ? visualNovelWorks
            : (await listVisualNovelWorks().catch(() => ({ works: [] }))).works
          ).find((item) => item.profileId === profileId) ?? null;
        if (matchedWork) {
          await openVisualNovelDraft(matchedWork, { forceDraft: true });
          return;
        }
        if (sessionId) {
          await visualNovelFlow.restoreDraft(sessionId);
        }
        return;
      }

      if (path.startsWith('/creation/bark-battle')) {
        const matchedWork =
          (barkBattleWorks.length > 0
            ? barkBattleWorks
            : (await listBarkBattleWorks().catch(() => ({ items: [] }))).items
          ).find(
            (item) => item.workId === workId || item.draftId === draftId,
          ) ?? null;
        if (matchedWork) {
          openBarkBattleDraft(matchedWork, { forceDraft: true });
        }
        return;
      }

      if (path.startsWith('/creation/baby-object-match')) {
        const matchedDraft =
          (babyObjectMatchDrafts.length > 0
            ? babyObjectMatchDrafts
            : await listLocalBabyObjectMatchDrafts().catch(() => [])
          ).find(
            (item) =>
              item.profileId === profileId ||
              item.draftId === draftId ||
              item.profileId === workId,
          ) ?? null;
        if (matchedDraft) {
          openBabyObjectMatchDraft(matchedDraft);
        }
        return;
      }

      if (path.startsWith('/creation/jump-hop')) {
        let session: JumpHopSessionSnapshotResponse | null = null;
        let work: JumpHopWorkProfileResponse | null = null;
        try {
          if (profileId) {
            work = (
              await jumpHopClient.getWorkDetail(profileId, {
                audience: 'creation',
              })
            ).item;
          }
        } catch {
          work = null;
        }
        try {
          if (sessionId) {
            session = (await jumpHopClient.getSession(sessionId)).session;
          }
        } catch {
          session = null;
        }
        if (!session && !work) {
          return;
        }
        try {
          setJumpHopSession(session);
          setJumpHopWork(work);
          writeCreationUrlState(
            buildJumpHopCreationUrlState({ session, work }),
          );
          enterCreateTab();
          setSelectionStage(
            path.includes('/generating')
              ? 'jump-hop-generating'
              : session?.draft || work
                ? 'jump-hop-result'
                : 'jump-hop-workspace',
          );
        } catch (error) {
          setJumpHopError(
            resolveRpgCreationErrorMessage(error, '读取跳一跳创作草稿失败。'),
          );
        }
        return;
      }

      if (path.startsWith('/creation/wooden-fish')) {
        if (!sessionId) {
          return;
        }
        try {
          const { session } = await woodenFishClient.getSession(sessionId);
          let work: WoodenFishWorkProfileResponse | null = null;
          if (profileId) {
            work = (await woodenFishClient.getWorkDetail(profileId)).item;
          }
          setWoodenFishSession(session);
          setWoodenFishWork(work);
          writeCreationUrlState(
            buildWoodenFishCreationUrlState({ session, work }),
          );
          enterCreateTab();
          setSelectionStage(
            path.includes('/generating')
              ? 'wooden-fish-generating'
              : session.draft
                ? 'wooden-fish-result'
                : 'wooden-fish-workspace',
          );
        } catch (error) {
          setWoodenFishError(
            resolveRpgCreationErrorMessage(error, '读取敲木鱼创作草稿失败。'),
          );
        }
      }
    };

    void restoreCreationUrlState();
  }, [
    babyObjectMatchDrafts,
    barkBattleWorks,
    bigFishFlow,
    bigFishWorks,
    enterCreateTab,
    initialCreationUrlState,
    jumpHopClient,
    match3dFlow,
    match3dWorks,
    openBabyObjectMatchDraft,
    openBarkBattleDraft,
    openBigFishDraft,
    openMatch3DDraft,
    openPuzzleDraft,
    openPuzzleClearDraft,
    openSquareHoleDraft,
    openVisualNovelDraft,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.isLoadingPlatform,
    puzzleFlow,
    puzzleClearWorks,
    puzzleWorks,
    setSelectionStage,
    squareHoleFlow,
    squareHoleWorks,
    visualNovelFlow,
    visualNovelWorks,
    woodenFishClient,
  ]);

  const startBigFishRunFromWork = useCallback(
    async (
      item: BigFishWorkSummary,
      returnStage: BigFishRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      const sessionId = item.sourceSessionId?.trim();
      if (!sessionId) {
        setBigFishError('当前作品缺少会话信息，暂时无法进入玩法。');
        return false;
      }

      const publicWorkCode = buildBigFishPublicWorkCode(item.sourceSessionId);
      setBigFishError(null);
      bigFishFlow.setSession(null);
      setBigFishRuntimeWork(item);
      setBigFishRuntimeShare({
        title: item.title,
        publicWorkCode,
      });
      setBigFishRuntimeStartedAt(null);
      setBigFishRuntimeSessionSource('work');
      setBigFishRuntimeReturnStage(returnStage);
      setBigFishRun(null);
      try {
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const { run } = options.embedded
          ? await startBigFishRuntimeRun(sessionId, runtimeGuestOptions)
          : await startBigFishRuntimeRun(sessionId);
        setBigFishRuntimeStartedAt(Date.now());
        setBigFishRun(run);
        if (!options.embedded) {
          setSelectionStage('big-fish-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath('big-fish-runtime', publicWorkCode),
          );
        }
        const recordPlayPromise = options.embedded
          ? recordBigFishPlay(sessionId, { elapsedMs: 0 }, runtimeGuestOptions)
          : recordBigFishPlay(sessionId, { elapsedMs: 0 });
        void recordPlayPromise.catch((error) => {
          setBigFishError(
            resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'),
          );
        });
        return true;
      } catch (error) {
        setBigFishError(
          resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'),
        );
        return false;
      }
    },
    [
      authUi,
      bigFishFlow,
      resolveBigFishErrorMessage,
      setBigFishError,
      setSelectionStage,
    ],
  );

  const startBarkBattleRunFromWork = useCallback(
    async (
      item: BarkBattleWorkSummary,
      returnStage: BarkBattleRuntimeReturnStage = 'work-detail',
      options: { embedded?: boolean } = {},
    ) => {
      if (item.status !== 'published') {
        setBarkBattleError('汪汪声浪作品发布后才能进入正式玩法。');
        return false;
      }

      setBarkBattleError(null);
      setBarkBattleGenerationPartialFailed(false);
      setBarkBattleRuntimeMode('published');
      setBarkBattlePublishedConfig(mapBarkBattleWorkToPublishedConfig(item));
      setBarkBattleRuntimeReturnStage(returnStage);
      try {
        const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
          authUi,
          options.embedded,
        );
        const runResponse = options.embedded
          ? await startBarkBattleRun(item.workId, {}, runtimeGuestOptions)
          : await startBarkBattleRun(item.workId);
        void runResponse;
        selectionStageRef.current = 'bark-battle-runtime';
        if (!options.embedded) {
          setSelectionStage('bark-battle-runtime');
          pushAppHistoryPath(
            buildPublicWorkStagePath(
              'bark-battle-runtime',
              buildBarkBattlePublicWorkCode(item.workId),
            ),
          );
        }
        return true;
      } catch (error) {
        setBarkBattleError(
          resolveBarkBattleErrorMessage(error, '启动汪汪声浪玩法失败。'),
        );
        return false;
      }
    },
    [authUi, resolveBarkBattleErrorMessage, setSelectionStage],
  );

  const startSelectedPublicWork = useCallback(() => {
    if (!selectedPublicWorkDetail || isPublicWorkDetailBusy) {
      return;
    }

    if (isJumpHopGalleryEntry(selectedPublicWorkDetail)) {
      setPublicWorkDetailError(null);
      void startJumpHopRunFromProfile(selectedPublicWorkDetail.profileId, {
        returnStage: 'work-detail',
      });
      return;
    }

    runProtectedAction(() => {
      if (isBigFishGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToBigFishWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError('当前作品缺少会话信息，暂时无法进入玩法。');
          return;
        }
        startBigFishRunFromWork(work);
        return;
      }

      if (isPuzzleGalleryEntry(selectedPublicWorkDetail)) {
        const work =
          selectedPuzzleDetail?.profileId === selectedPublicWorkDetail.profileId
            ? selectedPuzzleDetail
            : mapPublicWorkDetailToPuzzleWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError(
            '当前拼图作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startPuzzleRunFromProfile(
          work.profileId,
          'work-detail',
          work,
          true,
          null,
          { authMode: 'isolated' },
        );
        return;
      }

      if (isPuzzleClearGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError(null);
        void startPuzzleClearRunFromProfile(selectedPublicWorkDetail.profileId, {
          returnStage: 'work-detail',
        });
        return;
      }

      if (isWoodenFishGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError(null);
        void startWoodenFishRunFromProfile(selectedPublicWorkDetail.profileId, {
          returnStage: 'work-detail',
        });
        return;
      }

      if (isMatch3DGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToMatch3DWork(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError(
            '当前抓大鹅作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startMatch3DRunFromProfile(work, 'work-detail', true);
        return;
      }

      if (isSquareHoleGalleryEntry(selectedPublicWorkDetail)) {
        const work = mapPublicWorkDetailToSquareHoleWork(
          selectedPublicWorkDetail,
        );
        if (!work) {
          setPublicWorkDetailError(
            '当前方洞挑战作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        void startSquareHoleRunFromProfile(work, 'work-detail', true);
        return;
      }

      if (isVisualNovelGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError(null);
        void startVisualNovelRunFromProfile(
          selectedPublicWorkDetail.profileId,
          'work-detail',
        );
        return;
      }

      if (isBarkBattleGalleryEntry(selectedPublicWorkDetail)) {
        const work =
          barkBattleGalleryEntries.find(
            (item) => item.workId === selectedPublicWorkDetail.workId,
          ) ??
          barkBattleWorks.find(
            (item) => item.workId === selectedPublicWorkDetail.workId,
          ) ??
          mapBarkBattlePublicDetailToWorkSummary(selectedPublicWorkDetail);
        if (!work) {
          setPublicWorkDetailError(
            '当前汪汪声浪作品信息不完整，暂时无法进入玩法。',
          );
          return;
        }
        setPublicWorkDetailError(null);
        startBarkBattleRunFromWork(work, 'work-detail');
        return;
      }

      if (isEdutainmentGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError(null);
        void startBabyObjectMatchRuntimeFromEntry(
          selectedPublicWorkDetail,
          'work-detail',
        );
        return;
      }

      if (!isCustomWorldGalleryEntry(selectedPublicWorkDetail)) {
        setPublicWorkDetailError('未知公开作品类型，无法进入玩法。');
        return;
      }

      const launchEntry =
        selectedDetailEntry?.profileId === selectedPublicWorkDetail.profileId
          ? selectedDetailEntry
          : null;
      if (!launchEntry) {
        setPublicWorkDetailError('作品详情尚未读取完成。');
        return;
      }

      setIsPublicWorkDetailBusy(true);
      void recordRpgEntryWorldGalleryPlay(
        launchEntry.ownerUserId,
        launchEntry.profileId,
      )
        .then((updatedEntry) => {
          setSelectedDetailEntry(updatedEntry);
          setSelectedPublicWorkDetail(
            mapRpgGalleryCardToPublicWorkDetail(updatedEntry),
          );
          handleCustomWorldSelect(updatedEntry.profile);
        })
        .catch((error) => {
          setPublicWorkDetailError(
            resolveRpgCreationErrorMessage(error, '记录作品游玩失败。'),
          );
        })
        .finally(() => {
          setIsPublicWorkDetailBusy(false);
        });
    });
  }, [
    barkBattleGalleryEntries,
    barkBattleWorks,
    handleCustomWorldSelect,
    isPublicWorkDetailBusy,
    runProtectedAction,
    selectedDetailEntry,
    selectedPuzzleDetail,
    selectedPublicWorkDetail,
    startBarkBattleRunFromWork,
    startBigFishRunFromWork,
    startJumpHopRunFromProfile,
    startPuzzleClearRunFromProfile,
    startWoodenFishRunFromProfile,
    startPuzzleRunFromProfile,
    startMatch3DRunFromProfile,
    startSquareHoleRunFromProfile,
    startBabyObjectMatchRuntimeFromEntry,
    startVisualNovelRunFromProfile,
  ]);

  const selectRecommendRuntimeEntry = useCallback(
    async (entry: PlatformPublicGalleryCard) => {
      const entryKey = getPlatformPublicGalleryEntryKey(entry);
      const runtimeKind = getPlatformRecommendRuntimeKind(entry);
      const startRequestId = recommendRuntimeStartRequestRef.current + 1;
      recommendRuntimeStartRequestRef.current = startRequestId;
      const isCurrentStartRequest = () =>
        recommendRuntimeStartRequestRef.current === startRequestId;
      setActiveRecommendEntryKey(entryKey);
      setActiveRecommendRuntimeKind(runtimeKind);
      setActiveRecommendRuntimeError(null);
      setIsStartingRecommendEntry(true);
      if (entryKey !== activeRecommendEntryKey) {
        await saveAndExitRecommendPuzzleRuntime();
        if (!isCurrentStartRequest()) {
          return;
        }
      }
      setActiveRecommendEntryKey(entryKey);
      setActiveRecommendRuntimeKind(runtimeKind);
      setActiveRecommendRuntimeError(null);
      setIsStartingRecommendEntry(true);

      try {
        let started = false;
        if (isBigFishGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToBigFishWork(entry);
          if (!work) {
            setBigFishError('当前作品缺少会话信息，暂时无法进入玩法。');
          } else {
            started = await startBigFishRunFromWork(work, 'platform', {
              embedded: true,
            });
          }
        } else if (isPuzzleGalleryEntry(entry)) {
          const work =
            selectedPuzzleDetail?.profileId === entry.profileId
              ? selectedPuzzleDetail
              : mapPublicWorkDetailToPuzzleWork(entry);
          if (!work) {
            setPuzzleError('当前拼图作品信息不完整，暂时无法进入玩法。');
          } else {
            started = await startPuzzleRunFromProfile(
              work.profileId,
              'platform',
              work,
              false,
              null,
              { embedded: true },
            );
          }
        } else if (isPuzzleClearGalleryEntry(entry)) {
          started = await startPuzzleClearRunFromProfile(entry.profileId, {
            embedded: true,
            returnStage: 'platform',
          });
        } else if (isJumpHopGalleryEntry(entry)) {
          started = await startJumpHopRunFromProfile(entry.profileId, {
            embedded: true,
            returnStage: 'platform',
          });
        } else if (isWoodenFishGalleryEntry(entry)) {
          started = await startWoodenFishRunFromProfile(entry.profileId, {
            embedded: true,
            returnStage: 'platform',
          });
        } else if (isMatch3DGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToMatch3DWork(entry);
          if (!work) {
            setMatch3DError('当前抓大鹅作品信息不完整，暂时无法进入玩法。');
          } else {
            started = await startMatch3DRunFromProfile(
              work,
              'work-detail',
              false,
              { embedded: true },
            );
          }
        } else if (isSquareHoleGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToSquareHoleWork(entry);
          if (!work) {
            setSquareHoleError(
              '当前方洞挑战作品信息不完整，暂时无法进入玩法。',
            );
          } else {
            started = await startSquareHoleRunFromProfile(
              work,
              'platform',
              false,
              { embedded: true },
            );
          }
        } else if (isVisualNovelGalleryEntry(entry)) {
          started = await startVisualNovelRunFromProfile(
            entry.profileId,
            'platform',
            { embedded: true },
          );
        } else if (isBarkBattleGalleryEntry(entry)) {
          const work =
            barkBattleGalleryEntries.find(
              (item) => item.workId === entry.workId,
            ) ?? mapBarkBattlePublicDetailToWorkSummary(entry);
          if (!work) {
            setBarkBattleError(
              '当前汪汪声浪作品信息不完整，暂时无法进入玩法。',
            );
          } else {
            started = await startBarkBattleRunFromWork(work, 'platform', {
              embedded: true,
            });
          }
        } else if (isEdutainmentGalleryEntry(entry)) {
          started = await startBabyObjectMatchRuntimeFromEntry(
            entry,
            'platform',
            {
              embedded: true,
            },
          );
        } else if (isCustomWorldGalleryEntry(entry)) {
          started = true;
        } else {
          throw new Error('未知公开作品类型，无法启动推荐玩法。');
        }

        if (!isCurrentStartRequest()) {
          return;
        }

        if (started) {
          setActiveRecommendRuntimeKind(runtimeKind);
          setActiveRecommendRuntimeError(null);
        } else {
          setActiveRecommendRuntimeKind(null);
          setActiveRecommendRuntimeError('作品暂时无法进入，请稍后再试。');
        }
      } catch (error) {
        if (!isCurrentStartRequest()) {
          return;
        }

        setActiveRecommendRuntimeKind(null);
        setActiveRecommendRuntimeError('作品暂时无法进入，请稍后再试。');
      } finally {
        if (isCurrentStartRequest()) {
          setIsStartingRecommendEntry(false);
        }
      }
    },
    [
      activeRecommendEntryKey,
      barkBattleGalleryEntries,
      saveAndExitRecommendPuzzleRuntime,
      selectedPuzzleDetail,
      setBarkBattleError,
      setBigFishError,
      setMatch3DError,
      setPuzzleError,
      setPuzzleClearError,
      setSquareHoleError,
      startBarkBattleRunFromWork,
      startBigFishRunFromWork,
      startJumpHopRunFromProfile,
      startPuzzleClearRunFromProfile,
      startWoodenFishRunFromProfile,
      startMatch3DRunFromProfile,
      startPuzzleRunFromProfile,
      startSquareHoleRunFromProfile,
      startBabyObjectMatchRuntimeFromEntry,
      startVisualNovelRunFromProfile,
    ],
  );
  const selectAdjacentRecommendRuntimeEntry = useCallback(
    (direction: 1 | -1, baseEntryKey?: string | null) => {
      if (recommendRuntimeEntries.length === 0) {
        return;
      }

      const normalizedBaseEntryKey =
        baseEntryKey?.trim() || activeRecommendEntryKey;
      const activeIndex = recommendRuntimeEntries.findIndex(
        (entry) =>
          getPlatformPublicGalleryEntryKey(entry) === normalizedBaseEntryKey,
      );
      const baseIndex = activeIndex >= 0 ? activeIndex : 0;
      const nextIndex =
        (baseIndex + direction + recommendRuntimeEntries.length) %
        recommendRuntimeEntries.length;
      const nextEntry = recommendRuntimeEntries[nextIndex];
      if (!nextEntry) {
        return;
      }
      if (
        getPlatformPublicGalleryEntryKey(nextEntry) === normalizedBaseEntryKey
      ) {
        return;
      }

      void selectRecommendRuntimeEntry(nextEntry);
    },
    [
      activeRecommendEntryKey,
      recommendRuntimeEntries,
      selectRecommendRuntimeEntry,
    ],
  );

  const recommendRuntimeContent = useMemo(() => {
    if (
      isDesktopLayout ||
      selectionStage !== 'platform' ||
      platformBootstrap.platformTab !== 'home' ||
      !activeRecommendRuntimeKind
    ) {
      return null;
    }

    const activeEntry =
      recommendRuntimeEntries.find(
        (entry) =>
          getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
      ) ?? null;
    if (!activeEntry) {
      return null;
    }

    if (activeRecommendRuntimeKind === 'big-fish') {
      return (
        <BigFishRuntimeShell
          run={bigFishRun}
          assetSlots={bigFishSession?.assetSlots ?? []}
          shareTitle={bigFishRuntimeShare?.title ?? null}
          sharePublicWorkCode={bigFishRuntimeShare?.publicWorkCode ?? null}
          isBusy={isBigFishBusy}
          error={bigFishError}
          embedded
          onBack={() => {
            reportBigFishObservedPlayTime();
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            reportBigFishObservedPlayTime();
            void restartBigFishRun();
          }}
          onSubmitInput={submitBigFishInput}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'match3d') {
      const activeMatch3DRuntimeProfile = resolveActiveMatch3DRuntimeProfile(
        match3dRun,
        match3dRuntimeProfile,
        match3dProfile,
      );
      return (
        <Match3DRuntimeShell
          run={match3dRun}
          isBusy={isMatch3DBusy}
          error={match3dError}
          embedded
          hideBackButton
          levelName={
            activeMatch3DRuntimeProfile?.gameName?.trim() ||
            activeEntry.worldName
          }
          generatedItemAssets={resolveMatch3DRuntimeGeneratedItemAssets(
            match3dRun,
            activeMatch3DRuntimeProfile,
            activeEntry,
          )}
          generatedBackgroundAsset={resolveMatch3DRuntimeGeneratedBackgroundAsset(
            match3dRun,
            activeMatch3DRuntimeProfile,
            activeEntry,
          )}
          backgroundImageSrc={resolveMatch3DRuntimeBackgroundImageSrc(
            match3dRun,
            activeMatch3DRuntimeProfile,
            activeEntry,
          )}
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            if (!match3dRun?.runId || isMatch3DBusy) {
              return;
            }

            match3dFlow.setIsBusy(true);
            setMatch3DError(null);
            void resolveMatch3DRuntimeAdapter(
              activeMatch3DRuntimeProfile?.profileId,
            )
              .restartRun(match3dRun.runId)
              .then(({ run }) => {
                setMatch3DRun(run);
              })
              .catch((error) => {
                setMatch3DError(
                  resolveMatch3DErrorMessage(error, '重新开始抓大鹅玩法失败。'),
                );
              })
              .finally(() => {
                match3dFlow.setIsBusy(false);
              });
          }}
          onOptimisticRunChange={setMatch3DRun}
          onClickItem={(payload) => {
            const runId = payload.runId ?? match3dRun?.runId;
            if (!runId) {
              return Promise.reject(new Error('抓大鹅运行态缺少 runId。'));
            }
            return resolveMatch3DRuntimeAdapter(
              activeMatch3DRuntimeProfile?.profileId,
            ).clickItem(runId, payload);
          }}
          onTimeExpired={() => {
            if (!match3dRun?.runId) {
              return;
            }

            void resolveMatch3DRuntimeAdapter(
              activeMatch3DRuntimeProfile?.profileId,
            )
              .finishTimeUp(match3dRun.runId)
              .then(({ run }) => {
                setMatch3DRun(run);
              })
              .catch((error) => {
                setMatch3DError(
                  resolveMatch3DErrorMessage(error, '同步抓大鹅倒计时失败。'),
                );
              });
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'puzzle') {
      return (
        <PuzzleRuntimeShell
          run={puzzleRun}
          isBusy={
            isPuzzleBusy ||
            isPuzzleNextLevelGenerating ||
            isPuzzleLeaderboardBusy
          }
          error={puzzleError}
          embedded
          hideBackButton
          hideExitControls
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRemodelWork={
            selectedPuzzleDetail?.publicationStatus === 'published'
              ? remodelCurrentPuzzleRuntimeWork
              : undefined
          }
          onSwapPieces={(payload) => {
            void swapPuzzlePiecesInRun(payload);
          }}
          onDragPiece={(payload) => {
            void dragPuzzlePiece(payload);
          }}
          onAdvanceNextLevel={() => {
            selectAdjacentRecommendRuntimeEntry(1, activeRecommendEntryKey);
          }}
          onRestartLevel={() => {
            void restartPuzzleCurrentLevel();
          }}
          onPauseChange={setPuzzleRuntimePaused}
          onUseProp={usePuzzleProp}
          onTimeExpired={syncPuzzleRuntimeTimeout}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'jump-hop') {
      return (
        <JumpHopRuntimeShell
          profile={jumpHopWork}
          run={jumpHopRun}
          isBusy={isJumpHopBusy}
          error={jumpHopError}
          runtimeRequestOptions={jumpHopRuntimeRequestOptions ?? undefined}
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            void restartJumpHopRuntimeRun();
          }}
          onJump={async (payload) => {
            await submitJumpHopJumpAction(payload);
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'puzzle-clear') {
      return (
        <PuzzleClearRuntimeShell
          profile={puzzleClearWork}
          run={puzzleClearRun}
          isBusy={isPuzzleClearBusy}
          error={puzzleClearError}
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onRetryLevel={() => {
            void retryPuzzleClearLevelRun();
          }}
          onNextLevel={() => {
            void advancePuzzleClearLevelRun();
          }}
          onTimeUp={() => {
            void markPuzzleClearLevelTimeUp();
          }}
          onSwapCards={async (payload) => {
            await swapPuzzleClearCardsInRun(payload);
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'wooden-fish') {
      return (
        <WoodenFishRuntimeShell
          profile={woodenFishWork}
          run={woodenFishRun}
          isBusy={isWoodenFishBusy}
          error={woodenFishError}
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onCheckpoint={checkpointWoodenFishRuntimeRun}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'square-hole') {
      return (
        <SquareHoleRuntimeShell
          run={squareHoleRun}
          isBusy={isSquareHoleBusy}
          error={squareHoleError}
          embedded
          onBack={() => {
            if (
              squareHoleRun?.runId &&
              squareHoleRun.status.toLowerCase() === 'running'
            ) {
              void stopSquareHoleRun(squareHoleRun.runId).catch(
                () => undefined,
              );
            }
            setActiveRecommendRuntimeKind(null);
          }}
          onRestart={() => {
            if (!squareHoleRun?.runId || isSquareHoleBusy) {
              return;
            }

            squareHoleFlow.setIsBusy(true);
            setSquareHoleError(null);
            void restartSquareHoleRun(squareHoleRun.runId)
              .then(({ run }) => {
                setSquareHoleRun(run);
              })
              .catch((error) => {
                setSquareHoleError(
                  resolveSquareHoleErrorMessage(
                    error,
                    '重新开始方洞挑战失败。',
                  ),
                );
              })
              .finally(() => {
                squareHoleFlow.setIsBusy(false);
              });
          }}
          onOptimisticRunChange={setSquareHoleRun}
          onDropShape={(payload) => {
            const runId = payload.runId ?? squareHoleRun?.runId;
            if (!runId) {
              return Promise.reject(new Error('方洞挑战运行态缺少 runId。'));
            }
            return dropSquareHoleShape(runId, payload);
          }}
          onTimeExpired={() => {
            if (!squareHoleRun?.runId) {
              return;
            }

            void finishSquareHoleTimeUp(squareHoleRun.runId)
              .then(({ run }) => {
                setSquareHoleRun(run);
              })
              .catch((error) => {
                setSquareHoleError(
                  resolveSquareHoleErrorMessage(
                    error,
                    '同步方洞挑战倒计时失败。',
                  ),
                );
              });
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'visual-novel') {
      return (
        <VisualNovelRuntimeShell
          draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
          run={visualNovelRun}
          isBusy={isVisualNovelBusy}
          error={visualNovelError}
          embedded
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
          onSubmitAction={(payload) => {
            void submitVisualNovelRuntimeAction(payload);
          }}
        />
      );
    }

    if (
      activeRecommendRuntimeKind === 'bark-battle' &&
      barkBattlePublishedConfig
    ) {
      return (
        <BarkBattleRuntimeShell
          title={barkBattlePublishedConfig.title}
          workId={barkBattlePublishedConfig.workId}
          publishedConfig={barkBattlePublishedConfig}
          runtimeMode="published"
          onExit={() => {
            setActiveRecommendRuntimeKind(null);
          }}
        />
      );
    }

    if (activeRecommendRuntimeKind === 'edutainment' && babyObjectMatchDraft) {
      return (
        <BabyObjectMatchRuntimeShell
          draft={babyObjectMatchDraft}
          embedded
          onBack={() => {
            setActiveRecommendRuntimeKind(null);
          }}
        />
      );
    }

    return (
      <div
        className={`platform-theme ${platformThemeClass} flex h-full min-h-0 items-center justify-center bg-[var(--platform-recommend-runtime-state-fill)] px-5 text-center text-sm font-semibold leading-6 text-[var(--platform-recommend-runtime-state-text)]`}
      >
        正在读取世界
      </div>
    );
  }, [
    activeRecommendEntryKey,
    activeRecommendRuntimeKind,
    barkBattlePublishedConfig,
    babyObjectMatchDraft,
    bigFishError,
    bigFishRun,
    bigFishRuntimeShare,
    bigFishSession?.assetSlots,
    isBigFishBusy,
    isJumpHopBusy,
    isMatch3DBusy,
    isPuzzleBusy,
    isPuzzleLeaderboardBusy,
    isPuzzleNextLevelGenerating,
    isSquareHoleBusy,
    isVisualNovelBusy,
    match3dError,
    match3dFlow,
    match3dProfile,
    match3dRun,
    match3dRuntimeProfile,
    match3dRuntimeAdapter,
    platformBootstrap.platformTab,
    jumpHopError,
    jumpHopRun,
    jumpHopWork,
    puzzleClearError,
    puzzleClearRun,
    puzzleClearWork,
    isPuzzleClearBusy,
    isWoodenFishBusy,
    woodenFishError,
    woodenFishRun,
    woodenFishWork,
    platformThemeClass,
    puzzleError,
    puzzleRun,
    recommendRuntimeEntries,
    remodelCurrentPuzzleRuntimeWork,
    resolveMatch3DErrorMessage,
    resolveSquareHoleErrorMessage,
    reportBigFishObservedPlayTime,
    restartBigFishRun,
    selectedPuzzleDetail,
    selectionStage,
    setMatch3DError,
    setMatch3DRun,
    setPuzzleRuntimePaused,
    setSquareHoleRun,
    squareHoleError,
    squareHoleFlow,
    squareHoleRun,
    submitBigFishInput,
    submitVisualNovelRuntimeAction,
    dragPuzzlePiece,
    restartPuzzleCurrentLevel,
    selectAdjacentRecommendRuntimeEntry,
    setSquareHoleError,
    swapPuzzlePiecesInRun,
    syncPuzzleRuntimeTimeout,
    usePuzzleProp,
    visualNovelError,
    visualNovelRun,
    visualNovelSession,
    visualNovelWork,
    checkpointWoodenFishRuntimeRun,
    advancePuzzleClearLevelRun,
    isDesktopLayout,
    markPuzzleClearLevelTimeUp,
    retryPuzzleClearLevelRun,
    swapPuzzleClearCardsInRun,
  ]);

  const activeRecommendEntry =
    activeRecommendEntryKey && !isDesktopLayout
      ? (recommendRuntimeEntries.find(
          (entry) =>
            getPlatformPublicGalleryEntryKey(entry) ===
            activeRecommendEntryKey,
        ) ?? null)
      : null;
  const isActiveRecommendRuntimeReady =
    activeRecommendEntry !== null &&
    isRecommendRuntimeReadyForEntry(activeRecommendEntry, {
      activeKind: activeRecommendRuntimeKind,
      barkBattlePublishedConfig,
      babyObjectMatchDraft,
      bigFishRun,
      jumpHopRun,
      match3dRun,
      puzzleRun,
      puzzleClearRun,
      squareHoleRun,
      visualNovelRun,
      woodenFishRun,
    });

  useEffect(() => {
    if (
      isDesktopLayout ||
      selectionStage !== 'platform' ||
      platformBootstrap.platformTab !== 'home' ||
      platformBootstrap.isLoadingPlatform
    ) {
      return;
    }

    if (recommendRuntimeEntries.length === 0) {
      setActiveRecommendEntryKey(null);
      setActiveRecommendRuntimeKind(null);
      setActiveRecommendRuntimeError(null);
      return;
    }

    if (
      (activeRecommendEntry !== null && isActiveRecommendRuntimeReady) ||
      isStartingRecommendEntry
    ) {
      return;
    }

    const nextRecommendEntry =
      activeRecommendEntry ?? recommendRuntimeEntries[0];
    if (nextRecommendEntry) {
      void selectRecommendRuntimeEntry(nextRecommendEntry);
    }
  }, [
    activeRecommendEntryKey,
    activeRecommendRuntimeKind,
    activeRecommendEntry,
    barkBattlePublishedConfig,
    babyObjectMatchDraft,
    bigFishRun,
    jumpHopRun,
    isActiveRecommendRuntimeReady,
    isStartingRecommendEntry,
    match3dRun,
    platformBootstrap.isLoadingPlatform,
    platformBootstrap.platformTab,
    isDesktopLayout,
    puzzleRun,
    puzzleClearRun,
    recommendRuntimeEntries,
    selectRecommendRuntimeEntry,
    selectionStage,
    squareHoleRun,
    visualNovelRun,
    woodenFishRun,
  ]);

  const remixPublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(() => {
        setIsPublicWorkDetailBusy(true);
        setPublicWorkDetailError(null);

        if (isBigFishGalleryEntry(entry)) {
          void remixBigFishGalleryWork(entry.profileId)
            .then((response) => {
              bigFishFlow.setSession(response.session);
              enterCreateTab();
              setSelectionStage('big-fish-result');
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveBigFishErrorMessage(error, 'Remix 大鱼吃小鱼作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          void remixPuzzleGalleryWork(entry.profileId)
            .then((response) => {
              resetRecommendRuntimeSelection();
              puzzleFlow.setSession(response.session);
              setPuzzleOperation(null);
              enterCreateTab();
              setSelectionStage('puzzle-result');
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolvePuzzleErrorMessage(error, 'Remix 拼图作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        if (
          isPuzzleClearGalleryEntry(entry) ||
          isMatch3DGalleryEntry(entry) ||
          isSquareHoleGalleryEntry(entry) ||
          isJumpHopGalleryEntry(entry) ||
          isWoodenFishGalleryEntry(entry) ||
          isVisualNovelGalleryEntry(entry) ||
          isEdutainmentGalleryEntry(entry) ||
          isBarkBattleGalleryEntry(entry)
        ) {
          setPublicWorkDetailError(
            resolveUnsupportedPublicWorkActionMessage(entry, '改造'),
          );
          setIsPublicWorkDetailBusy(false);
          return;
        }

        if (isCustomWorldGalleryEntry(entry)) {
          void remixRpgEntryWorldGallery(entry.ownerUserId, entry.profileId)
            .then((response) => {
              const nextEntry = response.entry;
              setSelectedDetailEntry(nextEntry);
              platformBootstrap.setSavedCustomWorldEntries([
                nextEntry,
                ...platformBootstrap.savedCustomWorldEntries.filter(
                  (entry) => entry.profileId !== nextEntry.profileId,
                ),
              ]);
              void detailNavigation.openSavedCustomWorldEditor(nextEntry);
            })
            .catch((error) => {
              setPublicWorkDetailError(
                resolveRpgCreationErrorMessage(error, 'Remix RPG 作品失败。'),
              );
            })
            .finally(() => {
              setIsPublicWorkDetailBusy(false);
            });
          return;
        }

        setPublicWorkDetailError('未知公开作品类型，无法改造。');
        setIsPublicWorkDetailBusy(false);
      });
    },
    [
      bigFishFlow,
      detailNavigation,
      enterCreateTab,
      isPublicWorkDetailBusy,
      platformBootstrap,
      puzzleFlow,
      resetRecommendRuntimeSelection,
      resolveBigFishErrorMessage,
      resolvePuzzleErrorMessage,
      runProtectedAction,
      setSelectionStage,
    ],
  );

  const editOwnedPublicWork = useCallback(
    (entry: PlatformPublicGalleryCard) => {
      if (isPublicWorkDetailBusy) {
        return;
      }

      runProtectedAction(async () => {
        setPublicWorkDetailError(null);

        // 中文注释：自有公开作品必须恢复原草稿，不能复用 remix 复制链路。
        if (isBigFishGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToBigFishWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份大鱼吃小鱼作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openBigFishDraft(work);
          return;
        }

        if (isPuzzleGalleryEntry(entry)) {
          const work =
            selectedPuzzleDetail?.profileId === entry.profileId
              ? selectedPuzzleDetail
              : mapPublicWorkDetailToPuzzleWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份拼图作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openPuzzleDraft(work);
          return;
        }

        if (isPuzzleClearGalleryEntry(entry)) {
          const matchedWork =
            puzzleClearWork?.summary.profileId === entry.profileId
              ? puzzleClearWork.summary
              : puzzleClearWorks.find((work) => work.profileId === entry.profileId) ??
                {
                  runtimeKind: 'puzzle-clear',
                  workId: entry.workId,
                  profileId: entry.profileId,
                  ownerUserId: entry.ownerUserId,
                  sourceSessionId: entry.sourceSessionId ?? null,
                  workTitle: entry.worldName,
                  workDescription: entry.summaryText,
                  themePrompt: entry.themePrompt,
                  coverImageSrc: entry.coverImageSrc,
                  publicationStatus: 'published',
                  playCount: entry.playCount ?? 0,
                  updatedAt: entry.updatedAt,
                  publishedAt: entry.publishedAt,
                  publishReady: true,
                  generationStatus: 'ready',
                };
          if (!matchedWork?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份拼消消作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openPuzzleClearDraft(matchedWork);
          return;
        }

        if (isMatch3DGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToMatch3DWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份抓大鹅作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openMatch3DDraft(work, { forceDraft: true });
          return;
        }

        if (isSquareHoleGalleryEntry(entry)) {
          const work = mapPublicWorkDetailToSquareHoleWork(entry);
          if (!work?.sourceSessionId?.trim()) {
            setPublicWorkDetailError(
              '这份方洞挑战作品缺少原草稿会话，暂时无法编辑。',
            );
            return;
          }
          void openSquareHoleDraft(work, { forceDraft: true });
          return;
        }

        if (isJumpHopGalleryEntry(entry)) {
          setPublicWorkDetailError('这份跳一跳作品暂时请从作品架编辑。');
          return;
        }

        if (isWoodenFishGalleryEntry(entry)) {
          setPublicWorkDetailError('这份敲木鱼作品暂时请从作品架编辑。');
          return;
        }

        if (isVisualNovelGalleryEntry(entry)) {
          const matchedWork = visualNovelWorks.find(
            (work) => work.profileId === entry.profileId,
          );
          if (!matchedWork) {
            setPublicWorkDetailError('这份视觉小说缺少可编辑草稿。');
            return;
          }
          void openVisualNovelDraft(matchedWork, { forceDraft: true });
          return;
        }

        if (isEdutainmentGalleryEntry(entry)) {
          const matchedDraft = await resolveBabyObjectMatchRuntimeDraft(entry);
          if (!matchedDraft) {
            setPublicWorkDetailError('这份宝贝识物缺少可编辑草稿。');
            return;
          }

          openBabyObjectMatchDraft(matchedDraft);
          return;
        }

        if (isBarkBattleGalleryEntry(entry)) {
          const matchedWork =
            barkBattleWorks.find((work) => work.workId === entry.workId) ??
            barkBattleGalleryEntries.find(
              (work) => work.workId === entry.workId,
            ) ??
            mapBarkBattlePublicDetailToWorkSummary(entry);
          if (!matchedWork?.draftId?.trim()) {
            setPublicWorkDetailError('这份汪汪声浪缺少可编辑草稿。');
            return;
          }

          openBarkBattleDraft(matchedWork, { forceDraft: true });
          return;
        }

        if (!isCustomWorldGalleryEntry(entry)) {
          setPublicWorkDetailError('未知公开作品类型，无法编辑。');
          return;
        }

        const editEntry =
          selectedDetailEntry?.profileId === entry.profileId
            ? selectedDetailEntry
            : null;
        if (!editEntry) {
          setPublicWorkDetailError('作品详情尚未读取完成。');
          return;
        }

        void detailNavigation.openSavedCustomWorldEditor(editEntry);
      });
    },
    [
      detailNavigation,
      isPublicWorkDetailBusy,
      openBigFishDraft,
      openMatch3DDraft,
      openPuzzleDraft,
      openPuzzleClearDraft,
      openSquareHoleDraft,
      openVisualNovelDraft,
      barkBattleGalleryEntries,
      barkBattleWorks,
      openBabyObjectMatchDraft,
      openBarkBattleDraft,
      puzzleClearWork,
      puzzleClearWorks,
      resolveBabyObjectMatchRuntimeDraft,
      runProtectedAction,
      selectedDetailEntry,
      selectedPuzzleDetail,
      visualNovelWorks,
    ],
  );

  const remixSelectedPublicWork = useCallback(() => {
    if (!selectedPublicWorkDetail) {
      return;
    }
    if (isSelectedPublicWorkOwned) {
      editOwnedPublicWork(selectedPublicWorkDetail);
      return;
    }
    remixPublicWork(selectedPublicWorkDetail);
  }, [
    editOwnedPublicWork,
    isSelectedPublicWorkOwned,
    remixPublicWork,
    selectedPublicWorkDetail,
  ]);

  const handlePublicCodeSearch = useCallback(
    async (keyword: string) => {
      const normalizedKeyword = keyword.trim();
      if (!normalizedKeyword) {
        return;
      }

      setIsSearchingPublicCode(true);
      setPublicSearchError(null);
      setSearchedPublicUser(null);

      const upperKeyword = normalizedKeyword.toUpperCase();
      const shouldSearchUserIdFirst = /^user[_-][a-z0-9_-]+$/iu.test(
        normalizedKeyword,
      );
      const shouldSearchBigFishFirst = upperKeyword.startsWith('BF');
      const shouldSearchBabyObjectFirst = upperKeyword.startsWith('BO');
      const shouldSearchJumpHopFirst = upperKeyword.startsWith('JH');
      const shouldSearchWoodenFishFirst = upperKeyword.startsWith('WF');
      const shouldSearchPuzzleClearFirst = upperKeyword.startsWith('PC');
      const shouldSearchMatch3DFirst = upperKeyword.startsWith('M3');
      const shouldSearchPuzzleFirst = upperKeyword.startsWith('PZ');
      const shouldSearchSquareHoleFirst = upperKeyword.startsWith('SH');
      const shouldSearchVisualNovelFirst = upperKeyword.startsWith('VN');
      const shouldSearchBarkBattleFirst = upperKeyword.startsWith('BB');
      const shouldSearchWorkFirst =
        !shouldSearchUserIdFirst &&
        !shouldSearchBabyObjectFirst &&
        !shouldSearchBigFishFirst &&
        !shouldSearchJumpHopFirst &&
        !shouldSearchWoodenFishFirst &&
        !shouldSearchPuzzleClearFirst &&
        !shouldSearchMatch3DFirst &&
        !shouldSearchPuzzleFirst &&
        !shouldSearchSquareHoleFirst &&
        !shouldSearchVisualNovelFirst &&
        !shouldSearchBarkBattleFirst &&
        (upperKeyword.startsWith('CW') || /^\d{1,8}$/u.test(normalizedKeyword));
      const shouldSearchUserFirst =
        shouldSearchUserIdFirst ||
        upperKeyword.startsWith('SY') ||
        (!shouldSearchWorkFirst &&
          !shouldSearchBigFishFirst &&
          !shouldSearchBabyObjectFirst &&
          !shouldSearchJumpHopFirst &&
          !shouldSearchWoodenFishFirst &&
          !shouldSearchPuzzleClearFirst &&
          !shouldSearchMatch3DFirst &&
          !shouldSearchPuzzleFirst &&
          !shouldSearchSquareHoleFirst &&
          !shouldSearchVisualNovelFirst);

      const tryOpenGalleryEntry = async () => {
        const entry =
          await getRpgEntryWorldGalleryDetailByCode(normalizedKeyword);
        const card = {
          sourceType: 'custom-world',
          ownerUserId: entry.ownerUserId,
          profileId: entry.profileId,
          publicWorkCode: entry.publicWorkCode,
          authorPublicUserCode: entry.authorPublicUserCode,
          visibility: 'published',
          publishedAt: entry.publishedAt,
          updatedAt: entry.updatedAt,
          authorDisplayName: entry.authorDisplayName,
          worldName: entry.worldName,
          subtitle: entry.subtitle,
          summaryText: entry.summaryText,
          coverImageSrc: entry.coverImageSrc,
          themeMode: entry.themeMode,
          playableNpcCount: entry.playableNpcCount,
          landmarkCount: entry.landmarkCount,
          playCount: entry.playCount ?? 0,
          remixCount: entry.remixCount ?? 0,
          likeCount: entry.likeCount ?? 0,
        } satisfies PlatformPublicGalleryCard;
        if (!canExposePublicWork(card)) {
          throw new Error(EDUTAINMENT_HIDDEN_MESSAGE);
        }

        setSelectedDetailEntry(entry);
        openPublicWorkDetail(card);
      };
      const tryOpenPuzzleGalleryEntry = async () => {
        const entries =
          puzzleGalleryEntries.length > 0
            ? puzzleGalleryEntries
            : await refreshPuzzleGallery();
        const matchedEntry = entries
          .map(mapPuzzleWorkToPublicWorkDetail)
          .filter(canExposePublicWork)
          .find((entry) =>
            isSamePuzzlePublicWorkCode(normalizedKeyword, entry.profileId),
          );

        if (!matchedEntry) {
          throw new Error('未找到拼图作品。');
        }

        await openPuzzlePublicWorkDetail(matchedEntry.profileId, {
          tab: platformBootstrap.platformTab,
        });
      };
      const tryOpenBigFishGalleryEntry = async () => {
        const entries =
          bigFishGalleryEntries.length > 0
            ? bigFishGalleryEntries
            : await refreshBigFishGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapBigFishWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameBigFishPublicWorkCode(
              normalizedKeyword,
              entry.sourceSessionId,
            )
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到大鱼吃小鱼作品。');
        }

        openPublicWorkDetail(mapBigFishWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenJumpHopGalleryEntry = async () => {
        const entries =
          jumpHopGalleryEntries.length > 0
            ? jumpHopGalleryEntries
            : await refreshJumpHopGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapJumpHopWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameJumpHopPublicWorkCode(normalizedKeyword, entry.profileId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到跳一跳作品。');
        }

        openPublicWorkDetail(mapJumpHopWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenWoodenFishGalleryEntry = async () => {
        const entries =
          woodenFishGalleryEntries.length > 0
            ? woodenFishGalleryEntries
            : await refreshWoodenFishGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapWoodenFishWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameWoodenFishPublicWorkCode(normalizedKeyword, entry.profileId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到敲木鱼作品。');
        }

        openPublicWorkDetail(mapWoodenFishWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenPuzzleClearGalleryEntry = async () => {
        const entries =
          puzzleClearGalleryEntries.length > 0
            ? puzzleClearGalleryEntries
            : await refreshPuzzleClearGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapPuzzleClearWorkToPlatformGalleryCard(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSamePuzzleClearPublicWorkCode(
              normalizedKeyword,
              entry.profileId,
            )
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到拼消消作品。');
        }

        await openPuzzleClearPublicWorkDetail(matchedEntry.profileId);
      };
      const tryOpenMatch3DGalleryEntry = async () => {
        const entries =
          match3dGalleryEntries.length > 0
            ? match3dGalleryEntries
            : await refreshMatch3DGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapMatch3DWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameMatch3DPublicWorkCode(normalizedKeyword, entry.profileId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到抓大鹅作品。');
        }

        openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenSquareHoleGalleryEntry = async () => {
        const entries =
          squareHoleGalleryEntries.length > 0
            ? squareHoleGalleryEntries
            : await refreshSquareHoleGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapSquareHoleWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameSquareHolePublicWorkCode(normalizedKeyword, entry.profileId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到方洞挑战作品。');
        }

        openPublicWorkDetail(mapSquareHoleWorkToPublicWorkDetail(matchedEntry));
      };
      const tryOpenVisualNovelGalleryEntry = async () => {
        const entries =
          visualNovelGalleryEntries.length > 0
            ? visualNovelGalleryEntries
            : await refreshVisualNovelGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapVisualNovelWorkToPublicWorkDetail(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameVisualNovelPublicWorkCode(normalizedKeyword, entry.profileId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到视觉小说作品。');
        }

        openPublicWorkDetail(
          mapVisualNovelWorkToPublicWorkDetail(matchedEntry),
        );
      };
      const tryOpenBabyObjectMatchGalleryEntry = async () => {
        const entries = (await listLocalBabyObjectMatchDrafts()).filter(
          (draft) => draft.publicationStatus === 'published',
        );
        const matchedDraft = entries.find((draft) => {
          const detailEntry =
            mapBabyObjectMatchDraftToPlatformGalleryCard(draft);
          return (
            canExposePublicWork(detailEntry) &&
            isSameBabyObjectMatchPublicWorkCode(
              normalizedKeyword,
              draft.profileId,
            )
          );
        });

        if (!matchedDraft) {
          throw new Error('未找到宝贝识物作品。');
        }

        const detailEntry =
          mapBabyObjectMatchDraftToPlatformGalleryCard(matchedDraft);
        setBabyObjectMatchDraft(matchedDraft);
        openPublicWorkDetail(detailEntry);
      };
      const tryOpenBarkBattleGalleryEntry = async () => {
        const entries =
          barkBattleGalleryEntries.length > 0
            ? barkBattleGalleryEntries
            : await refreshBarkBattleGallery();
        const matchedEntry = entries.find((entry) => {
          const detailEntry = mapBarkBattleWorkToPlatformGalleryCard(entry);
          return (
            canExposePublicWork(detailEntry) &&
            isSameBarkBattlePublicWorkCode(normalizedKeyword, entry.workId)
          );
        });

        if (!matchedEntry) {
          throw new Error('未找到汪汪声浪作品。');
        }

        if (selectionStage === 'bark-battle-runtime') {
          startBarkBattleRunFromWork(matchedEntry, 'platform');
          return;
        }

        openPublicWorkDetail(
          mapBarkBattleWorkToPlatformGalleryCard(matchedEntry),
        );
      };

      try {
        if (shouldSearchUserIdFirst) {
          const user = await getPublicAuthUserById(normalizedKeyword);
          setSearchedPublicUser(user);
          return;
        }

        if (shouldSearchPuzzleFirst) {
          await tryOpenPuzzleGalleryEntry();
          return;
        }

        if (shouldSearchBigFishFirst) {
          await tryOpenBigFishGalleryEntry();
          return;
        }

        if (shouldSearchJumpHopFirst) {
          await tryOpenJumpHopGalleryEntry();
          return;
        }

        if (shouldSearchWoodenFishFirst) {
          await tryOpenWoodenFishGalleryEntry();
          return;
        }

        if (shouldSearchPuzzleClearFirst) {
          await tryOpenPuzzleClearGalleryEntry();
          return;
        }

        if (shouldSearchBabyObjectFirst) {
          await tryOpenBabyObjectMatchGalleryEntry();
          return;
        }

        if (shouldSearchMatch3DFirst) {
          await tryOpenMatch3DGalleryEntry();
          return;
        }

        if (shouldSearchSquareHoleFirst) {
          await tryOpenSquareHoleGalleryEntry();
          return;
        }

        if (shouldSearchVisualNovelFirst) {
          await tryOpenVisualNovelGalleryEntry();
          return;
        }

        if (shouldSearchBarkBattleFirst) {
          await tryOpenBarkBattleGalleryEntry();
          return;
        }

        if (shouldSearchWorkFirst) {
          try {
            await tryOpenGalleryEntry();
            return;
          } catch {
            // 作品号优先时允许继续回退到用户号搜索。
          }
        }

        if (shouldSearchUserFirst) {
          try {
            const user = await getPublicAuthUserByCode(normalizedKeyword);
            setSearchedPublicUser(user);
            return;
          } catch {
            // 用户号优先时允许继续回退到作品号搜索。
          }
        }

        if (!shouldSearchWorkFirst) {
          try {
            await tryOpenGalleryEntry();
            return;
          } catch {
            // 常规作品未命中时继续尝试汪汪声浪作品号。
          }

          try {
            await tryOpenBarkBattleGalleryEntry();
            return;
          } catch {
            // 汪汪声浪作品未命中时继续回退到陶泥号搜索。
          }
        }

        const user = await getPublicAuthUserByCode(normalizedKeyword);
        setSearchedPublicUser(user);
      } catch (error) {
        if (selectionStage === 'work-detail') {
          setSelectedPublicWorkDetail(null);
          setSelectedDetailEntry(null);
          setSelectedPuzzleDetail(null);
          setPuzzleDetailReturnTarget(null);
          setPuzzleRun(null);
          setPuzzleRuntimeAuthMode('default');
          setPuzzleError(null);
          setPublicWorkDetailError(null);
          returnPlatformHomeAfterMissingWork();
          return;
        }

        setPublicSearchError(
          resolveRpgCreationErrorMessage(error, '未找到对应的陶泥号或作品号。'),
        );
      } finally {
        setIsSearchingPublicCode(false);
      }
    },
    [
      barkBattleGalleryEntries,
      bigFishGalleryEntries,
      jumpHopGalleryEntries,
      match3dGalleryEntries,
      puzzleClearGalleryEntries,
      refreshMatch3DGallery,
      openPuzzlePublicWorkDetail,
      openPuzzleClearPublicWorkDetail,
      openPublicWorkDetail,
      platformBootstrap.platformTab,
      puzzleGalleryEntries,
      refreshBarkBattleGallery,
      refreshBigFishGallery,
      refreshJumpHopGallery,
      refreshPuzzleClearGallery,
      refreshWoodenFishGallery,
      refreshPuzzleGallery,
      refreshSquareHoleGallery,
      refreshVisualNovelGallery,
      squareHoleGalleryEntries,
      returnPlatformHomeAfterMissingWork,
      selectionStage,
      setPlatformTab,
      setPuzzleError,
      setSelectionStage,
      startBarkBattleRunFromWork,
      visualNovelGalleryEntries,
      woodenFishGalleryEntries,
    ],
  );

  const openProfilePlayedWorks = useCallback(() => {
    setIsProfilePlayStatsOpen(true);
    setIsProfilePlayStatsLoading(true);
    setProfilePlayStatsError(null);

    void getRpgProfilePlayStats()
      .then(setProfilePlayStats)
      .catch((error) => {
        setProfilePlayStats(null);
        setProfilePlayStatsError(
          resolveRpgCreationErrorMessage(error, '读取玩过作品失败。'),
        );
      })
      .finally(() => {
        setIsProfilePlayStatsLoading(false);
      });
  }, []);

  const openPlayedWork = useCallback(
    (work: ProfilePlayedWorkSummary) => {
      const worldType = (work.worldType ?? '').toLowerCase();
      setIsProfilePlayStatsOpen(false);

      if (worldType === 'puzzle' || work.worldKey.startsWith('puzzle:')) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^puzzle:/u, '');
        if (profileId) {
          void openPuzzlePublicWorkDetail(profileId, { tab: 'profile' });
        }
        return;
      }

      if (
        worldType === 'match3d' ||
        worldType === 'match_3d' ||
        work.worldKey.startsWith('match3d:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^match3d:/u, '');
        if (profileId) {
          void openMatch3DPublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'square-hole' ||
        worldType === 'square_hole' ||
        work.worldKey.startsWith('square-hole:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^square-hole:/u, '');
        if (profileId) {
          void openSquareHolePublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'jump-hop' ||
        worldType === 'jump_hop' ||
        work.worldKey.startsWith('jump-hop:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^jump-hop:/u, '');
        if (profileId) {
          void openJumpHopPublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'puzzle-clear' ||
        worldType === 'puzzle_clear' ||
        work.worldKey.startsWith('puzzle-clear:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^puzzle-clear:/u, '');
        if (profileId) {
          void openPuzzleClearPublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'wooden-fish' ||
        worldType === 'wooden_fish' ||
        work.worldKey.startsWith('wooden-fish:')
      ) {
        const profileId =
          work.profileId ?? work.worldKey.replace(/^wooden-fish:/u, '');
        if (profileId) {
          void openWoodenFishPublicWorkDetail(profileId);
        }
        return;
      }

      if (
        worldType === 'big_fish' ||
        worldType === 'big-fish' ||
        work.worldKey.startsWith('big-fish:')
      ) {
        const sessionId =
          work.profileId ?? work.worldKey.replace(/^big-fish:/u, '');
        if (!sessionId) {
          return;
        }
        void refreshBigFishGallery()
          .then((entries) => {
            const matchedEntry = entries.find(
              (entry) => entry.sourceSessionId === sessionId,
            );
            if (matchedEntry) {
              openPublicWorkDetail(
                mapBigFishWorkToPublicWorkDetail(matchedEntry),
              );
              return;
            }
            openPublicWorkDetail(
              mapBigFishWorkToPublicWorkDetail({
                workId: `big-fish:${sessionId}`,
                sourceSessionId: sessionId,
                ownerUserId: work.ownerUserId ?? '',
                authorDisplayName: work.worldSubtitle || '玩家',
                title: work.worldTitle,
                subtitle: work.worldSubtitle,
                summary: work.worldSubtitle,
                coverImageSrc: null,
                status: 'published',
                updatedAt: work.lastPlayedAt,
                publishReady: true,
                levelCount: 0,
                levelMainImageReadyCount: 0,
                levelMotionReadyCount: 0,
                backgroundReady: false,
              }),
            );
          })
          .catch((error) => {
            setBigFishError(
              resolveBigFishErrorMessage(error, '进入大鱼吃小鱼作品失败。'),
            );
          });
        return;
      }

      const profileId = work.profileId ?? work.worldKey;
      const ownerUserId = work.ownerUserId;
      if (!ownerUserId || !profileId) {
        return;
      }

      void openRpgPublicWorkDetail({
        ownerUserId,
        profileId,
        publicWorkCode: null,
        authorPublicUserCode: null,
        visibility: 'published',
        publishedAt: work.firstPlayedAt,
        updatedAt: work.lastPlayedAt,
        authorDisplayName: work.worldSubtitle,
        worldName: work.worldTitle,
        subtitle: work.worldSubtitle,
        summaryText: '',
        coverImageSrc: null,
        themeMode: 'martial',
        playableNpcCount: 0,
        landmarkCount: 0,
        playCount: 0,
        remixCount: 0,
        likeCount: 0,
      });
    },
    [
      openMatch3DPublicWorkDetail,
      openPuzzlePublicWorkDetail,
      openPublicWorkDetail,
      openJumpHopPublicWorkDetail,
      openPuzzleClearPublicWorkDetail,
      openWoodenFishPublicWorkDetail,
      openRpgPublicWorkDetail,
      openSquareHolePublicWorkDetail,
      refreshBigFishGallery,
      resolveBigFishErrorMessage,
      setBigFishError,
    ],
  );

  useEffect(() => {
    const publicWorkCode = initialPublicWorkCode?.trim();
    if (
      !publicWorkCode ||
      handledInitialPublicWorkCodeRef.current === publicWorkCode
    ) {
      return;
    }

    handledInitialPublicWorkCodeRef.current = publicWorkCode;
    void handlePublicCodeSearch(publicWorkCode);
  }, [handlePublicCodeSearch, initialPublicWorkCode]);

  useEffect(() => {
    if (selectionStage === 'platform') {
      if (isBigFishCreationVisible) {
        void refreshBigFishGallery();
      }
      void refreshJumpHopGallery();
      void refreshPuzzleClearGallery();
      void refreshWoodenFishGallery();
      void refreshMatch3DGallery();
      void refreshPuzzleGallery();
      void refreshBarkBattleGallery();
      if (isSquareHoleCreationVisible) {
        void refreshSquareHoleGallery();
      }
      if (isVisualNovelCreationOpen) {
        void refreshVisualNovelGallery();
      }
    }
  }, [
    isBigFishCreationVisible,
    isSquareHoleCreationVisible,
    isVisualNovelCreationOpen,
    refreshBigFishGallery,
    refreshJumpHopGallery,
    refreshPuzzleClearGallery,
    refreshWoodenFishGallery,
    refreshMatch3DGallery,
    refreshPuzzleGallery,
    refreshBarkBattleGallery,
    refreshSquareHoleGallery,
    refreshVisualNovelGallery,
    selectionStage,
  ]);

  useEffect(() => {
    if (
      platformBootstrap.platformTab === 'create' &&
      platformBootstrap.canReadProtectedData
    ) {
      // 中文注释：底部加号创作入口的“最近创作”依赖 RPG works 摘要；
      // 失败草稿也必须随进入创作页刷新，不能只等草稿页刷新后才可见。
      void platformBootstrap.refreshCustomWorldWorks().catch((error) => {
        platformBootstrap.setPlatformError(
          resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'),
        );
      });
    }

    if (
      (platformBootstrap.platformTab === 'create' ||
        selectionStage === 'platform') &&
      platformBootstrap.canReadProtectedData
    ) {
      void refreshPuzzleShelf();
      void refreshPuzzleClearShelf();
      void refreshMatch3DShelf();
      if (isSquareHoleCreationVisible) {
        void refreshSquareHoleShelf();
      }
      if (isVisualNovelCreationOpen) {
        void refreshVisualNovelShelf();
      }
      void refreshWoodenFishShelf();
      void refreshBabyObjectMatchShelf();
      void refreshBarkBattleShelf();
    }
  }, [
    isSquareHoleCreationVisible,
    isVisualNovelCreationOpen,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    platformBootstrap.refreshCustomWorldWorks,
    platformBootstrap.setPlatformError,
    refreshBabyObjectMatchShelf,
    refreshBarkBattleShelf,
    refreshMatch3DShelf,
    refreshPuzzleShelf,
    refreshPuzzleClearShelf,
    refreshWoodenFishShelf,
    refreshSquareHoleShelf,
    refreshVisualNovelShelf,
    selectionStage,
  ]);

  useEffect(() => {
    if (
      isBigFishCreationVisible &&
      (platformBootstrap.platformTab === 'create' ||
        selectionStage === 'platform') &&
      platformBootstrap.canReadProtectedData
    ) {
      void refreshBigFishShelf();
    }
  }, [
    isBigFishCreationVisible,
    platformBootstrap.canReadProtectedData,
    platformBootstrap.platformTab,
    refreshBigFishShelf,
    selectionStage,
  ]);

  // 中文注释：最近创作必须由真实作品架/后端草稿摘要决定，不能混入本地生成中占位。
  const backendRecentCreationShelfItems = useMemo(
    () =>
      buildCreationWorkShelfItems({
        rpgItems: creationHubItems,
        rpgLibraryEntries: platformBootstrap.savedCustomWorldEntries,
        bigFishItems: isBigFishCreationVisible ? bigFishWorks : [],
        match3dItems: match3dWorks,
        squareHoleItems: isSquareHoleCreationVisible ? squareHoleWorks : [],
        jumpHopItems: isJumpHopCreationVisible ? jumpHopWorks : [],
        woodenFishItems: woodenFishWorks,
        puzzleItems: puzzleWorks,
        babyObjectMatchItems: isBabyObjectMatchVisible
          ? babyObjectMatchDrafts
          : [],
        barkBattleItems: barkBattleWorks,
        visualNovelItems: isVisualNovelCreationOpen ? visualNovelWorks : [],
        getItemState: getCreationWorkShelfState,
      }),
    [
      barkBattleWorks,
      babyObjectMatchDrafts,
      bigFishWorks,
      creationHubItems,
      getCreationWorkShelfState,
      isBabyObjectMatchVisible,
      isBigFishCreationVisible,
      isJumpHopCreationVisible,
      isSquareHoleCreationVisible,
      isVisualNovelCreationOpen,
      jumpHopWorks,
      match3dWorks,
      platformBootstrap.savedCustomWorldEntries,
      puzzleWorks,
      squareHoleWorks,
      visualNovelWorks,
      woodenFishWorks,
    ],
  );

  const renderCreationHubContent = (
    mode: 'start-only' | 'works-only',
    fallbackLabel: string,
  ) => (
    <Suspense fallback={<LazyPanelFallback label={fallbackLabel} />}>
      {creationEntryConfig ? (
        <CustomWorldCreationHub
          mode={mode}
          items={creationHubItems}
          loading={
            platformBootstrap.isLoadingPlatform ||
            isBigFishLoadingLibrary ||
            isMatch3DLoadingLibrary ||
            (isSquareHoleCreationVisible && isSquareHoleLoadingLibrary) ||
            isPuzzleLoadingLibrary ||
            (isVisualNovelCreationOpen && isVisualNovelLoadingLibrary) ||
            isBabyObjectMatchBusy
          }
          error={
            platformBootstrap.isLoadingPlatform ||
            isBigFishLoadingLibrary ||
            isMatch3DLoadingLibrary ||
            (isSquareHoleCreationVisible && isSquareHoleLoadingLibrary) ||
            isPuzzleLoadingLibrary ||
            (isVisualNovelCreationOpen && isVisualNovelLoadingLibrary) ||
            isBabyObjectMatchBusy
              ? null
              : (platformBootstrap.platformError ??
                sessionController.agentWorkspaceRestoreError ??
                bigFishError ??
                match3dError ??
                (isSquareHoleCreationVisible ? squareHoleError : null) ??
                puzzleShelfError ??
                puzzleError ??
                (isVisualNovelCreationOpen ? visualNovelError : null) ??
              babyObjectMatchError ??
                puzzleClearError ??
                barkBattleError)
          }
          onRetry={() => {
            platformBootstrap.setPlatformError(null);
            setCreationEntryConfigError(null);
            void fetchCreationEntryConfig()
              .then(setCreationEntryConfig)
              .catch((error: unknown) => {
                setCreationEntryConfigError(
                  error instanceof Error
                    ? error.message
                    : '读取创作入口配置失败。',
                );
              });
            setBigFishError(null);
            setMatch3DError(null);
            setSquareHoleError(null);
            setPuzzleShelfError(null);
            setPuzzleCreationError(null);
            setPuzzleError(null);
            setVisualNovelError(null);
            setBabyObjectMatchError(null);
            setWoodenFishError(null);
            void platformBootstrap.refreshCustomWorldWorks().catch((error) => {
              platformBootstrap.setPlatformError(
                resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'),
              );
            });
            if (isBigFishCreationVisible) {
              void refreshBigFishShelf();
            }
            void refreshMatch3DShelf();
            void refreshPuzzleClearShelf();
            if (isSquareHoleCreationVisible) {
              void refreshSquareHoleShelf();
            }
            void refreshPuzzleShelf();
            void refreshWoodenFishShelf();
            if (isVisualNovelCreationOpen) {
              void refreshVisualNovelShelf();
            }
            void refreshBabyObjectMatchShelf();
            void refreshBarkBattleShelf();
          }}
          createBusy={
            !creationEntryConfig ||
            sessionController.isCreatingAgentSession ||
            isCreativeAgentBusy ||
            isCreativeAgentStreaming ||
            isBigFishBusy ||
            isMatch3DBusy ||
            (isSquareHoleCreationVisible && isSquareHoleBusy) ||
            isPuzzleClearBusy ||
            isWoodenFishBusy ||
            isPuzzleBusy ||
            (isVisualNovelCreationOpen && isVisualNovelBusy) ||
            (isVisualNovelCreationOpen && isVisualNovelStreamingReply) ||
            isBabyObjectMatchBusy ||
            isBarkBattleBusy
          }
          entryConfig={creationEntryConfig}
          creationTypes={creationEntryTypes}
          recentWorkItems={backendRecentCreationShelfItems}
          onCreateType={handleCreationHubCreateType}
          getWorkState={getCreationWorkShelfState}
          onOpenShelfItem={(item) => {
            markDraftNoticeSeen(getGenerationNoticeShelfKeys(item));
          }}
          onOpenDraft={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              void detailNavigation.handleOpenCreationWork(item);
            });
          }}
          onEnterPublished={(profileId) => {
            runProtectedAction(() => {
              const matchedWork = creationHubItems.find(
                (entry) => entry.profileId === profileId,
              );
              if (!matchedWork) {
                return;
              }
              markCreationFlowReturnToDraftShelf();
              void detailNavigation.handleOpenCreationWork(matchedWork);
            });
          }}
          onDeletePublished={(item) => {
            handleDeletePublishedWork(item);
          }}
          deletingWorkId={deletingCreationWorkId}
          rpgLibraryEntries={platformBootstrap.savedCustomWorldEntries}
          bigFishItems={isBigFishCreationVisible ? bigFishShelfItems : []}
          jumpHopItems={isJumpHopCreationVisible ? jumpHopShelfItems : []}
          puzzleClearItems={
            isPuzzleClearCreationVisible ? puzzleClearShelfItems : []
          }
          woodenFishItems={woodenFishShelfItems}
          onOpenBigFishDetail={
            isBigFishCreationVisible
              ? (item) => {
                  runProtectedAction(() => {
                    markCreationFlowReturnToDraftShelf();
                    void openBigFishDraft(item);
                  });
                }
              : undefined
          }
          onOpenJumpHopDetail={
            isJumpHopCreationVisible
              ? (item) => {
                  runProtectedAction(() => {
                    markCreationFlowReturnToDraftShelf();
                    void openJumpHopDraft(item);
                  });
                }
              : undefined
          }
          onDeleteBigFish={
            isBigFishCreationVisible
              ? (item) => {
                  handleDeleteBigFishWork(item);
                }
              : null
          }
          onDeleteJumpHop={
            isJumpHopCreationVisible
              ? (item) => {
                  handleDeleteJumpHopWork(item);
                }
              : null
          }
          onOpenPuzzleClearDetail={
            isPuzzleClearCreationVisible
              ? (item) => {
                  runProtectedAction(() => {
                    markCreationFlowReturnToDraftShelf();
                    void openPuzzleClearDraft(item);
                  });
                }
              : undefined
          }
          onDeletePuzzleClear={null}
          onOpenWoodenFishDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              void openWoodenFishDraft(item);
            });
          }}
          onDeleteWoodenFish={(item) => {
            handleDeleteWoodenFishWork(item);
          }}
          match3dItems={match3dShelfItems}
          onOpenMatch3DDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              void openMatch3DDraft(item);
            });
          }}
          onDeleteMatch3D={(item) => {
            handleDeleteMatch3DWork(item);
          }}
          squareHoleItems={
            isSquareHoleCreationVisible ? squareHoleShelfItems : []
          }
          onOpenSquareHoleDetail={
            isSquareHoleCreationVisible
              ? (item) => {
                  runProtectedAction(() => {
                    markCreationFlowReturnToDraftShelf();
                    void openSquareHoleDraft(item);
                  });
                }
              : undefined
          }
          onDeleteSquareHole={
            isSquareHoleCreationVisible
              ? (item) => {
                  handleDeleteSquareHoleWork(item);
                }
              : null
          }
          puzzleItems={puzzleShelfItems}
          onOpenPuzzleDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              void openPuzzleDraft(item);
            });
          }}
          onDeletePuzzle={(item) => {
            handleDeletePuzzleWork(item);
          }}
          onClaimPuzzlePointIncentive={(item) => {
            handleClaimPuzzlePointIncentive(item);
          }}
          claimingPuzzleProfileId={claimingPuzzlePointIncentiveProfileId}
          babyObjectMatchItems={
            isBabyObjectMatchVisible ? babyObjectMatchDrafts : []
          }
          onOpenBabyObjectMatchDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              openBabyObjectMatchDraft(item);
            });
          }}
          onDeleteBabyObjectMatch={(item) => {
            handleDeleteBabyObjectMatchWork(item);
          }}
          barkBattleItems={barkBattleShelfItems}
          onOpenBarkBattleDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              openBarkBattleDraft(item);
            });
          }}
          onDeleteBarkBattle={(item) => {
            handleDeleteBarkBattleWork(item);
          }}
          visualNovelItems={visualNovelShelfItems}
          onOpenVisualNovelDetail={(item) => {
            runProtectedAction(() => {
              markCreationFlowReturnToDraftShelf();
              void openVisualNovelDraft(item);
            });
          }}
          onDeleteVisualNovel={(item) => {
            handleDeleteVisualNovelWork(item);
          }}
        />
      ) : null}
    </Suspense>
  );
  const creationStartContent = renderCreationHubContent(
    'start-only',
    '正在加载创作大厅...',
  );
  const draftHubContent = renderCreationHubContent(
    'works-only',
    '正在加载草稿列表...',
  );

  return (
    <>
      <AnimatePresence mode="wait">
        {selectionStage === 'platform' && (
          <motion.div
            key="platform-home"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden"
          >
            <PlatformEntryHomeView
              activeTab={platformBootstrap.platformTab}
              onTabChange={platformBootstrap.setPlatformTab}
              hasSavedGame={hasSavedGame}
              savedSnapshot={savedSnapshot}
              saveEntries={platformBootstrap.saveEntries}
              saveError={platformBootstrap.saveError}
              featuredEntries={featuredGalleryEntries}
              latestEntries={latestGalleryEntries}
              myEntries={platformBootstrap.savedCustomWorldEntries}
              historyEntries={platformBootstrap.historyEntries}
              profileDashboard={platformBootstrap.profileDashboard}
              isLoadingPlatform={platformBootstrap.isLoadingPlatform}
              isLoadingDashboard={platformBootstrap.isLoadingDashboard}
              hasUnreadDraftUpdate={hasUnreadDraftUpdates}
              profileTaskRefreshKey={profileTaskRefreshKey}
              isDesktopLayout={isDesktopLayout}
              isResumingSaveWorldKey={platformBootstrap.isResumingSaveWorldKey}
              platformError={
                platformBootstrap.isLoadingPlatform
                  ? null
                  : (platformBootstrapErrorForDisplay ??
                    sessionController.agentWorkspaceRestoreError)
              }
              dashboardError={
                platformBootstrap.isLoadingDashboard
                  ? null
                  : platformBootstrap.dashboardError
              }
              createTabContent={creationStartContent}
              draftTabContent={draftHubContent}
              onContinueGame={handleContinueGame}
              onResumeSave={(entry) => {
                if (
                  (entry.worldType ?? '').toLowerCase() === 'puzzle' ||
                  entry.worldKey.startsWith('puzzle:')
                ) {
                  void resumePuzzleSaveArchive(entry);
                  return;
                }
                void platformBootstrap.handleResumeSaveEntry(entry);
              }}
              onOpenCreateWorld={openCreationTypePicker}
              onOpenCreateTypePicker={openCreationTypePicker}
              onOpenGalleryDetail={openPublicGalleryDetail}
              onOpenChildMotionDemo={startChildMotionDemo}
              onOpenBabyLoveDrawing={startBabyLoveDrawingRuntime}
              onOpenRecommendGalleryDetail={openRecommendGalleryDetail}
              recommendRuntimeContent={recommendRuntimeContent}
              activeRecommendEntryKey={activeRecommendEntryKey}
              isRecommendRuntimeReady={isActiveRecommendRuntimeReady}
              isStartingRecommendEntry={
                isStartingRecommendEntry ||
                isBigFishBusy ||
                (isPuzzleBusy &&
                  !(activeRecommendRuntimeKind === 'puzzle' && puzzleRun)) ||
                (isPuzzleClearBusy &&
                  !(
                    activeRecommendRuntimeKind === 'puzzle-clear' &&
                    puzzleClearRun
                  )) ||
                isMatch3DBusy ||
                isSquareHoleBusy ||
                isVisualNovelBusy ||
                isWoodenFishBusy
              }
              recommendRuntimeError={activeRecommendRuntimeError}
              onSelectNextRecommendEntry={(activeEntryKey) =>
                selectAdjacentRecommendRuntimeEntry(1, activeEntryKey)
              }
              onSelectPreviousRecommendEntry={(activeEntryKey) =>
                selectAdjacentRecommendRuntimeEntry(-1, activeEntryKey)
              }
              onLikeRecommendEntry={(entry) => {
                likePublicWork(entry);
              }}
              onShareRecommendEntry={(entry) => {
                openRecommendShareModal(entry);
              }}
              onRemixRecommendEntry={(entry) => {
                remixPublicWork(entry);
              }}
              onOpenLibraryDetail={(entry) => {
                runProtectedAction(() => {
                  void detailNavigation.openLibraryDetail(entry);
                });
              }}
              onDeleteLibraryEntry={(entry) => {
                handleDeleteLibraryEntry(entry);
              }}
              deletingLibraryEntryId={deletingCreationWorkId}
              onSearchPublicCode={(keyword) => {
                void handlePublicCodeSearch(keyword);
              }}
              isSearchingPublicCode={isSearchingPublicCode}
              profilePlayStats={profilePlayStats}
              isProfilePlayStatsOpen={isProfilePlayStatsOpen}
              isProfilePlayStatsLoading={isProfilePlayStatsLoading}
              profilePlayStatsError={profilePlayStatsError}
              onCloseProfilePlayStats={() => {
                setIsProfilePlayStatsOpen(false);
              }}
              onOpenPlayedWork={openPlayedWork}
              onOpenFeedback={openProfileFeedback}
              onOpenProfileDashboardCard={(cardKey) => {
                if (cardKey === 'playedWorks') {
                  openProfilePlayedWorks();
                  return;
                }
                if (platformBootstrap.dashboardError) {
                  void platformBootstrap.refreshProfileDashboard();
                }
              }}
              onRechargeSuccess={platformBootstrap.refreshProfileDashboard}
            />
          </motion.div>
        )}

        {selectionStage === 'profile-feedback' && (
          <motion.div
            key="platform-profile-feedback"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <PlatformFeedbackView
              onBack={() => {
                setPlatformTab('profile');
                setSelectionStage('platform');
              }}
              onSubmit={submitRpgProfileFeedback}
            />
          </motion.div>
        )}

        {selectionStage === 'work-detail' && !selectedPublicWorkDetail && (
          <motion.div
            key="platform-work-detail-empty"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 items-center justify-center"
          >
            <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
              {publicWorkDetailError || '正在读取作品详情...'}
            </div>
          </motion.div>
        )}

        {selectionStage === 'work-detail' && selectedPublicWorkDetail && (
          <motion.div
            key="platform-work-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <PlatformWorkDetailView
              entry={selectedPublicWorkDetail}
              authorSummary={selectedPublicWorkAuthor}
              isBusy={
                isPublicWorkDetailBusy ||
                isPuzzleBusy ||
                isPuzzleClearBusy ||
                isBigFishBusy ||
                isMatch3DBusy ||
                isSquareHoleBusy ||
                isVisualNovelBusy
              }
              error={publicWorkDetailError}
              actionMode={selectedPublicWorkActionMode}
              visibleCoverCount={resolveVisiblePuzzleDetailCoverCount(
                selectedPublicWorkDetail,
                puzzleRun,
              )}
              onBack={() => {
                setPublicWorkDetailError(null);
                clearSelectedPublicWorkAuthor();
                setSelectionStage('platform');
              }}
              onLike={() => {
                likePublicWork(selectedPublicWorkDetail);
              }}
              onStart={startSelectedPublicWork}
              onRemix={remixSelectedPublicWork}
            />
          </motion.div>
        )}

        {selectionStage === 'detail' && (
          <motion.div
            key="platform-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            {detailNavigation.isDetailLoading || !selectedDetailEntry ? (
              <div className="flex h-full items-center justify-center">
                <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
                  {detailNavigation.detailError || '正在读取作品详情...'}
                </div>
              </div>
            ) : selectedDetailEntry.visibility !== 'draft' ? (
              <PlatformWorkDetailView
                entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)}
                authorSummary={selectedPublicWorkAuthor}
                isBusy={detailNavigation.isMutatingDetail}
                error={detailNavigation.detailError}
                actionMode={
                  detailNavigation.isSelectedWorldOwned ? 'edit' : 'remix'
                }
                onBack={() => {
                  detailNavigation.setDetailError(null);
                  clearSelectedPublicWorkAuthor();
                  entryNavigation.backToPlatformHome();
                }}
                onLike={() => {
                  likePublicWork(
                    mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry),
                  );
                }}
                onStart={handleStartSelectedWorld}
                onRemix={() => {
                  const publicWorkEntry =
                    mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry);
                  if (detailNavigation.isSelectedWorldOwned) {
                    editOwnedPublicWork(publicWorkEntry);
                    return;
                  }
                  remixPublicWork(publicWorkEntry);
                }}
              />
            ) : (
              <PlatformEntryWorldDetailView
                entry={selectedDetailEntry}
                isMutating={detailNavigation.isMutatingDetail}
                error={detailNavigation.detailError}
                onBack={() => {
                  detailNavigation.setDetailError(null);
                  entryNavigation.backToPlatformHome();
                }}
                onStartGame={handleStartSelectedWorld}
                onContinueEdit={
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.openSavedCustomWorldEditor(
                            selectedDetailEntry,
                          );
                        });
                      }
                    : null
                }
                onPublish={
                  selectedDetailEntry.visibility === 'draft' &&
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handlePublishSelectedWorld();
                        });
                      }
                    : null
                }
                onUnpublish={
                  selectedDetailEntry.visibility !== 'draft' &&
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handleUnpublishSelectedWorld();
                        });
                      }
                    : null
                }
                onDelete={
                  detailNavigation.isSelectedWorldOwned
                    ? () => {
                        runProtectedAction(() => {
                          void detailNavigation.handleDeleteSelectedWorld();
                        });
                      }
                    : null
                }
              />
            )}
          </motion.div>
        )}

        {selectionStage === 'agent-workspace' && (
          <motion.div
            key="agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载 Agent 共创工作区..." />
              }
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('rpg')}
                onBack={leaveAgentWorkspace}
                isBackDisabled={
                  sessionController.isStreamingAgentReply ||
                  Boolean(sessionController.agentOperation)
                }
              >
                {sessionController.agentSession ? (
                  <CustomWorldAgentWorkspace
                    session={sessionController.agentSession}
                    activeOperation={sessionController.agentOperation}
                    streamingReplyText={
                      sessionController.streamingAgentReplyText
                    }
                    isStreamingReply={sessionController.isStreamingAgentReply}
                    onBack={leaveAgentWorkspace}
                    showBackButton={false}
                    onSubmitMessage={(payload) => {
                      void sessionController.submitAgentMessage(payload);
                    }}
                    onExecuteAction={(payload) => {
                      void sessionController.executeAgentAction(payload);
                    }}
                  />
                ) : (
                  <div className="flex h-full items-center justify-center">
                    <div className="platform-subpanel rounded-2xl px-5 py-4 text-sm text-[var(--platform-text-base)]">
                      {sessionController.isLoadingAgentSession
                        ? '正在准备 Agent 共创工作区...'
                        : sessionController.agentWorkspaceRestoreError ||
                          '正在恢复创作工作区...'}
                    </div>
                  </div>
                )}
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-agent-workspace' && (
          <motion.div
            key="big-fish-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载大鱼吃小鱼共创工作区..." />
              }
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('big-fish')}
                onBack={leaveBigFishFlow}
                isBackDisabled={isBigFishBusy || isStreamingBigFishReply}
              >
                <BigFishAgentWorkspace
                  session={bigFishSession}
                  streamingReplyText={streamingBigFishReplyText}
                  isStreamingReply={isStreamingBigFishReply}
                  isBusy={isBigFishBusy || isStreamingBigFishReply}
                  error={bigFishError}
                  onBack={leaveBigFishFlow}
                  showBackButton={false}
                  onSubmitMessage={(payload) => {
                    void submitBigFishMessage(payload);
                  }}
                  onExecuteAction={(payload) => {
                    void executeBigFishAction(payload);
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-generating' && (
          <motion.div
            key="big-fish-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载大鱼吃小鱼生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  bigFishSession?.lastAssistantReply ?? '正在整理当前玩法草稿。'
                }
                anchorEntries={buildBigFishGenerationAnchorEntries(
                  bigFishSession,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  bigFishGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isBigFishBusy}
                error={bigFishError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={() => {
                  setSelectionStage('big-fish-agent-workspace');
                }}
                onRetry={() => {
                  void executeBigFishAction({
                    action: 'big_fish_compile_draft',
                  });
                }}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingDescription={null}
                progressTitle="大鱼吃小鱼草稿生成进度"
                activeBadgeLabel="草稿生成中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-result' && bigFishSession?.draft && (
          <motion.div
            key="big-fish-result"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载玩法结果页..." />}
            >
              <BigFishResultView
                session={bigFishSession}
                isBusy={isBigFishBusy}
                error={bigFishError}
                onBack={() => {
                  if (shouldReturnToDraftShelf()) {
                    leaveBigFishFlow();
                    return;
                  }
                  setSelectionStage('big-fish-agent-workspace');
                }}
                onDismissError={() => {
                  setBigFishError(null);
                }}
                onExecuteAction={(payload) => {
                  void executeBigFishAction(payload);
                }}
                onStartTestRun={() => {
                  void startBigFishRun();
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'big-fish-runtime' && (
          <motion.div
            key="big-fish-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载大鱼吃小鱼玩法..." />}
            >
              <BigFishRuntimeShell
                run={bigFishRun}
                assetSlots={bigFishSession?.assetSlots ?? []}
                shareTitle={bigFishRuntimeShare?.title ?? null}
                sharePublicWorkCode={
                  bigFishRuntimeShare?.publicWorkCode ?? null
                }
                isBusy={isBigFishBusy}
                error={bigFishError}
                onBack={() => {
                  reportBigFishObservedPlayTime();
                  setSelectionStage(bigFishRuntimeReturnStage);
                }}
                onRestart={() => {
                  reportBigFishObservedPlayTime();
                  void restartBigFishRun();
                }}
                onSubmitInput={submitBigFishInput}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-agent-workspace' && (
          <motion.div
            key="match3d-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载抓大鹅共创工作区..." />
              }
            >
              <UnifiedCreationWorkspace
                playId="match3d"
                spec={getUnifiedSpec('match3d')}
                session={match3dSession}
                isBusy={isStreamingMatch3DReply}
                error={match3dError}
                onBack={leaveMatch3DFlow}
                onExecuteAction={(payload) => {
                  void executeMatch3DAction(payload);
                }}
                initialFormPayload={match3dFormDraftPayload}
                onCreateFromForm={(payload) => {
                  runProtectedAction(() => {
                    void createMatch3DDraftFromForm(payload);
                  });
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-generating' && (
          <motion.div
            key="match3d-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载抓大鹅生成面板..." />}
            >
              <UnifiedGenerationPage
                playId="match3d"
                settingText={
                  match3dGenerationViewSession?.lastAssistantReply ??
                  '正在生成本局抓大鹅物品素材。'
                }
                anchorEntries={buildMatch3DGenerationAnchorEntries(
                  match3dGenerationViewSession,
                  match3dGenerationViewPayload,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  match3dGenerationViewState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isMatch3DGenerationViewBusy}
                error={match3dGenerationViewError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={() => {
                  setSelectionStage('match3d-agent-workspace');
                }}
                onRetry={retryMatch3DDraftGeneration}
                hideBatchModule
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-result' && match3dSession?.draft && (
          <motion.div
            key="match3d-result"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载抓大鹅结果..." />}
            >
              <Match3DResultView
                profile={
                  match3dProfile ??
                  buildMatch3DProfileFromSession(match3dSession)!
                }
                draft={match3dSession.draft}
                isBusy={isMatch3DBusy}
                error={match3dError}
                onBack={() => {
                  if (shouldReturnToDraftShelf()) {
                    leaveMatch3DFlow();
                    return;
                  }
                  returnToCreationCenterFromGeneration();
                }}
                onSaved={(profile) => {
                  setMatch3DProfile(normalizeMatch3DWorkForRuntimeUi(profile));
                }}
                onPublished={(profile) => {
                  const normalizedProfile =
                    normalizeMatch3DWorkForRuntimeUi(profile);
                  setMatch3DProfile(normalizedProfile);
                  void Promise.allSettled([
                    refreshMatch3DShelf(),
                    refreshMatch3DGallery(),
                  ]);
                  openPublicWorkDetail(
                    mapMatch3DWorkToPublicWorkDetail(normalizedProfile),
                  );
                  openPublishShareModal({
                    title: normalizedProfile.gameName,
                    publicWorkCode: buildMatch3DPublicWorkCode(
                      normalizedProfile.profileId,
                    ),
                    stage: 'work-detail',
                  });
                }}
                onStartTestRun={(profile, options) => {
                  const normalizedProfile =
                    normalizeMatch3DWorkForRuntimeUi(profile);
                  setMatch3DProfile(normalizedProfile);
                  void startMatch3DRunFromProfile(
                    normalizedProfile,
                    'match3d-result',
                    false,
                    options,
                  );
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'match3d-runtime' &&
          (() => {
            const activeMatch3DRuntimeProfile =
              resolveActiveMatch3DRuntimeProfile(
                match3dRun,
                match3dRuntimeProfile,
                match3dProfile,
              );
            return (
              <motion.div
                key="match3d-runtime"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                className="fixed inset-0 z-[100]"
              >
                <Suspense
                  fallback={<LazyPanelFallback label="正在加载抓大鹅玩法..." />}
                >
                  <Match3DRuntimeShell
                    run={match3dRun}
                    isBusy={isMatch3DBusy}
                    error={match3dError}
                    generatedItemAssets={resolveMatch3DRuntimeGeneratedItemAssets(
                      match3dRun,
                      activeMatch3DRuntimeProfile,
                      selectedPublicWorkDetail,
                    )}
                    generatedBackgroundAsset={resolveMatch3DRuntimeGeneratedBackgroundAsset(
                      match3dRun,
                      activeMatch3DRuntimeProfile,
                      selectedPublicWorkDetail,
                    )}
                    backgroundImageSrc={resolveMatch3DRuntimeBackgroundImageSrc(
                      match3dRun,
                      activeMatch3DRuntimeProfile,
                      selectedPublicWorkDetail,
                    )}
                    onBack={() => {
                      if (
                        match3dRun?.runId &&
                        match3dRun.status === 'running'
                      ) {
                        void resolveMatch3DRuntimeAdapter(
                          activeMatch3DRuntimeProfile?.profileId,
                        )
                          .stopRun(match3dRun.runId)
                          .catch(() => undefined);
                      }
                      setSelectionStage(match3dRuntimeReturnStage);
                    }}
                    onRestart={() => {
                      if (!match3dRun?.runId || isMatch3DBusy) {
                        return;
                      }

                      match3dFlow.setIsBusy(true);
                      setMatch3DError(null);
                      void resolveMatch3DRuntimeAdapter(
                        activeMatch3DRuntimeProfile?.profileId,
                      )
                        .restartRun(match3dRun.runId)
                        .then(({ run }) => {
                          setMatch3DRun(run);
                        })
                        .catch((error) => {
                          setMatch3DError(
                            resolveMatch3DErrorMessage(
                              error,
                              '重新开始抓大鹅玩法失败。',
                            ),
                          );
                        })
                        .finally(() => {
                          match3dFlow.setIsBusy(false);
                        });
                    }}
                    onOptimisticRunChange={setMatch3DRun}
                    onClickItem={(payload) => {
                      const runId = payload.runId ?? match3dRun?.runId;
                      if (!runId) {
                        return Promise.reject(
                          new Error('抓大鹅运行态缺少 runId。'),
                        );
                      }
                      return resolveMatch3DRuntimeAdapter(
                        activeMatch3DRuntimeProfile?.profileId,
                      ).clickItem(runId, payload);
                    }}
                    onTimeExpired={() => {
                      if (!match3dRun?.runId) {
                        return;
                      }

                      void resolveMatch3DRuntimeAdapter(
                        activeMatch3DRuntimeProfile?.profileId,
                      )
                        .finishTimeUp(match3dRun.runId)
                        .then(({ run }) => {
                          setMatch3DRun(run);
                        })
                        .catch((error) => {
                          setMatch3DError(
                            resolveMatch3DErrorMessage(
                              error,
                              '同步抓大鹅倒计时失败。',
                            ),
                          );
                        });
                    }}
                  />
                </Suspense>
              </motion.div>
            );
          })()}

        {selectionStage === 'baby-object-match-workspace' && (
          <motion.div
            key="baby-object-match-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载宝贝识物创作..." />}
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('baby-object-match')}
                onBack={leaveBabyObjectMatchFlow}
                isBackDisabled={isBabyObjectMatchBusy}
              >
                <BabyObjectMatchWorkspace
                  isBusy={isBabyObjectMatchBusy}
                  error={babyObjectMatchError}
                  onBack={leaveBabyObjectMatchFlow}
                  showBackButton={false}
                  title={null}
                  initialPayload={babyObjectMatchFormPayload}
                  onCreateDraft={(payload) => {
                    void createBabyObjectMatchDraftFromForm(payload);
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'baby-object-match-generating' && (
          <motion.div
            key="baby-object-match-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载宝贝识物生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  babyObjectMatchFormPayload
                    ? `${babyObjectMatchFormPayload.itemAName} / ${babyObjectMatchFormPayload.itemBName}`
                    : '正在整理当前宝贝识物草稿。'
                }
                anchorEntries={buildBabyObjectMatchGenerationAnchorEntries(
                  babyObjectMatchFormPayload,
                  babyObjectMatchDraft,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  babyObjectMatchGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={
                  isBabyObjectMatchBusy &&
                  babyObjectMatchGenerationPhase === 'generating'
                }
                error={babyObjectMatchError}
                onBack={leaveBabyObjectMatchFlow}
                onEditSetting={() => {
                  setSelectionStage('baby-object-match-workspace');
                }}
                onRetry={retryBabyObjectMatchDraftGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingDescription={null}
                progressTitle="宝贝识物草稿生成进度"
                activeBadgeLabel="草稿生成中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'baby-object-match-result' &&
          babyObjectMatchDraft && (
            <motion.div
              key="baby-object-match-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载宝贝识物结果..." />}
              >
                <BabyObjectMatchResultView
                  draft={babyObjectMatchDraft}
                  isBusy={isBabyObjectMatchBusy}
                  error={babyObjectMatchError}
                  onBack={() => {
                    if (shouldReturnToDraftShelf()) {
                      leaveBabyObjectMatchFlow();
                      return;
                    }
                    setSelectionStage('baby-object-match-workspace');
                  }}
                  onSaveDraft={(draft) => {
                    void saveBabyObjectMatchResultDraft(draft);
                  }}
                  onPublish={(draft) => {
                    void publishBabyObjectMatchResultDraft(draft);
                  }}
                  onRegenerateAssets={(draft) => {
                    void regenerateBabyObjectMatchResultAssets(draft);
                  }}
                  onStartTestRun={(draft) => {
                    void startBabyObjectMatchRuntimeFromDraft(
                      draft,
                      'baby-object-match-result',
                    );
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'baby-object-match-runtime' &&
          babyObjectMatchDraft && (
            <motion.div
              key="baby-object-match-runtime"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              className="fixed inset-0 z-[100]"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载宝贝识物玩法..." />}
              >
                <BabyObjectMatchRuntimeShell
                  draft={babyObjectMatchDraft}
                  onBack={() => {
                    setSelectionStage(babyObjectMatchRuntimeReturnStage);
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'baby-love-drawing-runtime' && (
          <motion.div
            key="baby-love-drawing-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载宝贝爱画..." />}
            >
              <BabyLoveDrawingRuntimeShell
                onBack={() => {
                  setSelectionStage('platform');
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'bark-battle-workspace' && (
          <motion.div
            key="bark-battle-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载汪汪声浪创作..." />}
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('bark-battle')}
                onBack={leaveBarkBattleFlow}
                isBackDisabled={isBarkBattleBusy}
              >
                <BarkBattleConfigEditor
                  isBusy={isBarkBattleBusy}
                  error={barkBattleError}
                  onBack={leaveBarkBattleFlow}
                  showBackButton={false}
                  title={null}
                  onPreview={(payload) => {
                    void createBarkBattleGeneratingDraft(payload);
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-agent-workspace' && (
          <motion.div
            key="square-hole-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载方洞挑战共创工作区..." />
              }
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('square-hole')}
                onBack={leaveSquareHoleFlow}
                isBackDisabled={isSquareHoleBusy || isStreamingSquareHoleReply}
              >
                <SquareHoleAgentWorkspace
                  session={squareHoleSession}
                  streamingReplyText={streamingSquareHoleReplyText}
                  isStreamingReply={isStreamingSquareHoleReply}
                  isBusy={isSquareHoleBusy || isStreamingSquareHoleReply}
                  error={squareHoleError}
                  onBack={leaveSquareHoleFlow}
                  showBackButton={false}
                  onSubmitMessage={(payload) => {
                    void submitSquareHoleMessage(payload);
                  }}
                  onExecuteAction={(payload) => {
                    void executeSquareHoleAction(payload);
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-generating' && (
          <motion.div
            key="square-hole-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载方洞挑战生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  squareHoleSession?.lastAssistantReply ??
                  '正在整理当前方洞挑战草稿。'
                }
                anchorEntries={buildSquareHoleGenerationAnchorEntries(
                  squareHoleSession,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  squareHoleGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isSquareHoleBusy}
                error={squareHoleError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={() => {
                  setSelectionStage('square-hole-agent-workspace');
                }}
                onRetry={retrySquareHoleAssetGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成图片"
                settingDescription={null}
                progressTitle="方洞挑战图片生成进度"
                activeBadgeLabel="图片生成中"
                pausedBadgeLabel="图片生成已暂停"
                idleBadgeLabel="等待返回结果页"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'square-hole-result' &&
          squareHoleSession?.draft && (
            <motion.div
              key="square-hole-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载方洞挑战结果..." />}
              >
                <SquareHoleResultView
                  profile={
                    squareHoleProfile ??
                    buildSquareHoleProfileFromSession(squareHoleSession)!
                  }
                  draft={squareHoleSession.draft}
                  isBusy={isSquareHoleBusy}
                  error={squareHoleError}
                  onBack={() => {
                    if (shouldReturnToDraftShelf()) {
                      leaveSquareHoleFlow();
                      return;
                    }
                    setSelectionStage('square-hole-agent-workspace');
                  }}
                  onSaved={(profile) => {
                    setSquareHoleProfile(profile);
                  }}
                  onPublished={(profile) => {
                    setSquareHoleProfile(profile);
                    void Promise.allSettled([
                      refreshSquareHoleShelf(),
                      refreshSquareHoleGallery(),
                    ]);
                    openPublicWorkDetail(
                      mapSquareHoleWorkToPublicWorkDetail(profile),
                    );
                    openPublishShareModal({
                      title: profile.gameName,
                      publicWorkCode: buildSquareHolePublicWorkCode(
                        profile.profileId,
                      ),
                      stage: 'work-detail',
                    });
                  }}
                  onStartTestRun={(profile) => {
                    setSquareHoleProfile(profile);
                    void startSquareHoleRunFromProfile(
                      profile,
                      'square-hole-result',
                    );
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'square-hole-runtime' && (
          <motion.div
            key="square-hole-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载方洞挑战玩法..." />}
            >
              <SquareHoleRuntimeShell
                run={squareHoleRun}
                isBusy={isSquareHoleBusy}
                error={squareHoleError}
                onBack={() => {
                  if (
                    squareHoleRun?.runId &&
                    squareHoleRun.status.toLowerCase() === 'running'
                  ) {
                    void stopSquareHoleRun(squareHoleRun.runId).catch(
                      () => undefined,
                    );
                  }
                  setSelectionStage(squareHoleRuntimeReturnStage);
                }}
                onRestart={() => {
                  if (!squareHoleRun?.runId || isSquareHoleBusy) {
                    return;
                  }

                  squareHoleFlow.setIsBusy(true);
                  setSquareHoleError(null);
                  void restartSquareHoleRun(squareHoleRun.runId)
                    .then(({ run }) => {
                      setSquareHoleRun(run);
                    })
                    .catch((error) => {
                      setSquareHoleError(
                        resolveSquareHoleErrorMessage(
                          error,
                          '重新开始方洞挑战失败。',
                        ),
                      );
                    })
                    .finally(() => {
                      squareHoleFlow.setIsBusy(false);
                    });
                }}
                onOptimisticRunChange={setSquareHoleRun}
                onDropShape={(payload) => {
                  const runId = payload.runId ?? squareHoleRun?.runId;
                  if (!runId) {
                    return Promise.reject(
                      new Error('方洞挑战运行态缺少 runId。'),
                    );
                  }
                  return dropSquareHoleShape(runId, payload);
                }}
                onTimeExpired={() => {
                  if (!squareHoleRun?.runId) {
                    return;
                  }

                  void finishSquareHoleTimeUp(squareHoleRun.runId)
                    .then(({ run }) => {
                      setSquareHoleRun(run);
                    })
                    .catch((error) => {
                      setSquareHoleError(
                        resolveSquareHoleErrorMessage(
                          error,
                          '同步方洞挑战倒计时失败。',
                        ),
                      );
                    });
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'jump-hop-workspace' && (
          <motion.div
            key="jump-hop-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载跳一跳创作..." />}
            >
              <UnifiedCreationWorkspace
                playId="jump-hop"
                spec={getUnifiedSpec('jump-hop')}
                isBusy={isJumpHopBusy}
                error={jumpHopError}
                onBack={leaveJumpHopFlow}
                onSubmitted={(result, payload) => {
                  void compileJumpHopSession(result, payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'jump-hop-generating' && (
          <motion.div
            key="jump-hop-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载跳一跳生成面板..." />}
            >
              <UnifiedGenerationPage
                playId="jump-hop"
                settingText={
                  jumpHopSession?.draft?.workTitle?.trim() ||
                  jumpHopSession?.draft?.workDescription?.trim() ||
                  '正在整理跳一跳草稿。'
                }
                anchorEntries={buildJumpHopGenerationAnchorEntries(
                  jumpHopSession,
                  null,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  jumpHopGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isJumpHopBusy}
                error={jumpHopError}
                onBack={leaveJumpHopFlow}
                onEditSetting={() => {
                  setSelectionStage('jump-hop-workspace');
                }}
                onRetry={retryJumpHopDraftGeneration}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'jump-hop-result' &&
          (() => {
            const activeJumpHopResultProfile =
              jumpHopWork ?? jumpHopSession?.draft ?? null;

            return (
              <motion.div
                key="jump-hop-result"
                initial={{ opacity: 0, y: 12 }}
                animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: -12 }}
                className="flex h-full min-h-0 flex-col"
              >
                {activeJumpHopResultProfile ? (
                  <Suspense
                    fallback={
                      <LazyPanelFallback label="正在加载跳一跳结果..." />
                    }
                  >
                    <JumpHopResultView
                      profile={activeJumpHopResultProfile}
                      error={jumpHopError}
                      onBack={leaveJumpHopFlow}
                      onEdit={() => {
                        setSelectionStage('jump-hop-workspace');
                      }}
                      onStartTestRun={startJumpHopTestRunFromProfile}
                      onPublish={publishJumpHopDraft}
                      onRegenerateTiles={() => {
                        void regenerateJumpHopAsset('regenerate-tiles');
                      }}
                    />
                  </Suspense>
                ) : (
                  <CreationResultRecoveryPanel
                    title="跳一跳草稿未恢复"
                    message="当前链接缺少可恢复的跳一跳草稿信息。"
                    actionLabel="返回创作"
                    onAction={() => {
                      setSelectionStage('jump-hop-workspace');
                    }}
                  />
                )}
              </motion.div>
            );
          })()}

        {selectionStage === 'jump-hop-runtime' && (
          <motion.div
            key="jump-hop-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载跳一跳玩法..." />}
            >
              <JumpHopRuntimeShell
                snapshot={jumpHopRun}
                profile={jumpHopWork}
                isBusy={isJumpHopBusy}
                error={jumpHopError}
                runtimeRequestOptions={jumpHopRuntimeRequestOptions ?? undefined}
                onBack={() => {
                  setSelectionStage(jumpHopRuntimeReturnStage);
                }}
                onRestart={() => {
                  void restartJumpHopRuntimeRun();
                }}
                onJump={submitJumpHopJumpAction}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-clear-workspace' && (
          <motion.div
            key="puzzle-clear-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼消消创作..." />}
            >
              <PuzzleClearWorkspace
                isBusy={isPuzzleClearBusy}
                error={puzzleClearError}
                onBack={leavePuzzleClearFlow}
                onSubmitted={(result, payload) => {
                  void compilePuzzleClearSession(result, payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-clear-generating' && (
          <motion.div
            key="puzzle-clear-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼消消生成面板..." />}
            >
              <CustomWorldGenerationView
                settingText={
                  puzzleClearSession?.draft?.workTitle?.trim() ||
                  puzzleClearSession?.draft?.themePrompt?.trim() ||
                  '正在整理拼消消草稿。'
                }
                anchorEntries={buildPuzzleClearGenerationAnchorEntries(
                  puzzleClearSession,
                  null,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  puzzleClearGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isPuzzleClearBusy}
                error={puzzleClearError}
                onBack={leavePuzzleClearFlow}
                onEditSetting={() => {
                  setSelectionStage('puzzle-clear-workspace');
                }}
                onRetry={retryPuzzleClearDraftGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingTitle="当前拼消消信息"
                settingDescription={null}
                progressTitle="拼消消草稿生成进度"
                activeBadgeLabel="素材生成中"
                pausedBadgeLabel="素材生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-clear-result' &&
          puzzleClearSession?.draft && (
            <motion.div
              key="puzzle-clear-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载拼消消结果..." />}
              >
                <PuzzleClearResultView
                  profile={puzzleClearWork ?? puzzleClearSession.draft}
                  isBusy={isPuzzleClearBusy}
                  error={puzzleClearError}
                  onBack={leavePuzzleClearFlow}
                  onEdit={() => {
                    setSelectionStage('puzzle-clear-workspace');
                  }}
                  onStartTestRun={startPuzzleClearTestRunFromProfile}
                  onPublish={publishPuzzleClearDraft}
                  onRegenerateAtlas={() => {
                    void regeneratePuzzleClearAtlas();
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'puzzle-clear-runtime' && (
          <motion.div
            key="puzzle-clear-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼消消玩法..." />}
            >
              <PuzzleClearRuntimeShell
                snapshot={puzzleClearRun}
                profile={puzzleClearWork}
                isBusy={isPuzzleClearBusy}
                error={puzzleClearError}
                onBack={() => {
                  setSelectionStage(puzzleClearRuntimeReturnStage);
                }}
                onRetryLevel={() => {
                  void retryPuzzleClearLevelRun();
                }}
                onNextLevel={() => {
                  void advancePuzzleClearLevelRun();
                }}
                onTimeUp={() => {
                  void markPuzzleClearLevelTimeUp();
                }}
                onSwapCards={async (payload) => {
                  await swapPuzzleClearCardsInRun(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'wooden-fish-workspace' && (
          <motion.div
            key="wooden-fish-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载敲木鱼创作..." />}
            >
              <UnifiedCreationWorkspace
                playId="wooden-fish"
                spec={getUnifiedSpec('wooden-fish')}
                isBusy={isWoodenFishBusy}
                error={woodenFishError}
                onBack={leaveWoodenFishFlow}
                onSubmitted={(result, payload) => {
                  void compileWoodenFishSession(result, payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'wooden-fish-generating' && (
          <motion.div
            key="wooden-fish-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载敲木鱼生成面板..." />}
            >
              <UnifiedGenerationPage
                playId="wooden-fish"
                settingText={
                  woodenFishSession?.draft?.workTitle?.trim() ||
                  woodenFishSession?.draft?.workDescription?.trim() ||
                  '正在整理敲木鱼草稿。'
                }
                anchorEntries={buildWoodenFishGenerationAnchorEntries(
                  woodenFishSession,
                  null,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  woodenFishGenerationState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isWoodenFishBusy}
                error={woodenFishError}
                onBack={leaveWoodenFishFlow}
                onEditSetting={() => {
                  setSelectionStage('wooden-fish-workspace');
                }}
                onRetry={retryWoodenFishDraftGeneration}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'wooden-fish-result' &&
          woodenFishSession?.draft && (
            <motion.div
              key="wooden-fish-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载敲木鱼结果..." />}
              >
                <WoodenFishResultView
                  profile={woodenFishWork ?? woodenFishSession.draft}
                  isBusy={isWoodenFishBusy}
                  error={woodenFishError}
                  onBack={leaveWoodenFishFlow}
                  onEdit={() => {
                    setSelectionStage('wooden-fish-workspace');
                  }}
                  onStartTestRun={startWoodenFishTestRunFromProfile}
                  onPublish={publishWoodenFishDraft}
                  onRegenerateHitObject={() => {
                    void regenerateWoodenFishAsset('regenerate-hit-object');
                  }}
                  onUpdateWorkMeta={updateWoodenFishWorkMeta}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'wooden-fish-runtime' && (
          <motion.div
            key="wooden-fish-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载敲木鱼玩法..." />}
            >
              <WoodenFishRuntimeShell
                snapshot={woodenFishRun}
                profile={woodenFishWork}
                isBusy={isWoodenFishBusy}
                error={woodenFishError}
                onBack={() => {
                  setSelectionStage(woodenFishRuntimeReturnStage);
                }}
                onCheckpoint={checkpointWoodenFishRuntimeRun}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'creative-agent-workspace' && (
          <motion.div
            key="creative-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载智能创作..." />}
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('creative-agent')}
                onBack={leaveCreativeAgentWorkspace}
                isBackDisabled={isCreativeAgentBusy || isPuzzleBusy}
              >
                <CreativeAgentWorkspace
                  session={creativeAgentSession}
                  isBusy={isCreativeAgentBusy || isPuzzleBusy}
                  isStreaming={isCreativeAgentStreaming}
                  error={creativeAgentError}
                  eventLog={creativeAgentEvents}
                  onBack={leaveCreativeAgentWorkspace}
                  showBackButton={false}
                  onSubmitMessage={(payload) => {
                    void submitCreativeAgentMessage(payload);
                  }}
                  onConfirmTemplate={(selection) => {
                    void confirmCreativeTemplateSelection(selection);
                  }}
                  onOpenTarget={() => {
                    void openCreativeAgentTarget();
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-agent-workspace' && (
          <motion.div
            key="puzzle-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图创作..." />}
            >
              <UnifiedCreationWorkspace
                playId="puzzle"
                spec={getUnifiedSpec('puzzle')}
                session={puzzleSession}
                isBusy={isStreamingPuzzleReply}
                error={puzzleError}
                onBack={leavePuzzleFlow}
                onSubmitMessage={(payload) => {
                  void submitPuzzleMessage(payload);
                }}
                onExecuteAction={(payload) => {
                  executePuzzleWorkspaceAction(payload);
                }}
                initialFormPayload={puzzleFormDraftPayload}
                onCreateFromForm={(payload) => {
                  void createPuzzleDraftFromForm(payload);
                }}
                onAutoSaveForm={(payload) => {
                  void savePuzzleFormDraft(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-onboarding' && (
          <motion.div
            key="puzzle-onboarding"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <PuzzleOnboardingView
              prompt={puzzleOnboardingPrompt}
              phase={puzzleOnboardingPhase}
              error={puzzleOnboardingError}
              copy={PUZZLE_ONBOARDING_COPY}
              onPromptChange={setPuzzleOnboardingPrompt}
              onSubmit={() => {
                void submitPuzzleOnboardingPrompt();
              }}
              onSkip={skipPuzzleOnboarding}
            />
          </motion.div>
        )}

        {selectionStage === 'puzzle-generating' && (
          <motion.div
            key="puzzle-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图生成面板..." />}
            >
              <UnifiedGenerationPage
                playId="puzzle"
                settingText={
                  puzzleGenerationViewSession?.lastAssistantReply ??
                  '正在整理当前拼图草稿。'
                }
                anchorEntries={buildPuzzleGenerationAnchorEntries(
                  puzzleGenerationViewSession,
                  puzzleGenerationViewPayload,
                )}
                progress={buildMiniGameDraftGenerationProgress(
                  puzzleGenerationViewState,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isPuzzleGenerationViewBusy}
                error={puzzleGenerationViewError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={() => {
                  setSelectionStage('puzzle-agent-workspace');
                }}
                onRetry={retryPuzzleDraftGeneration}
                hideBatchModule
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-result' &&
          puzzleSession?.draft &&
          !isPuzzleFormOnlyDraft(puzzleSession) && (
            <motion.div
              key="puzzle-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载拼图结果..." />}
              >
                <PuzzleResultView
                  session={puzzleSession}
                  profileId={
                    puzzleSession.publishedProfileId ??
                    buildPuzzleResultProfileId(puzzleSession.sessionId)
                  }
                  isBusy={isPuzzleBusy}
                  error={puzzleError}
                  onBack={leavePuzzleFlow}
                  onExecuteAction={(payload) => {
                    executePuzzleWorkspaceAction(payload);
                  }}
                  onStartTestRun={startPuzzleTestRunFromDraft}
                  creativeDraftEdit={
                    activeCreativeAgentSessionId
                      ? {
                          isBusy: isCreativeDraftEditBusy,
                          error: creativeDraftEditError,
                          onSubmit: submitCreativeDraftEdit,
                        }
                      : null
                  }
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'visual-novel-agent-workspace' && (
          <motion.div
            key="visual-novel-agent-workspace"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载视觉小说创作..." />}
            >
              <UnifiedCreationPage
                spec={getUnifiedSpec('visual-novel')}
                onBack={leaveVisualNovelFlow}
                isBackDisabled={isVisualNovelBusy || isVisualNovelStreamingReply}
              >
                <VisualNovelAgentWorkspace
                  session={visualNovelSession}
                  isBusy={isVisualNovelBusy || isVisualNovelStreamingReply}
                  error={visualNovelError}
                  onBack={leaveVisualNovelFlow}
                  showBackButton={false}
                  title={null}
                  initialFormPayload={visualNovelFormDraftPayload}
                  onCreateFromForm={(payload) => {
                    void createVisualNovelDraftFromForm(payload);
                  }}
                />
              </UnifiedCreationPage>
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'visual-novel-generating' && (
          <motion.div
            key="visual-novel-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={
                <LazyPanelFallback label="正在加载视觉小说生成面板..." />
              }
            >
              <CustomWorldGenerationView
                settingText={
                  visualNovelFormDraftPayload?.seedText ??
                  visualNovelSession?.messages.find(
                    (message) => message.role === 'user',
                  )?.text ??
                  '正在整理当前视觉小说草稿。'
                }
                anchorEntries={buildVisualNovelEntryGenerationAnchorEntries(
                  visualNovelFormDraftPayload,
                )}
                progress={buildVisualNovelEntryGenerationProgress(
                  visualNovelGenerationStartedAtMs,
                  visualNovelGenerationPhase,
                  miniGameGenerationProgressNowMs,
                )}
                isGenerating={isVisualNovelBusy || isVisualNovelStreamingReply}
                error={visualNovelError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={() => {
                  setSelectionStage('visual-novel-agent-workspace');
                }}
                onRetry={retryVisualNovelDraftGeneration}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="重新生成草稿"
                settingDescription={null}
                progressTitle="视觉小说草稿生成进度"
                activeBadgeLabel="草稿生成中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'visual-novel-result' &&
          (visualNovelSession?.draft || visualNovelWork?.draft) && (
            <motion.div
              key="visual-novel-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载视觉小说结果..." />}
              >
                <VisualNovelResultView
                  draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
                  isBusy={isVisualNovelBusy}
                  error={visualNovelError}
                  onBack={() => {
                    if (shouldReturnToDraftShelf()) {
                      leaveVisualNovelFlow();
                      return;
                    }
                    setSelectionStage('visual-novel-agent-workspace');
                  }}
                  onSaveDraft={(draft) => {
                    void saveVisualNovelDraft(draft);
                  }}
                  onCompileWorkProfile={(draft) => {
                    void compileVisualNovelDraft(draft);
                  }}
                  onStartTestRun={(draft) => {
                    void startVisualNovelTestRunFromDraft(draft);
                  }}
                  onPublish={(draft) => {
                    void publishVisualNovelDraft(draft);
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'visual-novel-runtime' && (
          <motion.div
            key="visual-novel-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载视觉小说试玩..." />}
            >
              <VisualNovelRuntimeShell
                draft={visualNovelWork?.draft ?? visualNovelSession?.draft}
                run={visualNovelRun}
                isBusy={isVisualNovelBusy}
                error={visualNovelError}
                onBack={() => {
                  setSelectionStage(visualNovelRuntimeReturnStage);
                }}
                onSubmitAction={(payload) => {
                  void submitVisualNovelRuntimeAction(payload);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'visual-novel-gallery-detail' &&
          visualNovelWork && (
            <motion.div
              key="visual-novel-gallery-detail"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载视觉小说详情..." />}
              >
                <VisualNovelResultView
                  draft={visualNovelWork.draft}
                  isBusy={isVisualNovelBusy}
                  error={visualNovelError}
                  onBack={() => {
                    setSelectionStage('platform');
                  }}
                  onStartTestRun={(draft) => {
                    setVisualNovelRuntimeReturnStage(
                      'visual-novel-gallery-detail',
                    );
                    void startVisualNovelTestRunFromDraft(draft);
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'puzzle-gallery-detail' && selectedPuzzleDetail && (
          <motion.div
            key="puzzle-gallery-detail"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图详情..." />}
            >
              <PuzzleGalleryDetailView
                item={selectedPuzzleDetail}
                isBusy={isPuzzleBusy}
                error={puzzleError}
                onBack={() => {
                  platformBootstrap.setPlatformTab(
                    puzzleDetailReturnTarget?.tab ?? 'home',
                  );
                  setPuzzleDetailReturnTarget(null);
                  setSelectionStage('platform');
                }}
                onEdit={
                  selectedPuzzleDetail.ownerUserId === authUi?.user?.id &&
                  Boolean(selectedPuzzleDetail.sourceSessionId?.trim())
                    ? () => {
                        runProtectedAction(() => {
                          void openPuzzleDraft(selectedPuzzleDetail);
                        });
                      }
                    : null
                }
                onStartGame={() => {
                  void startPuzzleRunFromProfile(
                    selectedPuzzleDetail.profileId,
                    'puzzle-gallery-detail',
                    selectedPuzzleDetail,
                    false,
                    null,
                    { authMode: 'isolated' },
                  );
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'puzzle-runtime' && (
          <motion.div
            key="puzzle-runtime"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-[100]"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载拼图玩法..." />}
            >
              <PuzzleRuntimeShell
                run={puzzleRun}
                isBusy={
                  isPuzzleBusy ||
                  isPuzzleNextLevelGenerating ||
                  isPuzzleLeaderboardBusy
                }
                error={puzzleError}
                hideBackButton={Boolean(puzzleOnboardingDraft)}
                onBack={() => {
                  const returnStage = puzzleRuntimeReturnStage;
                  setSelectionStage(returnStage);
                  if (returnStage === 'puzzle-result') {
                    writeCreationUrlState(
                      buildPuzzleCreationUrlState(puzzleSession),
                    );
                  }
                }}
                onRemodelWork={
                  selectedPuzzleDetail?.publicationStatus === 'published'
                    ? remodelCurrentPuzzleRuntimeWork
                    : undefined
                }
                onSwapPieces={(payload) => {
                  void swapPuzzlePiecesInRun(payload);
                }}
                onDragPiece={(payload) => {
                  void dragPuzzlePiece(payload);
                }}
                onAdvanceNextLevel={(target) => {
                  void advancePuzzleLevel(target);
                }}
                onRestartLevel={() => {
                  void restartPuzzleCurrentLevel();
                }}
                onPauseChange={setPuzzleRuntimePaused}
                onUseProp={usePuzzleProp}
                onTimeExpired={syncPuzzleRuntimeTimeout}
              />
            </Suspense>
            {isPuzzleNextLevelGenerating ? (
              <div className="fixed inset-0 z-[120] flex items-center justify-center bg-slate-950/62 px-5 backdrop-blur-sm">
                <div className="flex max-w-[18rem] flex-col items-center gap-3 rounded-[1.5rem] border border-white/12 bg-slate-950/92 px-6 py-5 text-center text-white shadow-[0_28px_80px_rgba(0,0,0,0.35)]">
                  <Loader2 className="h-6 w-6 animate-spin text-amber-200" />
                  <div className="text-sm font-bold">正在准备下一关</div>
                  <div className="text-xs leading-5 text-white/68">
                    广场暂无可接续作品，正在生成新的候选图。
                  </div>
                </div>
              </div>
            ) : null}
            {puzzleOnboardingDraft &&
            puzzleRun?.currentLevel?.status === 'cleared' ? (
              <PuzzleOnboardingLoginOverlay
                isSaving={isPuzzleOnboardingSaving}
                error={puzzleOnboardingError}
                copy={PUZZLE_ONBOARDING_CLEAR_COPY}
                onLogin={requestPuzzleOnboardingLogin}
              />
            ) : null}
          </motion.div>
        )}

        {selectionStage === 'custom-world-generating' && (
          <motion.div
            key="custom-world-generating"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载世界生成面板..." />}
            >
              <CustomWorldGenerationView
                settingText={sessionController.agentDraftSettingPreview}
                anchorEntries={sessionController.agentDraftAnchorPreviewEntries}
                progress={sessionController.agentDraftGenerationProgress}
                isGenerating={sessionController.isActiveGenerationRunning}
                error={sessionController.activeGenerationError}
                onBack={returnToCreationCenterFromGeneration}
                onEditSetting={leaveAgentDraftGeneration}
                onRetry={() => {
                  void sessionController.executeAgentAction({
                    action: 'draft_foundation',
                  });
                }}
                onInterrupt={undefined}
                backLabel="返回创作中心"
                settingActionLabel={null}
                retryLabel="继续生成草稿"
                settingDescription={null}
                progressTitle="世界草稿生成进度"
                activeBadgeLabel="草稿编译中"
                pausedBadgeLabel="草稿生成已暂停"
                idleBadgeLabel="等待返回工作区"
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'bark-battle-generating' &&
          barkBattleDraftConfig && (
            <motion.div
              key="bark-battle-generating"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在生成汪汪声浪素材..." />}
              >
                <BarkBattleGeneratingView
                  draft={barkBattleDraftConfig}
                  isBusy={isBarkBattleBusy}
                  error={barkBattleError}
                  onBack={() => {
                    enterCreateTab();
                    selectionStageRef.current = 'platform';
                    setSelectionStage('platform');
                  }}
                  onComplete={handleBarkBattleGenerationComplete}
                  onError={setBarkBattleError}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'bark-battle-result' && barkBattleDraftConfig && (
          <motion.div
            key="bark-battle-result"
            initial={{ opacity: 0, y: 12 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -12 }}
            className="flex h-full min-h-0 flex-col"
          >
            <Suspense
              fallback={<LazyPanelFallback label="正在加载汪汪声浪草稿..." />}
            >
              <BarkBattleResultView
                draft={barkBattleDraftConfig}
                isBusy={isBarkBattleBusy}
                error={
                  barkBattleGenerationPartialFailed
                    ? (barkBattleError ??
                      '部分素材未生成成功，可以在对应槽位重新生成或上传。')
                    : barkBattleError
                }
                onBack={() => {
                  if (shouldReturnToDraftShelf()) {
                    leaveBarkBattleFlow();
                    return;
                  }
                  enterCreateTab();
                  selectionStageRef.current = 'platform';
                  setSelectionStage('platform');
                }}
                onDraftChange={(draft) => {
                  void handleBarkBattleDraftChange(draft);
                }}
                onStartTestRun={testBarkBattleDraft}
                onPublish={(draft) => {
                  void publishBarkBattleDraft(draft);
                }}
              />
            </Suspense>
          </motion.div>
        )}

        {selectionStage === 'bark-battle-runtime' &&
          barkBattlePublishedConfig && (
            <motion.div
              key="bark-battle-runtime"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载汪汪声浪试玩..." />}
              >
                <BarkBattleRuntimeShell
                  title={barkBattlePublishedConfig.title}
                  workId={barkBattlePublishedConfig.workId}
                  publishedConfig={barkBattlePublishedConfig}
                  runtimeMode={barkBattleRuntimeMode}
                  onExit={() => {
                    if (
                      barkBattleRuntimeReturnStage === 'bark-battle-result' &&
                      barkBattleDraftConfig
                    ) {
                      setSelectionStage('bark-battle-result');
                    } else {
                      enterCreateTab();
                      setSelectionStage('platform');
                    }
                  }}
                />
              </Suspense>
            </motion.div>
          )}

        {selectionStage === 'custom-world-result' &&
          sessionController.generatedCustomWorldProfile && (
            <motion.div
              key="custom-world-result"
              initial={{ opacity: 0, y: 12 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -12 }}
              className="flex h-full min-h-0 flex-col"
            >
              <Suspense
                fallback={<LazyPanelFallback label="正在加载世界编辑器..." />}
              >
                <RpgCreationResultView
                  profile={sessionController.generatedCustomWorldProfile}
                  previewCharacters={previewCustomWorldCharacters}
                  isGenerating={false}
                  progress={0}
                  progressLabel=""
                  error={resultViewError}
                  onProfileChange={(profile) => {
                    sessionController.setGeneratedCustomWorldProfile(profile);
                  }}
                  onBack={
                    sessionController.isAgentDraftResultView
                      ? () => {
                          leaveAgentDraftResult();
                        }
                      : leaveCustomWorldResult
                  }
                  onEditSetting={undefined}
                  onRegenerate={undefined}
                  onContinueExpand={undefined}
                  onEnterWorld={() => {
                    runProtectedAction(() => {
                      void enterWorldCoordinator
                        .enterWorldFromCurrentResult()
                        .catch((error) => {
                          sessionController.setCustomWorldError(
                            resolveRpgCreationErrorMessage(
                              error,
                              '发布并进入世界失败。',
                            ),
                          );
                        });
                    });
                  }}
                  onTestWorld={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? () => {
                          runProtectedAction(() => {
                            void enterWorldCoordinator
                              .enterWorldForTestFromCurrentResult()
                              .catch((error) => {
                                sessionController.setCustomWorldError(
                                  resolveRpgCreationErrorMessage(
                                    error,
                                    '进入作品测试失败。',
                                  ),
                                );
                              });
                          });
                        }
                      : undefined
                  }
                  onPublishWorld={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? async () => {
                          try {
                            const publishedProfile =
                              await enterWorldCoordinator.publishCurrentResult();
                            void openRpgPublishShareModal(publishedProfile);
                          } catch (error) {
                            sessionController.setCustomWorldError(
                              resolveRpgCreationErrorMessage(
                                error,
                                '发布到广场失败。',
                              ),
                            );
                            throw error;
                          }
                        }
                      : undefined
                  }
                  onGenerateEntity={
                    sessionController.isAgentDraftResultView
                      ? async (kind) => {
                          const action =
                            kind === 'landmark'
                              ? 'generate_landmarks'
                              : 'generate_characters';
                          await autosaveCoordinator.executeAgentActionAndWait({
                            action,
                            count: 1,
                            ...(kind === 'playable'
                              ? { roleType: 'playable' as const }
                              : kind === 'story'
                                ? { roleType: 'story' as const }
                                : {}),
                          });
                          const latestView =
                            sessionController.activeAgentSessionId
                              ? await sessionController.syncAgentCreationResultView(
                                  sessionController.activeAgentSessionId,
                                )
                              : null;
                          const latestProfile =
                            rpgCreationPreviewAdapter.buildPreviewFromResultView(
                              latestView,
                            );
                          if (latestProfile) {
                            sessionController.setGeneratedCustomWorldProfile(
                              latestProfile,
                            );
                          }
                          return { profile: latestProfile };
                        }
                      : undefined
                  }
                  onDeleteEntities={
                    sessionController.isAgentDraftResultView
                      ? async (kind, ids) => {
                          if (ids.length === 0) return;
                          await autosaveCoordinator.executeAgentActionAndWait(
                            kind === 'story'
                              ? { action: 'delete_characters', roleIds: ids }
                              : { action: 'delete_landmarks', sceneIds: ids },
                          );
                          const latestView =
                            sessionController.activeAgentSessionId
                              ? await sessionController.syncAgentCreationResultView(
                                  sessionController.activeAgentSessionId,
                                )
                              : null;
                          const latestProfile =
                            rpgCreationPreviewAdapter.buildPreviewFromResultView(
                              latestView,
                            );
                          if (latestProfile) {
                            sessionController.setGeneratedCustomWorldProfile(
                              latestProfile,
                            );
                          }
                        }
                      : undefined
                  }
                  readOnly={false}
                  compactAgentResultMode={
                    sessionController.isAgentDraftResultView
                  }
                  backLabel={
                    sessionController.isAgentDraftResultView
                      ? '返回创作'
                      : undefined
                  }
                  editActionLabel="继续调整设定"
                  enterWorldActionLabel={
                    sessionController.isAgentDraftResultView &&
                    sessionController.agentSession?.stage !== 'published'
                      ? '发布并进入世界'
                      : '进入世界'
                  }
                  publishReady={
                    sessionController.isAgentDraftResultView
                      ? agentResultPublishGateView.publishReady
                      : true
                  }
                  publishBlockers={
                    sessionController.isAgentDraftResultView
                      ? agentResultPublishGateView.blockers
                      : []
                  }
                  qualityFindings={
                    sessionController.isAgentDraftResultView
                      ? agentResultPreviewQualityFindings
                      : []
                  }
                  previewSourceLabel={
                    sessionController.isAgentDraftResultView
                      ? agentResultPreviewSourceLabel
                      : null
                  }
                  autoSaveState={autosaveCoordinator.customWorldAutoSaveState}
                />
              </Suspense>
            </motion.div>
          )}
      </AnimatePresence>

      {creationEntryConfig ? (
        <PlatformEntryCreationTypeModal
          isOpen={showCreationTypeModal}
          isBusy={
            sessionController.isCreatingAgentSession ||
            isCreativeAgentBusy ||
            isCreativeAgentStreaming ||
            isBigFishBusy ||
            isMatch3DBusy ||
            isSquareHoleBusy ||
            isPuzzleBusy ||
            isJumpHopBusy ||
            isWoodenFishBusy ||
            (isVisualNovelCreationOpen && isVisualNovelBusy) ||
            (isVisualNovelCreationOpen && isVisualNovelStreamingReply) ||
            isBabyObjectMatchBusy
          }
          entryConfig={creationEntryConfig}
          creationTypes={creationEntryTypes}
          onClose={() => {
            if (
              sessionController.isCreatingAgentSession ||
              isCreativeAgentBusy ||
              isCreativeAgentStreaming ||
              isBigFishBusy ||
              isMatch3DBusy ||
              isSquareHoleBusy ||
              isPuzzleBusy ||
              isJumpHopBusy ||
              isWoodenFishBusy ||
              (isVisualNovelCreationOpen && isVisualNovelBusy) ||
              (isVisualNovelCreationOpen && isVisualNovelStreamingReply) ||
              isBabyObjectMatchBusy
            ) {
              return;
            }
            setShowCreationTypeModal(false);
          }}
          onSelectRpg={() => {
            runProtectedAction(() => {
              void sessionController.openRpgAgentWorkspace();
            });
          }}
          onSelectBigFish={() => {
            runProtectedAction(() => {
              void openBigFishAgentWorkspace();
            });
          }}
          onSelectMatch3D={() => {
            handleCreationHubCreateType('match3d');
          }}
          onSelectSquareHole={() => {
            runProtectedAction(() => {
              void openSquareHoleAgentWorkspace();
            });
          }}
          onSelectPuzzle={() => {
            handleCreationHubCreateType('puzzle');
          }}
          onSelectJumpHop={() => {
            handleCreationHubCreateType('jump-hop');
          }}
          onSelectWoodenFish={() => {
            handleCreationHubCreateType('wooden-fish');
          }}
          onSelectCreativeAgent={() => {
            runProtectedAction(() => {
              void openCreativeAgentWorkspace();
            });
          }}
          onSelectBarkBattle={() => {
            handleCreationHubCreateType('bark-battle');
          }}
          onSelectVisualNovel={() => {
            handleCreationHubCreateType('visual-novel');
          }}
          onSelectBabyObjectMatch={() => {
            handleCreationHubCreateType('baby-object-match');
          }}
        />
      ) : null}
      <UnifiedModal
        open={Boolean(draftGenerationPointNotice)}
        title={draftGenerationPointNotice?.title ?? '泥点提示'}
        description={draftGenerationPointNoticeDescription}
        onClose={() => setDraftGenerationPointNotice(null)}
        closeOnBackdrop
        size="sm"
        overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
        panelClassName="platform-remap-surface rounded-[1.75rem]"
        footer={
          <button
            type="button"
            onClick={() => setDraftGenerationPointNotice(null)}
            className="platform-button platform-button--primary min-h-0 rounded-full px-4 py-2 text-sm"
          >
            知道了
          </button>
        }
      >
        <div className="text-sm leading-6 text-[var(--platform-text-base)]">
          {draftGenerationPointNotice?.message}
        </div>
      </UnifiedModal>
      <PublishShareModal
        open={Boolean(publishSharePayload)}
        payload={publishSharePayload}
        onClose={() => setPublishSharePayload(null)}
      />
      <PlatformErrorDialog
        error={activePlatformErrorDialog}
        onClose={closePlatformErrorDialog}
        overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
        panelClassName="platform-remap-surface rounded-[1.5rem]"
      />
      <PlatformTaskCompletionDialog
        completion={activePlatformTaskCompletionDialog}
        onClose={closePlatformTaskCompletionDialog}
        overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
        panelClassName="platform-remap-surface rounded-[1.5rem]"
      />
      <UnifiedModal
        open={Boolean(pendingDeleteCreationWork)}
        title="删除作品"
        description={
          pendingDeleteCreationWork
            ? `确认删除《${pendingDeleteCreationWork.title}》吗？`
            : undefined
        }
        onClose={closeDeleteCreationWorkConfirmation}
        closeDisabled={Boolean(deletingCreationWorkId)}
        closeOnBackdrop={!deletingCreationWorkId}
        size="sm"
        overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
        panelClassName="platform-remap-surface rounded-[1.75rem]"
        footer={
          <>
            <button
              type="button"
              onClick={closeDeleteCreationWorkConfirmation}
              disabled={Boolean(deletingCreationWorkId)}
              className="platform-button platform-button--ghost min-h-0 rounded-full px-4 py-2 text-sm"
            >
              取消
            </button>
            <button
              type="button"
              onClick={confirmDeleteCreationWork}
              disabled={Boolean(deletingCreationWorkId)}
              className="platform-button platform-button--danger min-h-0 rounded-full px-4 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-55"
            >
              {deletingCreationWorkId ? '删除中' : '确认删除'}
            </button>
          </>
        }
      >
        <div className="text-sm leading-6 text-[var(--platform-text-base)]">
          {pendingDeleteCreationWork?.detail}
        </div>
      </UnifiedModal>
      <AnimatePresence>
        {(searchedPublicUser || publicSearchError) && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/45 p-4"
          >
            <div className="platform-surface w-full max-w-md rounded-[1.6rem] p-5">
              <div className="flex items-start justify-between gap-4">
                <div>
                  <div className="text-sm font-semibold text-[var(--platform-text-soft)]">
                    公开编号搜索
                  </div>
                  <div className="mt-1 text-xl font-black text-[var(--platform-text-strong)]">
                    {publicSearchError ? '未找到结果' : '命中用户'}
                  </div>
                </div>
                <button
                  type="button"
                  onClick={() => {
                    setSearchedPublicUser(null);
                    setPublicSearchError(null);
                  }}
                  className="platform-icon-button"
                  aria-label="关闭搜索结果"
                >
                  ×
                </button>
              </div>
              {publicSearchError ? (
                <div className="mt-4 text-sm leading-6 text-[var(--platform-text-soft)]">
                  {publicSearchError}
                </div>
              ) : searchedPublicUser ? (
                <div className="mt-4 rounded-[1.2rem] border border-[var(--platform-line-soft)] p-4">
                  <div className="text-lg font-bold text-[var(--platform-text-strong)]">
                    {searchedPublicUser.displayName}
                  </div>
                  <div className="mt-2 text-sm text-[var(--platform-text-soft)]">
                    陶泥号 {searchedPublicUser.publicUserCode}
                  </div>
                </div>
              ) : null}
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}

export default PlatformEntryFlowShellImpl;
