/* @vitest-environment jsdom */

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

import {
  AnimationState,
  type Character,
  type QuestLogEntry,
  type StoryMoment,
  type StoryOption,
  WorldType,
} from '../../types';
import { RpgAdventurePanel } from './RpgAdventurePanel';

function createCharacter(): Character {
  return {
    id: 'hero',
    name: '沈行',
    title: '试剑客',
    description: '测试主角',
    backstory: '测试背景',
    avatar: '/hero.png',
    portrait: '/hero.png',
    assetFolder: 'hero',
    assetVariant: 'default',
    attributes: {
      strength: 10,
      agility: 10,
      intelligence: 8,
      spirit: 9,
    },
    personality: 'calm',
    skills: [],
    adventureOpenings: {},
  } as Character;
}

function createOption(functionId: string, actionText: string): StoryOption {
  return {
    functionId,
    actionText,
    text: actionText,
    visuals: {
      playerAnimation: AnimationState.IDLE,
      playerMoveMeters: 0,
      playerOffsetY: 0,
      playerFacing: 'right',
      scrollWorld: false,
      monsterChanges: [],
    },
  };
}

function createPendingQuest(): QuestLogEntry {
  return {
    id: 'quest-liu-1',
    issuerNpcId: 'npc-liu',
    issuerNpcName: '柳无声',
    sceneId: 'scene-bamboo',
    title: '竹林密信',
    description: '替柳无声查清竹林中的密信来源。',
    summary: '去竹林查清密信来源。',
    objective: {
      kind: 'inspect_treasure',
      requiredCount: 1,
    },
    progress: 0,
    status: 'active',
    reward: {
      affinityBonus: 5,
      currency: 10,
      items: [],
    },
    rewardText: '完成后可获得报酬。',
  };
}

function createPendingQuestStory(quest: QuestLogEntry): StoryMoment {
  const viewOption = createOption('npc_chat_quest_offer_view', '查看任务');
  viewOption.runtimePayload = {
    npcChatQuestOfferAction: 'view',
  };
  const replaceOption = createOption('npc_chat_quest_offer_replace', '更换任务');
  replaceOption.runtimePayload = {
    npcChatQuestOfferAction: 'replace',
  };
  const abandonOption = createOption('npc_chat_quest_offer_abandon', '放弃任务');
  abandonOption.runtimePayload = {
    npcChatQuestOfferAction: 'abandon',
  };

  return {
    text: '柳无声把真正的委托说了出来。',
    displayMode: 'dialogue',
    dialogue: [
      { speaker: 'npc', speakerName: '柳无声', text: '这件事我只想正式托付给你。' },
    ],
    options: [viewOption, replaceOption, abandonOption],
    npcChatState: {
      npcId: 'npc-liu',
      npcName: '柳无声',
      turnCount: 2,
      customInputPlaceholder: '输入你想对 TA 说的话',
      pendingQuestOffer: {
        quest,
      },
    },
  };
}

function createAcceptedQuestStory(quest: QuestLogEntry): StoryMoment {
  return {
    text: '柳无声把接下来的线索正式交给了你。',
    displayMode: 'dialogue',
    dialogue: [
      { speaker: 'npc', speakerName: '柳无声', text: '这件事我只想正式托付给你。' },
      { speaker: 'player', text: '这件事我愿意接下，你把关键要点交给我。' },
      { speaker: 'npc', speakerName: '柳无声', text: '先去竹林查清密信来源。' },
    ],
    options: [
      createOption('npc_chat', '这件事里你最担心哪一步'),
      createOption('npc_chat', '我回来时你最想先知道什么'),
    ],
    npcChatState: {
      npcId: 'npc-liu',
      npcName: '柳无声',
      turnCount: 2,
      customInputPlaceholder: '输入你想对 TA 说的话',
      pendingQuestOffer: null,
    },
  };
}

function QuestOfferHarness() {
  const pendingQuest = createPendingQuest();
  const [currentStory, setCurrentStory] = useState<StoryMoment>(
    createPendingQuestStory(pendingQuest),
  );
  const [quests, setQuests] = useState<QuestLogEntry[]>([]);
  const acceptPendingOffer = vi.fn(() => {
    queueMicrotask(() => {
      setQuests([pendingQuest]);
      setCurrentStory(createAcceptedQuestStory(pendingQuest));
    });
    return pendingQuest.id;
  });

  return (
    <RpgAdventurePanel
      aiError={null}
      currentStory={currentStory}
      isLoading={false}
      displayedOptions={currentStory.options}
      hideOptions={false}
      canRefreshOptions={false}
      onRefreshOptions={() => undefined}
      onChoice={() => undefined}
      onSubmitNpcChatInput={() => true}
      onExitNpcChat={() => true}
      onOpenCharacter={() => undefined}
      onOpenInventory={() => undefined}
      playerCharacter={createCharacter()}
      worldType={WorldType.WUXIA}
      quests={quests}
      questUi={{
        acknowledgeQuestCompletion: () => undefined,
        claimQuestReward: () => null,
      }}
      npcChatQuestOfferUi={{
        replacePendingOffer: () => false,
        abandonPendingOffer: () => false,
        acceptPendingOffer,
      }}
      goalStack={{
        northStarGoal: null,
        activeGoal: null,
        immediateStepGoal: null,
        supportGoals: [],
      }}
      goalPulse={null}
      onDismissGoalPulse={() => undefined}
      battleRewardUi={{
        reward: null,
        dismiss: () => undefined,
      }}
      playerHp={100}
      playerMaxHp={100}
      playerMana={20}
      playerMaxMana={20}
      playerSkillCooldowns={{}}
      inBattle={false}
      currentNpcBattleMode={null}
      statistics={{
        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,
      }}
      musicVolume={0.6}
      onMusicVolumeChange={() => undefined}
      onSaveAndExit={() => undefined}
    />
  );
}

beforeAll(() => {
  if (!HTMLElement.prototype.scrollTo) {
    HTMLElement.prototype.scrollTo = () => undefined;
  }
});

test('quest offer accept button reuses the shared accepted-quest follow-up chain', async () => {
  const user = userEvent.setup();

  render(<QuestOfferHarness />);

  await user.click(screen.getByRole('button', { name: /查看任务/u }));
  await user.click(await screen.findByRole('button', { name: '领取任务' }));

  expect(await screen.findByText('任务进度：0/1')).toBeTruthy();
  expect(screen.getAllByText('竹林密信').length).toBeGreaterThan(0);
  expect(screen.queryByText('待领取')).toBeNull();
  expect(screen.getByText('这件事里你最担心哪一步')).toBeTruthy();
});
