/* @vitest-environment jsdom */

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

import type {
  CanvasGenerationDialogState,
  CanvasLayer,
  CanvasTool,
  GenerateDialogState,
  ImageContextMenuState,
  SidebarPanel,
} from './ImageCanvasEditorTypes';
import { useCanvasGenerationDialogs } from './useCanvasGenerationDialogs';
import { useImageCanvasGenerationWorkflow } from './useImageCanvasGenerationWorkflow';

const generateEditorImageMock = vi.hoisted(() => vi.fn());
const generateEditorCharacterAnimationMock = vi.hoisted(() => vi.fn());
const generateEditorIconSpritesheetMock = vi.hoisted(() => vi.fn());
const editEditorImageMock = vi.hoisted(() => vi.fn());

vi.mock('../../services/image-editor/editorImageReference', () => ({
  resolveEditorImageReferenceDataUrl: vi.fn(async (src: string) => src),
}));

vi.mock('../../services/image-editor/editorProjectClient', async () => {
  const actual = await vi.importActual<
    typeof import('../../services/image-editor/editorProjectClient')
  >('../../services/image-editor/editorProjectClient');
  return {
    ...actual,
    editEditorImage: editEditorImageMock,
    generateEditorCharacterAnimation: generateEditorCharacterAnimationMock,
    generateEditorIconSpritesheet: generateEditorIconSpritesheetMock,
    generateEditorImage: generateEditorImageMock,
  };
});

function createLayer(overrides: Partial<CanvasLayer> = {}): CanvasLayer {
  return {
    id: 'layer-source',
    resourceId: 'resource-source',
    title: '源图',
    src: 'data:image/png;base64,source',
    x: 120,
    y: 140,
    width: 320,
    height: 240,
    originalWidth: 1024,
    originalHeight: 768,
    zIndex: 2,
    sourceType: 'uploaded',
    ...overrides,
  };
}

function createGenerated(overrides = {}) {
  return {
    imageSrc: 'data:image/png;base64,generated',
    width: 1024,
    height: 1024,
    sourceType: 'generated' as const,
    prompt: '生成提示词',
    actualPrompt: '生成提示词',
    model: 'gpt-image-2',
    provider: 'VectorEngine',
    taskId: 'task-generated',
    ...overrides,
  };
}

