/* @vitest-environment jsdom */

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, expect, test, vi } from 'vitest';

import {
  AnimationState,
  WorldType,
  type GameState,
  type StoryMoment,
} from '../../types';
import { RpgRuntimeShell } from './RpgRuntimeShell';
import type { RpgRuntimeShellProps } from './types';

const noop = () => {};
const asyncFalse = async () => false;

vi.mock('../auth/AuthUiContext', () => ({
  useAuthUi: () => null,
}));

vi.mock('./useRpgRuntimeShellViewModel', () => ({
  useRpgRuntimeShellViewModel: () => ({
    selectionStage: 'platform',
    setSelectionStage: () => {},
    overlayPanel: null,
    openOverlayPanel: () => {},
    closeOverlayPanel: () => {},
    selectedSceneEntity: null,
    setSelectedSceneEntity: () => {},
    openPartyMemberDetails: () => {},
    closeAdventureEntityModal: () => {},
    showTeamModal: false,
    openCampModal: () => {},
    closeCampModal: () => {},
    resetForSaveAndExit: () => {},
    shouldMountAdventureEntityModal: false,
    shouldMountCampModal: false,
    shouldMountMapModal: false,
    shouldMountCharacterChatModal: false,
    shouldMountNpcModals: false,
    visibleGameState: mockVisibleGameState,
    visibleCurrentStory: {
      text: '测试故事',
      options: [],
    },
    sceneTransitionPhase: 'idle',
    sceneTransitionToken: 0,
    setSceneTransitionDurations: () => {},
    isCharacterSelectionStage: false,
    shouldHideStoryOptions: false,
    hideSelectionHero: false,
    dialogueIndicator: {
      showPlayer: false,
      showEncounter: false,
      activeSpeaker: null,
    },
    characterChatSummaries: {},
    canvasCompanionRenderStates: [],
    adventureStatistics: {
      playTimeMs: 0,
      hostileNpcsDefeated: 0,
      questsAccepted: 0,
      questsCompleted: 0,
      questsTurnedIn: 0,
      itemsUsed: 0,
      scenesTraveled: 0,
      currentSceneName: '测试场景',
      playerCurrency: 0,
      inventoryItemCount: 0,
      inventoryStackCount: 0,
      activeCompanionCount: 0,
      rosterCompanionCount: 0,
    },
    handleSceneTransitionChoice: () => {},
  }),
}));

vi.mock('./RpgRuntimeCanvasStage', () => ({
  RpgRuntimeCanvasStage: () => <div>画布舞台</div>,
}));

vi.mock('./RpgRuntimeOverlayHost', () => ({
  RpgRuntimeOverlayHost: () => null,
}));

vi.mock('./RpgRuntimeStageRouter', () => ({
  RpgRuntimeStageRouter: () => <div>运行时主面板</div>,
}));

let mockVisibleGameState: GameState;

function createGameState(
  runtimeMode: GameState['runtimeMode'],
  runtimePersistenceDisabled?: boolean,
): GameState {
  return {
    worldType: WorldType.CUSTOM,
    customWorldProfile: null,
    playerCharacter: {
      id: 'player-1',
      name: '测试角色',
      title: '测试者',
      description: '测试角色',
      backstory: '测试背景',
      personality: '冷静',
      avatar: '',
      portrait: '',
      assetFolder: '',
      assetVariant: '',
      attributes: {
        strength: 5,
        agility: 5,
        intelligence: 5,
        spirit: 5,
      },
      backstoryReveal: {
        publicSummary: '测试',
        privateChatUnlockAffinity: 60,
        chapters: [],
      },
      skills: [],
      adventureOpenings: {},
    },
    runtimeMode,
    runtimePersistenceDisabled: runtimePersistenceDisabled ?? false,
    runtimeStats: {
      playTimeMs: 0,
      lastPlayTickAt: null,
      hostileNpcsDefeated: 0,
      questsAccepted: 0,
      itemsUsed: 0,
      scenesTraveled: 0,
    },
    playerProgression: {
      level: 1,
      currentLevelXp: 0,
      totalXp: 0,
      xpToNextLevel: 100,
    },
    currentScene: 'Story',
    storyHistory: [],
    characterChats: {},
    animationState: AnimationState.IDLE,
    currentEncounter: null,
    npcInteractionActive: false,
    currentScenePreset: null,
    sceneHostileNpcs: [],
    playerX: 0,
    playerOffsetY: 0,
    playerFacing: 'right',
    playerActionMode: 'idle',
    scrollWorld: false,
    inBattle: false,
    playerHp: 100,
    playerMaxHp: 100,
    playerMana: 50,
    playerMaxMana: 50,
    playerSkillCooldowns: {},
    activeCombatEffects: [],
    playerCurrency: 0,
    playerInventory: [],
    playerEquipment: {
      weapon: null,
      armor: null,
      relic: null,
    },
    npcStates: {},
    quests: [],
    roster: [],
    companions: [],
    currentBattleNpcId: null,
    currentNpcBattleMode: null,
    currentNpcBattleOutcome: null,
    sparReturnEncounter: null,
    sparPlayerHpBefore: null,
    sparPlayerMaxHpBefore: null,
    sparStoryHistoryBefore: null,
  };
}

