/* @vitest-environment jsdom */

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

import type {
  CustomWorldAgentSessionSnapshot,
  CustomWorldDraftCardDetail,
} from '../../../packages/shared/src/contracts/customWorldAgent';
import { getCustomWorldAgentCardDetail } from '../../services/aiService';
import { CustomWorldAgentWorkspace } from './CustomWorldAgentWorkspace';

vi.mock('../../services/aiService', () => ({
  getCustomWorldAgentCardDetail: vi.fn(),
}));

vi.mock('../CustomWorldRoleAssetStudioModal', () => ({
  CustomWorldRoleAssetStudioModal: ({
    role,
    onPublishSuccess,
  }: {
    role: { name: string };
    onPublishSuccess?: (
      payload: {
        roleId: string;
        portraitPath: string;
        generatedVisualAssetId: string;
        generatedAnimationSetId?: string | null;
        animationMap?: Record<string, unknown> | null;
      },
      options?: { closeAfterSync?: boolean },
    ) => void;
  }) => (
    <div>
      <div>角色资产工坊：{role.name}</div>
      <button
        type="button"
        onClick={() =>
          onPublishSuccess?.(
            {
              roleId: 'character-1',
              portraitPath: '/generated/character-1.png',
              generatedVisualAssetId: 'visual-character-1',
              generatedAnimationSetId: 'animation-set-character-1',
              animationMap: {
                idle: { basePath: '/generated/character-1/idle' },
                run: { basePath: '/generated/character-1/run' },
                attack: { basePath: '/generated/character-1/attack' },
                hurt: { basePath: '/generated/character-1/hurt' },
                die: { basePath: '/generated/character-1/die' },
              },
            },
            {
              closeAfterSync: true,
            },
          )
        }
      >
        模拟同步角色资产
      </button>
    </div>
  ),
}));

const detailById: Record<string, CustomWorldDraftCardDetail> = {
  'world-foundation': {
    id: 'world-foundation',
    kind: 'world',
    title: '潮雾列岛',
    sections: [
      {
        id: 'title',
        label: '标题',
        value: '潮雾列岛',
      },
      {
        id: 'summary',
        label: '摘要',
        value: '这是第一版世界底稿。',
      },
    ],
    linkedIds: ['thread-1', 'character-1'],
    locked: false,
    editable: true,
    editableSectionIds: ['title', 'summary'],
    warningMessages: [],
  },
  'character-1': {
    id: 'character-1',
    kind: 'character',
    title: '沈砺',
    sections: [
      {
        id: 'name',
        label: '角色名',
        value: '沈砺',
      },
      {
        id: 'summary',
        label: '角色摘要',
        value: '他像旧友，但也像一把始终没收回鞘的刀。',
      },
    ],
    linkedIds: ['thread-1'],
    locked: false,
    editable: true,
    editableSectionIds: ['name', 'summary'],
    warningMessages: [],
    assetStatus: 'missing',
    assetStatusLabel: '待生成主图',
  },
  'character-2': {
    id: 'character-2',
    kind: 'character',
    title: '顾潮音',
    sections: [
      {
        id: 'name',
        label: '角色名',
        value: '顾潮音',
      },
      {
        id: 'summary',
        label: '角色摘要',
        value: '她总像比所有人更早知道海雾会往哪一侧压下来。',
      },
    ],
    linkedIds: ['thread-1'],
    locked: false,
    editable: true,
    editableSectionIds: ['name', 'summary'],
    warningMessages: [],
    assetStatus: 'missing',
    assetStatusLabel: '待生成主图',
  },
};

