/* @vitest-environment jsdom */

import { fireEvent, render, screen } from '@testing-library/react';
import { createRef, useState } from 'react';
import { describe, expect, it, vi } from 'vitest';

import type {
  GenerateDialogState,
  SpecGenerationType,
  UploadTarget,
} from './ImageCanvasEditorTypes';
import { ImageCanvasCharacterGenerationComposerView } from './ImageCanvasCharacterGenerationComposerView';

function createDialog(
  patch: Partial<GenerateDialogState> = {},
): GenerateDialogState {
  return {
    mode: 'character',
    prompt: '旧角色设定',
    status: 'idle',
    characterSpecReference: {
      id: 'spec-a',
      label: '角色规范A',
      src: 'data:image/png;base64,c3BlYw==',
    },
    characterReferences: [
      {
        id: 'ref-a',
        label: '参考图A',
        src: 'data:image/png;base64,cmVm',
      },
    ],
    ...patch,
  };
}

function CharacterGenerationHarness({
  initialDialog = createDialog(),
  onOpenSpecDialog = vi.fn(),
  onRequestUpload = vi.fn(),
  onSubmit = vi.fn(),
  onRememberImageModel = vi.fn(),
}: {
  initialDialog?: GenerateDialogState;
  onOpenSpecDialog?: (specType: SpecGenerationType) => void;
  onRequestUpload?: (target: UploadTarget) => void;
  onSubmit?: (dialog: GenerateDialogState) => void;
  onRememberImageModel?: (model: string) => void;
}) {
  const [dialog, setDialog] = useState<GenerateDialogState | null>(
    initialDialog,
  );
  const [isCharacterSpecMenuOpen, setIsCharacterSpecMenuOpen] =
    useState(false);
  const [
    isCharacterReferenceMenuOpen,
    setIsCharacterReferenceMenuOpen,
  ] = useState(false);
  const [isPickingSpec, setIsPickingSpec] = useState(false);
  const [isPickingReference, setIsPickingReference] = useState(false);
  const characterSpecButtonRef = createRef<HTMLButtonElement>();
  const characterReferenceButtonRef = createRef<HTMLButtonElement>();

  return dialog ? (
    <div>
      <ImageCanvasCharacterGenerationComposerView
        dialog={dialog}
        style={{ left: 10, top: 20 }}
        characterSpecButtonRef={characterSpecButtonRef}
        characterReferenceButtonRef={characterReferenceButtonRef}
        isCharacterSpecMenuOpen={isCharacterSpecMenuOpen}
        isCharacterReferenceMenuOpen={isCharacterReferenceMenuOpen}
        setGenerateDialog={setDialog}
        setIsCharacterSpecMenuOpen={setIsCharacterSpecMenuOpen}
        setIsCharacterReferenceMenuOpen={setIsCharacterReferenceMenuOpen}
        setIsPickingCharacterSpecFromCanvas={setIsPickingSpec}
        setIsPickingCharacterReferenceFromCanvas={setIsPickingReference}
        renderEditorPortal={(node) => node}
        buildPortalMenuStyle={() => ({ position: 'fixed', left: 0, top: 0 })}
        onOpenSpecDialog={onOpenSpecDialog}
        onRequestUpload={onRequestUpload}
        onRememberImageModel={onRememberImageModel}
        onSubmit={onSubmit}
      />
      <output aria-label="当前角色设定">{dialog.prompt}</output>
      <output aria-label="当前状态">{dialog.status}</output>
      <output aria-label="当前错误">{dialog.errorMessage ?? '-'}</output>
      <output aria-label="选择角色规范">{String(isPickingSpec)}</output>
      <output aria-label="选择常规参考图">{String(isPickingReference)}</output>
    </div>
  ) : null;
}

describe('ImageCanvasCharacterGenerationComposerView', () => {
  it('updates character prompt, clears failed state and submits', () => {
    const submitCharacter = vi.fn();
    render(
      <CharacterGenerationHarness
        initialDialog={createDialog({
          status: 'failed',
          errorMessage: '角色失败',
        })}
        onSubmit={submitCharacter}
      />,
    );

    fireEvent.change(screen.getByLabelText('角色设定'), {
      target: { value: '新的角色设定' },
    });
    fireEvent.click(screen.getByRole('button', { name: '生成' }));

    expect(screen.getByLabelText('当前角色设定').textContent).toBe(
      '新的角色设定',
    );
    expect(screen.getByLabelText('当前状态').textContent).toBe('idle');
    expect(screen.getByLabelText('当前错误').textContent).toBe('-');
    expect(submitCharacter).toHaveBeenCalledWith(
      expect.objectContaining({ prompt: '新的角色设定', status: 'idle' }),
    );
  });

  it('routes character reference menus to canvas picking, spec creation and upload', () => {
    const openSpecDialog = vi.fn();
    const requestUpload = vi.fn();
    render(
      <CharacterGenerationHarness
        onOpenSpecDialog={openSpecDialog}
        onRequestUpload={requestUpload}
      />,
    );

    fireEvent.click(screen.getByRole('button', { name: '角色规范A' }));
    fireEvent.click(screen.getByRole('menuitem', { name: '从画布中选择' }));

    expect(screen.getByLabelText('选择角色规范').textContent).toBe('true');

    fireEvent.click(screen.getByRole('button', { name: '角色规范A' }));
    fireEvent.click(screen.getByRole('menuitem', { name: '新建角色形象规范' }));

    expect(openSpecDialog).toHaveBeenCalledWith('character');

    fireEvent.click(screen.getByRole('button', { name: '角色规范A' }));
    fireEvent.click(screen.getByRole('menuitem', { name: '上传图片' }));

    expect(requestUpload).toHaveBeenCalledWith('character-spec');

    fireEvent.click(screen.getByRole('button', { name: '上传常规参考图' }));
    fireEvent.click(screen.getByRole('menuitem', { name: '从画布中选择' }));

    expect(screen.getByLabelText('选择常规参考图').textContent).toBe('true');
  });
});
