/* @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 './visualNovelForbiddenCopy';
import { mockVisualNovelDraft, mockVisualNovelRun } from './visualNovelMockData';
import { VisualNovelRuntimeShell } from './VisualNovelRuntimeShell';

test('visual novel runtime renders mock play surface and opens panels as dialogs', async () => {
  const user = userEvent.setup();

  render(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={mockVisualNovelRun}
      onBack={() => {}}
    />,
  );

  expect(screen.getByText('风雪站台')).toBeTruthy();
  expect(screen.getAllByText('林遥').length).toBeGreaterThan(0);
  expect(screen.queryByText(buildVisualNovelForbiddenCopyPattern())).toBeNull();

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

  const dialog = screen.getByRole('dialog', { name: '历史' });
  expect(within(dialog).getByText('#1')).toBeTruthy();
});

test('visual novel runtime submits free text action with client event id', async () => {
  const user = userEvent.setup();
  const onSubmitAction = vi.fn();

  render(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={mockVisualNovelRun}
      onBack={() => {}}
      onSubmitAction={onSubmitAction}
    />,
  );

  await user.type(screen.getByLabelText('输入行动'), '检查广播柜');
  await user.click(screen.getByRole('button', { name: '发送行动' }));

  expect(onSubmitAction).toHaveBeenCalledWith(
    expect.objectContaining({
      actionKind: 'free_text',
      text: '检查广播柜',
    }),
  );
  expect(onSubmitAction.mock.calls[0]?.[0].clientEventId).toMatch(
    /^vn-free-text-/u,
  );
});

test('visual novel runtime submits choice and continue actions', async () => {
  const user = userEvent.setup();
  const onSubmitAction = vi.fn();
  const onContinue = vi.fn();
  const runWithoutChoices = {
    ...mockVisualNovelRun,
    availableChoices: [],
  };

  const { rerender } = render(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={mockVisualNovelRun}
      onBack={() => {}}
      onSubmitAction={onSubmitAction}
      onContinue={onContinue}
    />,
  );

  await user.click(screen.getByRole('button', { name: '靠近广播柜，确认频段来源。' }));
  expect(onSubmitAction).toHaveBeenCalledWith(
    expect.objectContaining({
      actionKind: 'choice',
      choiceId: 'vn-choice-radio',
    }),
  );
  expect(onSubmitAction.mock.calls[0]?.[0].clientEventId).toMatch(
    /^vn-choice-/u,
  );

  rerender(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={runWithoutChoices}
      onBack={() => {}}
      onSubmitAction={onSubmitAction}
      onContinue={onContinue}
    />,
  );

  await user.click(screen.getByRole('button', { name: '继续' }));
  expect(onContinue).toHaveBeenCalledTimes(1);
});

test('visual novel runtime panels call regeneration and platform archive actions', async () => {
  const user = userEvent.setup();
  const onRegenerateHistoryEntry = vi.fn();
  const onSaveRun = vi.fn();
  const onResumeSaveArchive = vi.fn();

  render(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={mockVisualNovelRun}
      onBack={() => {}}
      onRegenerateHistoryEntry={onRegenerateHistoryEntry}
      onSaveRun={onSaveRun}
      onResumeSaveArchive={onResumeSaveArchive}
      saveArchives={[
        {
          worldKey: 'visual-novel:archive-1',
          ownerUserId: 'mock-user',
          profileId: 'vn-profile-mock-1',
          worldType: 'visual-novel',
          worldName: '雪线电台',
          subtitle: '风雪站台',
          summaryText: '第 2 回合',
          coverImageSrc: null,
          lastPlayedAt: '2026-05-05T12:00:00.000Z',
        },
      ]}
    />,
  );

  await user.click(screen.getByRole('button', { name: '历史' }));
  await user.click(screen.getByRole('button', { name: '重生成' }));
  expect(onRegenerateHistoryEntry).toHaveBeenCalledWith('vn-history-1');

  await user.click(screen.getByRole('button', { name: '关闭' }));
  await user.click(screen.getByRole('button', { name: '存档' }));
  await user.click(screen.getByRole('button', { name: '保存' }));
  expect(onSaveRun).toHaveBeenCalledTimes(1);
  await user.click(screen.getByText('雪线电台'));
  expect(onResumeSaveArchive).toHaveBeenCalledWith('visual-novel:archive-1');
});

test('visual novel runtime shows raw text only as transient stream text', () => {
  const transientText = '这是临时流式文本';

  render(
    <VisualNovelRuntimeShell
      draft={mockVisualNovelDraft}
      run={mockVisualNovelRun}
      streamingText={transientText}
      onBack={() => {}}
    />,
  );

  expect(screen.getByText(transientText)).toBeTruthy();
  const textModeBlocks = screen.queryAllByText((content, element) => {
    return Boolean(
      element?.className.toString().includes('whitespace-pre-line') &&
        content.includes(transientText),
    );
  });
  expect(textModeBlocks).toHaveLength(0);
});
