/* @vitest-environment jsdom */

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

import { buildVisualNovelForbiddenCopyPattern } from '../visual-novel-runtime/visualNovelForbiddenCopy';
import { mockVisualNovelDraft } from '../visual-novel-runtime/visualNovelMockData';
import { VisualNovelResultView } from './VisualNovelResultView';

vi.mock('../../services/visual-novel-creation', () => ({
  createVisualNovelBackgroundMusicTask: vi.fn(),
  createVisualNovelSoundEffectTask: vi.fn(),
  generateVisualNovelImageAsset: vi.fn(),
  buildVisualNovelImageGenerationPrompt: vi.fn(() => '默认图片提示词'),
  listVisualNovelHistoryAssets: vi.fn().mockResolvedValue([]),
  publishVisualNovelBackgroundMusicAsset: vi.fn(),
  publishVisualNovelSoundEffectAsset: vi.fn(),
  uploadVisualNovelAsset: vi.fn(),
}));

vi.mock('../../services/assetReadUrlService', () => ({
  resolveAssetReadUrl: vi.fn().mockResolvedValue(''),
  shouldResolveAssetReadUrl: vi.fn(() => false),
}));

test('visual novel profile tab uses PlatformSubpanel shells', () => {
  const { container } = render(
    <VisualNovelResultView draft={mockVisualNovelDraft} onBack={() => {}} />,
  );

  const profilePanels = Array.from(
    container.querySelectorAll('section.platform-subpanel'),
  ).filter((panel) => panel.className.includes('rounded-[1.35rem]'));
  const workTitlePanel = screen.getByText('作品名称').closest('section');

  expect(profilePanels).toHaveLength(2);
  for (const panel of profilePanels) {
    expect(panel.className).toContain('p-4');
  }
  expect(workTitlePanel?.className).toContain('platform-subpanel');
  expect(workTitlePanel?.className).toContain('rounded-[1.35rem]');
});

test('visual novel profile media previews use PlatformMediaFrame aspects', () => {
  const draftWithCover = {
    ...mockVisualNovelDraft,
    coverImageSrc: '/visual-novel/cover.png',
  };

  const { container } = render(
    <VisualNovelResultView draft={draftWithCover} onBack={() => {}} />,
  );

  const coverImages = Array.from(
    container.querySelectorAll('img[src="/visual-novel/cover.png"]'),
  );
  const mediaFrameClassNames = coverImages.map(
    (image) => image.closest('div.relative')?.className ?? '',
  );

  expect(coverImages).toHaveLength(2);
  expect(mediaFrameClassNames).toContainEqual(
    expect.stringContaining('aspect-[4/3]'),
  );
  expect(mediaFrameClassNames).toContainEqual(
    expect.stringContaining('aspect-[16/9]'),
  );
  const assetPreviewFrameClassName = mediaFrameClassNames.find((className) =>
    className.includes('aspect-[16/9]'),
  );
  expect(assetPreviewFrameClassName).toBeTruthy();
  expect(assetPreviewFrameClassName).not.toContain(
    'bg-[var(--platform-subpanel-fill)]',
  );
});

test('visual novel upload-only asset picker uses PlatformEmptyState chrome', async () => {
  const user = userEvent.setup();

  render(
    <VisualNovelResultView draft={mockVisualNovelDraft} onBack={() => {}} />,
  );

  await user.click(screen.getByRole('button', { name: '封面' }));

  const pickerDialog = screen.getByRole('dialog', { name: '封面' });
  const uploadOnlyEmptyState =
    within(pickerDialog).getByText('选择本地文件上传');

  expect(uploadOnlyEmptyState.className).toContain('border-dashed');
  expect(uploadOnlyEmptyState.className).toContain('rounded-[1.35rem]');
  expect(uploadOnlyEmptyState.className).toContain('min-h-24');
  expect(uploadOnlyEmptyState.className).toContain(
    'text-[var(--platform-text-base)]',
  );
});

test('visual novel entity list items use interactive PlatformSubpanel shells', async () => {
  const user = userEvent.setup();

  render(
    <VisualNovelResultView draft={mockVisualNovelDraft} onBack={() => {}} />,
  );

  await user.click(screen.getByRole('button', { name: '角色' }));

  const characterCard = screen.getByRole('button', { name: /林遥/u });

  expect(characterCard.className).toContain('platform-subpanel');
  expect(characterCard.className).toContain('hover:bg-white');
  expect(characterCard.className).toContain('disabled:cursor-not-allowed');
  expect(characterCard.className).toContain('rounded-[1.25rem]');
});

test('visual novel empty entity list uses PlatformEmptyState shell', async () => {
  const user = userEvent.setup();
  const emptyCharacterDraft = {
    ...mockVisualNovelDraft,
    characters: [],
  };

  render(
    <VisualNovelResultView draft={emptyCharacterDraft} onBack={() => {}} />,
  );

  await user.click(screen.getByRole('button', { name: '角色' }));

  const emptyPanel = screen
    .getByText('暂无角色')
    .closest('.platform-empty-state');

  expect(emptyPanel?.className).toContain('platform-empty-state');
  expect(emptyPanel?.className).toContain('bg-white/74');
  expect(emptyPanel?.className).toContain('rounded-[1.25rem]');
  expect(emptyPanel?.className).toContain('min-h-32');
  expect(emptyPanel?.className).toContain(
    'text-[var(--platform-text-soft)]',
  );
});

test('visual novel result opens complex editors as a dialog', async () => {
  const user = userEvent.setup();

  render(
    <VisualNovelResultView draft={mockVisualNovelDraft} onBack={() => {}} />,
  );

  await user.click(screen.getByRole('button', { name: '角色' }));
  await user.click(screen.getByRole('button', { name: /林遥/u }));

  const dialog = screen.getByRole('dialog', { name: '林遥' });
  expect(within(dialog).getByDisplayValue('林遥')).toBeTruthy();
  expect(screen.queryByText(buildVisualNovelForbiddenCopyPattern())).toBeNull();
});