function buildProps(
  runtimeMode: GameState['runtimeMode'],
  runtimePersistenceDisabled?: boolean,
): RpgRuntimeShellProps {
  const gameState = createGameState(runtimeMode, runtimePersistenceDisabled);
  const currentStory: StoryMoment = {
    text: '测试故事',
    options: [],
  };
  mockVisibleGameState = gameState;
  return {
    session: {
      gameState,
      currentStory,
      isLoading: false,
      aiError: null,
      bottomTab: 'adventure',
      setBottomTab: () => {},
      isMapOpen: false,
      setIsMapOpen: () => {},
    },
    story: {
      displayedOptions: [],
      canRefreshOptions: false,
      handleRefreshOptions: () => {},
      handleChoice: () => {},
      handleNpcChatInput: () => false,
      refreshNpcChatOptions: () => false,
      exitNpcChat: () => false,
      handleMapTravelToScene: () => false,
      npcUi: {
        tradeModal: null,
        giftModal: null,
        recruitModal: null,
        setTradeMode: noop,
        selectTradeNpcItem: noop,
        selectTradePlayerItem: noop,
        setTradeQuantity: noop,
        closeTradeModal: noop,
        confirmTrade: noop,
        selectGiftItem: noop,
        closeGiftModal: noop,
        confirmGift: noop,
        selectRecruitRelease: noop,
        closeRecruitModal: noop,
        confirmRecruit: noop,
      },
      characterChatUi: {
        modal: null,
        openChat: noop,
        closeChat: noop,
        setDraft: noop,
        useSuggestion: noop,
        refreshSuggestions: noop,
        sendDraft: noop,
      },
      inventoryUi: {
        useInventoryItem: asyncFalse,
        equipInventoryItem: asyncFalse,
        unequipItem: asyncFalse,
        playerCurrency: 0,
        currencyText: '0',
        backpackItems: [],
        equipmentSlots: [],
        forgeRecipes: [],
        craftRecipe: asyncFalse,
        dismantleItem: asyncFalse,
        reforgeItem: asyncFalse,
      },
      battleRewardUi: {
        reward: null,
        dismiss: noop,
      },
      questUi: {
        acknowledgeQuestCompletion: noop,
        claimQuestReward: () => null,
      },
      npcChatQuestOfferUi: {
        replacePendingOffer: () => false,
        abandonPendingOffer: () => false,
        acceptPendingOffer: () => null,
      },
      goalUi: {
        goalStack: {
          northStarGoal: null,
          activeGoal: null,
          immediateStepGoal: null,
          supportGoals: [],
        },
        pulse: null,
        dismissPulse: noop,
      },
    },
    entry: {
      hasSavedGame: false,
      savedSnapshot: null,
      handleContinueGame: () => {},
      handleStartNewGame: () => {},
      handleSaveAndExit: () => {},
      handleCustomWorldSelect: () => {},
      handleBackToWorldSelect: () => {},
      handleCharacterSelect: () => {},
    },
    companions: {
      companionRenderStates: [],
      buildCompanionRenderStates: () => [],
      onBenchCompanion: () => {},
      onActivateRosterCompanion: () => {},
    },
    audio: {
      musicVolume: 0.5,
      onMusicVolumeChange: () => {},
    },
  };
}

beforeEach(() => {
  mockVisibleGameState = createGameState('play');
});

test('结果页测试入口可显示结束测试按钮并触发退出回调', async () => {
  const user = userEvent.setup();
  const onExitRuntimePreview = vi.fn();

  render(
    <RpgRuntimeShell
      {...buildProps('play', true)}
      onExitRuntimePreview={onExitRuntimePreview}
      showRuntimePreviewExit
    />,
  );

  await user.click(screen.getByRole('button', { name: '结束测试' }));

  expect(onExitRuntimePreview).toHaveBeenCalledTimes(1);
});

test('正式运行态不显示结束测试按钮', () => {
  render(<RpgRuntimeShell {...buildProps('play')} />);

  expect(screen.queryByRole('button', { name: '结束测试' })).toBeNull();
});