const baseSession: CustomWorldAgentSessionSnapshot = {
  sessionId: 'custom-world-agent-session-1',
  stage: 'object_refining',
  focusCardId: 'world-foundation',
  creatorIntent: {},
  creatorIntentReadiness: {
    isReady: true,
    completedKeys: [
      'world_hook',
      'player_premise',
      'theme_and_tone',
      'core_conflict',
      'relationship_seed',
      'iconic_element',
    ],
    missingKeys: [],
  },
  anchorPack: {},
  lockState: {},
  draftProfile: {
    name: '潮雾列岛',
    storyNpcs: [
      {
        id: 'character-1',
        name: '沈砺',
        title: '守灯会旧友',
        role: '航道向导',
        publicMask: '守灯会里最熟悉旧航道的人。',
        hiddenHook: '暗地里正在为沉船商盟引路。',
        relationToPlayer: '旧友兼宿敌',
        threadIds: ['thread-1'],
        summary: '他像旧友，但也像一把始终没收回鞘的刀。',
      },
    ],
  },
  messages: [
    {
      id: 'message-1',
      role: 'assistant',
      kind: 'summary',
      text: '当前底稿已经可以继续精修。',
      createdAt: new Date().toISOString(),
      relatedOperationId: null,
    },
  ],
  draftCards: [
    {
      id: 'world-foundation',
      kind: 'world',
      title: '潮雾列岛',
      subtitle: '旧灯塔与航道争夺',
      summary: '世界总卡已经生成。',
      status: 'warning',
      linkedIds: ['thread-1', 'character-1'],
      warningCount: 1,
    },
    {
      id: 'character-1',
      kind: 'character',
      title: '沈砺',
      subtitle: '守灯会旧友',
      summary: '他最了解旧航道，也最可能先背叛。',
      status: 'suggested',
      linkedIds: ['thread-1'],
      warningCount: 0,
    },
  ],
  pendingClarifications: [],
  suggestedActions: [
    {
      id: 'request-summary',
      type: 'request_summary',
      label: '总结当前世界底稿',
      targetId: null,
    },
  ],
  recommendedReplies: [
    '现在开始生成草稿',
    '先总结一下当前设定',
    '我还想再补充一点',
  ],
  qualityFindings: [],
  assetCoverage: {
    roleAssets: [
      {
        roleId: 'character-1',
        roleName: '沈砺',
        roleKind: 'story',
        priorityTier: 'featured',
        portraitPath: null,
        generatedVisualAssetId: null,
        generatedAnimationSetId: null,
        status: 'missing',
        missingAnimations: ['idle', 'run', 'attack', 'hurt', 'die'],
        nextPointCost: 20,
      },
    ],
    sceneAssets: [],
    allRoleAssetsReady: false,
    allSceneAssetsReady: false,
  },
  updatedAt: '2026-04-14T10:00:00.000Z',
};

beforeEach(() => {
  vi.mocked(getCustomWorldAgentCardDetail).mockImplementation(
    async (_sessionId, cardId): Promise<CustomWorldDraftCardDetail> =>
      detailById[cardId] ?? detailById['world-foundation']!,
  );
  if (!Element.prototype.scrollIntoView) {
    Element.prototype.scrollIntoView = () => {};
  }
});