function GenerationWorkflowHarness({
  initialLayers = [createLayer()],
}: {
  initialLayers?: CanvasLayer[];
}) {
  const [layers, setLayers] = useState<CanvasLayer[]>(initialLayers);
  const [activeTool, setActiveTool] = useState<CanvasTool>('select');
  const [activeSidebarPanel, setActiveSidebarPanel] =
    useState<SidebarPanel | null>('assets');
  const [selectedLayerId, setSelectedLayerId] = useState<string | null>(null);
  const [metadataLayer, setMetadataLayer] = useState<CanvasLayer | null>(null);
  const [imageContextMenu, setImageContextMenu] =
    useState<ImageContextMenuState | null>({
      layerId: 'layer-source',
      x: 1,
      y: 2,
    });
  const fitLayersMockRef = useRef(vi.fn());
  const layerCounterRef = useRef(0);
  const dialogs = useCanvasGenerationDialogs();
  const workflow = useImageCanvasGenerationWorkflow({
    layers,
    canvasSize: { width: 900, height: 640 },
    viewport: { x: 10, y: 20, scale: 2 },
    layerCounterRef,
    generateDialog: dialogs.generateDialog,
    setGenerateDialog: dialogs.setGenerateDialog,
    openCanvasGenerationDialog: dialogs.openCanvasGenerationDialog,
    updateCanvasGenerationDialogById: dialogs.updateCanvasGenerationDialogById,
    removeCanvasGenerationDialogById: dialogs.removeCanvasGenerationDialogById,
    removeCanvasGenerationDialogsByLayerId:
      dialogs.removeCanvasGenerationDialogsByLayerId,
    getGeneratingDialogPlaceholder: dialogs.getGeneratingDialogPlaceholder,
    appendCanvasLayersWithResources: (nextLayers) =>
      setLayers((currentLayers) => [...currentLayers, ...nextLayers]),
    selectSingleLayer: setSelectedLayerId,
    fitLayers: fitLayersMockRef.current,
    setActiveTool,
    setActiveSidebarPanel,
    setMetadataLayer,
    setImageContextMenu,
  });

  const activeDialog = dialogs.generateDialog;
  const activeCanvasDialog =
    activeDialog && 'id' in activeDialog
      ? (activeDialog as CanvasGenerationDialogState)
      : null;

  return (
    <div>
      <span data-testid="tool">{activeTool}</span>
      <span data-testid="sidebar">{activeSidebarPanel ?? '-'}</span>
      <span data-testid="selected">{selectedLayerId ?? '-'}</span>
      <span data-testid="metadata">{metadataLayer?.id ?? '-'}</span>
      <span data-testid="image-context">{imageContextMenu ? 'open' : '-'}</span>
      <span data-testid="layers">
        {layers
          .map(
            (layer) =>
              `${layer.id}:${layer.title}:${layer.sourceResourceId ?? '-'}:${layer.assetKind ?? '-'}`,
          )
          .join('|')}
      </span>
      <span data-testid="dialog">
        {activeDialog
          ? `${activeDialog.mode}:${activeDialog.status}:${activeDialog.composerOpen !== false ? 'open' : 'closed'}:${activeDialog.generatedLayerId ?? '-'}:${activeDialog.placeholder ? 'placeholder' : '-'}`
          : '-'}
      </span>
      <span data-testid="quick-edit">
        {workflow.quickEditPanel
          ? `${workflow.quickEditPanel.sourceLayerId}:${workflow.quickEditPanel.status}:${workflow.quickEditPanel.prompt || '-'}`
          : '-'}
      </span>
      <span data-testid="character-animation">
        {workflow.characterAnimationPanel
          ? `${workflow.characterAnimationPanel.sourceLayerId}:${workflow.characterAnimationPanel.status}`
          : '-'}
      </span>
      <span data-testid="fit-count">
        {fitLayersMockRef.current.mock.calls.length}
      </span>
      <button type="button" onClick={workflow.openGenerateDialog}>
        打开生成
      </button>
      <button
        type="button"
        onClick={() =>
          dialogs.setGenerateDialog({
            mode: 'edit',
            prompt: '',
            status: 'idle',
            sourceLayerId: 'layer-source',
          })
        }
      >
        打开修改状态
      </button>
      <button
        type="button"
        onClick={() => workflow.openQuickEditPanel(layers[0]!)}
      >
        打开快速编辑
      </button>
      <button
        type="button"
        onClick={() =>
          workflow.openCharacterAnimationPanel(
            createLayer({ id: 'layer-character', assetKind: 'character' }),
          )
        }
      >
        打开角色动画
      </button>
      <button
        type="button"
        onClick={() =>
          workflow.openCharacterAnimationPanel(createLayer({ id: 'layer-plain' }))
        }
      >
        打开普通动画
      </button>
      <button
        type="button"
        onClick={() =>
          dialogs.setGenerateDialog((currentDialog) =>
            currentDialog
              ? { ...currentDialog, prompt: '一张生成图' }
              : currentDialog,
          )
        }
      >
        填写生成提示词
      </button>
      <button
        type="button"
        onClick={() => {
          if (activeDialog) {
            void workflow.submitImageGeneration(activeDialog);
          }
        }}
      >
        提交生成
      </button>
      <button
        type="button"
        onClick={() =>
          workflow.setQuickEditPanel((currentPanel) =>
            currentPanel ? { ...currentPanel, prompt: '快速修图' } : currentPanel,
          )
        }
      >
        填写快速编辑
      </button>
      <button
        type="button"
        onClick={() => void workflow.submitQuickEdit()}
      >
        提交快速编辑
      </button>
      <button
        type="button"
        onClick={() => workflow.clearDeletedLayerGenerationState('layer-source')}
      >
        清理源图状态
      </button>
      <button
        type="button"
        onClick={() => {
          if (activeCanvasDialog) {
            dialogs.updateCanvasGenerationDialogById(
              activeCanvasDialog.id,
              (dialog) => ({
                ...dialog,
                generatedLayerId: 'layer-source',
              }),
            );
          }
        }}
      >
        绑定生成图层
      </button>
      <button type="button" onClick={workflow.hideGeneratedLayerPanelAfterBlur}>
        隐藏生成面板
      </button>
    </div>
  );
}