test('visual novel result exposes test run action with current draft', async () => {
  const user = userEvent.setup();
  const onStartTestRun = vi.fn();

  render(
    <VisualNovelResultView
      draft={mockVisualNovelDraft}
      onBack={() => {}}
      onStartTestRun={onStartTestRun}
    />,
  );

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

  expect(onStartTestRun).toHaveBeenCalledTimes(1);
  expect(onStartTestRun.mock.calls[0]?.[0].workTitle).toBe('雪线电台');
});

test('visual novel result sends edited character draft to save and test run', async () => {
  const user = userEvent.setup();
  const onSaveDraft = vi.fn();
  const onStartTestRun = vi.fn();

  render(
    <VisualNovelResultView
      draft={mockVisualNovelDraft}
      onBack={() => {}}
      onSaveDraft={onSaveDraft}
      onStartTestRun={onStartTestRun}
    />,
  );

  await user.click(screen.getByRole('button', { name: '角色' }));
  await user.click(screen.getByRole('button', { name: /林遥/u }));

  const dialog = screen.getByRole('dialog', { name: '林遥' });
  const nameInput = within(dialog).getByDisplayValue('林遥');
  await user.clear(nameInput);
  await user.type(nameInput, '林遥改');
  await user.click(within(dialog).getByRole('button', { name: '关闭' }));

  const saveButtons = screen.getAllByRole('button', { name: '保存草稿' });
  await user.click(saveButtons[1]!);
  await user.click(screen.getByRole('button', { name: '试玩' }));

  expect(onSaveDraft.mock.calls[0]?.[0].characters[0]?.name).toBe('林遥改');
  expect(onStartTestRun.mock.calls[0]?.[0].characters[0]?.name).toBe('林遥改');
});

test('visual novel result uploads scene and character assets into platform references', async () => {
  const user = userEvent.setup();
  const onSaveDraft = vi.fn();
  const uploadMock = vi.mocked(
    await import('../../services/visual-novel-creation'),
  ).uploadVisualNovelAsset;

  uploadMock.mockResolvedValue({
    assetObjectId: 'asset-scene-1',
    assetKind: 'scene_image',
    objectKey:
      'generated-custom-world-scenes/vn-profile/scene-1/background.png',
    imageSrc:
      '/generated-custom-world-scenes/vn-profile/scene-1/background.png',
  });

  render(
    <VisualNovelResultView
      draft={mockVisualNovelDraft}
      onBack={() => {}}
      onSaveDraft={onSaveDraft}
    />,
  );

  await user.click(screen.getByRole('button', { name: '场景' }));
  await user.click(screen.getByRole('button', { name: /风雪站台/u }));

  const dialog = screen.getByRole('dialog', { name: '风雪站台' });
  const backgroundButtons = within(dialog).getAllByRole('button', {
    name: '背景图',
  });
  await user.click(backgroundButtons[0]!);

  const fileInput = within(
    screen.getByRole('dialog', { name: '背景图' }),
  ).getByLabelText('上传背景图文件') as HTMLInputElement;
  await user.upload(
    fileInput,
    new File(['image-bytes'], 'scene.png', { type: 'image/png' }),
  );

  await user.click(within(dialog).getByRole('button', { name: '关闭' }));
  await user.click(screen.getAllByRole('button', { name: '保存草稿' })[1]!);

  expect(onSaveDraft).toHaveBeenCalled();
  expect(
    onSaveDraft.mock.calls[0]?.[0].scenes[0]?.backgroundImageSrc,
  ).toContain('/generated-custom-world-scenes/');
});

test('visual novel result generates scene background from asset picker', async () => {
  const user = userEvent.setup();
  const onSaveDraft = vi.fn();
  const visualNovelCreation = await import(
    '../../services/visual-novel-creation'
  );
  const generateImageMock = vi.mocked(
    visualNovelCreation.generateVisualNovelImageAsset,
  );

  generateImageMock.mockResolvedValue({
    imageSrc: '/generated-custom-world-scenes/vn-profile/scene-ai.webp',
    assetId: 'asset-scene-ai',
    model: 'test-image-model',
    size: '1280*720',
    taskId: 'task-scene-ai',
    prompt: '默认图片提示词',
  });

  render(
    <VisualNovelResultView
      draft={mockVisualNovelDraft}
      onBack={() => {}}
      onSaveDraft={onSaveDraft}
    />,
  );

  await user.click(screen.getByRole('button', { name: '场景' }));
  await user.click(screen.getByRole('button', { name: /风雪站台/u }));

  const editorDialog = screen.getByRole('dialog', { name: '风雪站台' });
  await user.click(
    within(editorDialog).getAllByRole('button', { name: '背景图' })[0]!,
  );
  await user.click(
    within(screen.getByRole('dialog', { name: '背景图' })).getByRole('button', {
      name: 'AI生成',
    }),
  );

  await user.click(within(editorDialog).getByRole('button', { name: '关闭' }));
  await user.click(screen.getAllByRole('button', { name: '保存草稿' })[1]!);

  expect(generateImageMock).toHaveBeenCalledWith(
    expect.objectContaining({
      kind: 'scene_background',
      scene: expect.objectContaining({
        sceneId: mockVisualNovelDraft.scenes[0]?.sceneId,
      }),
      prompt: '默认图片提示词',
    }),
  );
  expect(onSaveDraft.mock.calls[0]?.[0].scenes[0]?.backgroundImageSrc).toBe(
    '/generated-custom-world-scenes/vn-profile/scene-ai.webp',
  );
});