test('workspace loads detail, saves edits, opens generate actions, and reflects updated drawer cards', async () => {
  const user = userEvent.setup();
  const onExecuteAction = vi.fn();

  const { rerender } = render(
    <CustomWorldAgentWorkspace
      session={baseSession}
      activeOperation={null}
      onBack={() => {}}
      onRefresh={() => {}}
      onSubmitMessage={() => {}}
      onExecuteAction={onExecuteAction}
    />,
  );

  await waitFor(() => {
    expect(getCustomWorldAgentCardDetail).toHaveBeenCalledWith(
      baseSession.sessionId,
      'world-foundation',
    );
  });

  expect(screen.getByText('卡片详情')).toBeTruthy();
  expect(screen.queryByPlaceholderText('输入消息')).toBeNull();
  expect(screen.queryByText('当前底稿已经可以继续精修。')).toBeNull();

  await user.click(screen.getByRole('button', { name: '编辑设定' }));
  const summaryInput = screen.getByLabelText('摘要');
  await user.clear(summaryInput);
  await user.type(summaryInput, '这是更新后的世界摘要。');
  await user.click(screen.getByRole('button', { name: '保存' }));

  expect(onExecuteAction).toHaveBeenCalledWith({
    action: 'update_draft_card',
    cardId: 'world-foundation',
    sections: [
      {
        sectionId: 'title',
        value: '潮雾列岛',
      },
      {
        sectionId: 'summary',
        value: '这是更新后的世界摘要。',
      },
    ],
  });

  await user.click(screen.getByRole('button', { name: /沈砺/u }));
  await waitFor(() => {
    expect(getCustomWorldAgentCardDetail).toHaveBeenLastCalledWith(
      baseSession.sessionId,
      'character-1',
    );
  });

  const [generateCharacterButton] = screen.getAllByRole('button', {
    name: '新增角色',
  });
  await user.click(generateCharacterButton!);
  expect(screen.getByRole('button', { name: '生成角色' })).toBeTruthy();
  await user.click(screen.getByRole('button', { name: '生成角色' }));

  expect(onExecuteAction).toHaveBeenCalledWith({
    action: 'generate_characters',
    count: 2,
    promptText: null,
    anchorCardIds: ['character-1'],
  });

  const [generateLandmarkButton] = screen.getAllByRole('button', {
    name: '新增场景',
  });
  await user.click(generateLandmarkButton!);
  expect(screen.getByRole('button', { name: '生成场景' })).toBeTruthy();
  await user.click(screen.getByRole('button', { name: '生成场景' }));

  expect(onExecuteAction).toHaveBeenCalledWith({
    action: 'generate_landmarks',
    count: 2,
    promptText: null,
    anchorCardIds: ['character-1'],
  });

  const [openRoleAssetsButton] = screen.getAllByRole('button', {
    name: '角色资产',
  });
  await user.click(openRoleAssetsButton!);
  expect(onExecuteAction).toHaveBeenCalledWith({
    action: 'generate_role_assets',
    roleIds: ['character-1'],
  });

  rerender(
    <CustomWorldAgentWorkspace
      session={{
        ...baseSession,
        stage: 'visual_refining',
        draftCards: [
          ...baseSession.draftCards,
          {
            id: 'character-2',
            kind: 'character',
            title: '顾潮音',
            subtitle: '回潮记录员',
            summary: '她会把每一次海雾异常都记到连自己都不愿复看的本子里。',
            status: 'suggested',
            linkedIds: ['thread-1'],
            warningCount: 0,
          },
        ],
        updatedAt: '2026-04-14T10:05:00.000Z',
      }}
      activeOperation={{
        operationId: 'operation-role-assets',
        type: 'generate_role_assets',
        status: 'completed',
        phaseLabel: '角色资产工坊已就绪',
        phaseDetail: '可以开始生成角色主图与动作。',
        progress: 100,
        error: null,
      }}
      onBack={() => {}}
      onRefresh={() => {}}
      onSubmitMessage={() => {}}
      onExecuteAction={onExecuteAction}
    />,
  );

  await waitFor(() => {
    expect(screen.getByText('顾潮音')).toBeTruthy();
  });
  expect(screen.getByText('角色资产工坊：沈砺')).toBeTruthy();

  await user.click(screen.getByRole('button', { name: '模拟同步角色资产' }));
  expect(onExecuteAction).toHaveBeenCalledWith({
    action: 'sync_role_assets',
    roleId: 'character-1',
    portraitPath: '/generated/character-1.png',
    generatedVisualAssetId: 'visual-character-1',
    generatedAnimationSetId: 'animation-set-character-1',
    animationMap: {
      idle: { basePath: '/generated/character-1/idle' },
      run: { basePath: '/generated/character-1/run' },
      attack: { basePath: '/generated/character-1/attack' },
      hurt: { basePath: '/generated/character-1/hurt' },
      die: { basePath: '/generated/character-1/die' },
    },
  });

  rerender(
    <CustomWorldAgentWorkspace
      session={{
        ...baseSession,
        stage: 'visual_refining',
        draftCards: [
          {
            ...baseSession.draftCards[0]!,
          },
          {
            ...baseSession.draftCards[1]!,
            subtitle: '守灯会旧友 / 动作已就绪',
            assetStatus: 'complete',
            assetStatusLabel: '动作已就绪',
          },
        ],
        assetCoverage: {
          roleAssets: [
            {
              roleId: 'character-1',
              roleName: '沈砺',
              roleKind: 'story',
              priorityTier: 'featured',
              portraitPath: '/generated/character-1.png',
              generatedVisualAssetId: 'visual-character-1',
              generatedAnimationSetId: 'animation-set-character-1',
              status: 'complete',
              missingAnimations: [],
              nextPointCost: 0,
            },
          ],
          sceneAssets: [],
          allRoleAssetsReady: true,
          allSceneAssetsReady: false,
        },
        draftProfile: {
          ...baseSession.draftProfile,
          storyNpcs: [
            {
              id: 'character-1',
              name: '沈砺',
              title: '守灯会旧友',
              role: '航道向导',
              publicMask: '守灯会里最熟悉旧航道的人。',
              hiddenHook: '暗地里正在为沉船商盟引路。',
              relationToPlayer: '旧友兼宿敌',
              threadIds: ['thread-1'],
              summary: '他像旧友，但也像一把始终没收回鞘的刀。',
              imageSrc: '/generated/character-1.png',
              generatedVisualAssetId: 'visual-character-1',
              generatedAnimationSetId: 'animation-set-character-1',
              animationMap: {
                idle: { basePath: '/generated/character-1/idle' },
                run: { basePath: '/generated/character-1/run' },
                attack: { basePath: '/generated/character-1/attack' },
                hurt: { basePath: '/generated/character-1/hurt' },
                die: { basePath: '/generated/character-1/die' },
              },
            },
          ],
        },
      }}
      activeOperation={{
        operationId: 'operation-sync-role-assets',
        type: 'sync_role_assets',
        status: 'completed',
        phaseLabel: '角色资产已同步',
        phaseDetail: '角色资产已经写回草稿。',
        progress: 100,
        error: null,
      }}
      onBack={() => {}}
      onRefresh={() => {}}
      onSubmitMessage={() => {}}
      onExecuteAction={onExecuteAction}
    />,
  );

  await waitFor(() => {
    expect(screen.getAllByText('动作已就绪').length).toBeGreaterThan(0);
  });
});