describe('useImageCanvasGenerationWorkflow', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('opens a movable canvas generation placeholder and keeps toolbar state active', () => {
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开生成' }));

    expect(screen.getByTestId('tool').textContent).toBe('generate');
    expect(screen.getByTestId('selected').textContent).toBe('-');
    expect(screen.getByTestId('dialog').textContent).toBe(
      'generate:idle:open:-:placeholder',
    );
  });

  it('submits a normal generation, appends the generated layer, and keeps the composer anchored', async () => {
    generateEditorImageMock.mockResolvedValueOnce(
      createGenerated({ prompt: '一张生成图' }),
    );
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开生成' }));
    fireEvent.click(screen.getByRole('button', { name: '填写生成提示词' }));
    fireEvent.click(screen.getByRole('button', { name: '提交生成' }));

    expect(generateEditorImageMock).toHaveBeenCalledWith({
      prompt: '一张生成图',
    });
    expect(screen.getByTestId('dialog').textContent).toBe(
      'generate:generating:closed:-:placeholder',
    );

    await waitFor(() => {
      expect(screen.getByTestId('layers').textContent).toContain(
        'layer-generated-1:生成图片 1',
      );
    });
    expect(screen.getByTestId('sidebar').textContent).toBe('layers');
    expect(screen.getByTestId('selected').textContent).toBe(
      'layer-generated-1',
    );
    expect(screen.getByTestId('dialog').textContent).toBe(
      'generate:idle:open:layer-generated-1:-',
    );
  });

  it('submits quick edits beside the source and fits source plus result', async () => {
    generateEditorImageMock.mockResolvedValueOnce(
      createGenerated({ prompt: '快速修图' }),
    );
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
    await waitFor(() => {
      expect(screen.getByTestId('quick-edit').textContent).toBe(
        'layer-source:idle:-',
      );
    });
    fireEvent.click(screen.getByRole('button', { name: '填写快速编辑' }));
    await waitFor(() => {
      expect(screen.getByTestId('quick-edit').textContent).toBe(
        'layer-source:idle:快速修图',
      );
    });
    fireEvent.click(screen.getByRole('button', { name: '提交快速编辑' }));

    await waitFor(() => {
      expect(generateEditorImageMock).toHaveBeenCalledWith({
        prompt: '快速修图',
        size: '1024x768',
        kind: 'quick-edit',
        model: 'gpt-image-2',
        referenceImageSrcs: ['data:image/png;base64,source'],
      });
    });

    await waitFor(() => {
      expect(screen.getByTestId('layers').textContent).toContain(
        'layer-quick-edit-1:源图 快速编辑:resource-source',
      );
    });
    expect(screen.getByTestId('quick-edit').textContent).toBe('-');
    expect(screen.getByTestId('tool').textContent).toBe('select');
    expect(screen.getByTestId('fit-count').textContent).toBe('1');
  });

  it('clears generation side panels and linked edit dialogs when a source layer is deleted', async () => {
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
    await waitFor(() => {
      expect(screen.getByTestId('quick-edit').textContent).toBe(
        'layer-source:idle:-',
      );
    });
    fireEvent.click(screen.getByRole('button', { name: '打开修改状态' }));
    fireEvent.click(screen.getByRole('button', { name: '清理源图状态' }));

    expect(screen.getByTestId('quick-edit').textContent).toBe('-');
    expect(screen.getByTestId('dialog').textContent).toBe('-');
  });

  it('only opens the character animation panel for character layers', () => {
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开普通动画' }));
    expect(screen.getByTestId('character-animation').textContent).toBe('-');

    fireEvent.click(screen.getByRole('button', { name: '打开角色动画' }));
    expect(screen.getByTestId('character-animation').textContent).toBe(
      'layer-character:idle',
    );
    expect(screen.getByTestId('image-context').textContent).toBe('-');
  });

  it('hides non-generating canvas generation composers without deleting placeholders', () => {
    render(<GenerationWorkflowHarness />);

    fireEvent.click(screen.getByRole('button', { name: '打开生成' }));
    fireEvent.click(screen.getByRole('button', { name: '隐藏生成面板' }));

    expect(screen.getByTestId('dialog').textContent).toBe(
      'generate:idle:closed:-:placeholder',
    );
  });
});
