/* @vitest-environment jsdom */

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

import type { CanvasLayer, CanvasViewport } from './ImageCanvasEditorTypes';
import { useImageCanvasProjectPersistence } from './useImageCanvasProjectPersistence';

const createEditorProjectResourceMock = vi.hoisted(() => vi.fn());
const loadEditorProjectMock = vi.hoisted(() => vi.fn());
const loadOrCreateRecentEditorProjectMock = vi.hoisted(() => vi.fn());
const saveEditorProjectLayoutMock = vi.hoisted(() => vi.fn());

vi.mock('../../services/image-editor/editorProjectClient', async () => {
  const actual = await vi.importActual<
    typeof import('../../services/image-editor/editorProjectClient')
  >('../../services/image-editor/editorProjectClient');
  return {
    ...actual,
    createEditorProjectResource: createEditorProjectResourceMock,
    loadEditorProject: loadEditorProjectMock,
    loadOrCreateRecentEditorProject: loadOrCreateRecentEditorProjectMock,
    saveEditorProjectLayout: saveEditorProjectLayoutMock,
  };
});

function createLayer(id: string): CanvasLayer {
  return {
    id,
    resourceId: `local-${id}`,
    title: '账号素材A',
    src: 'data:image/png;base64,YQ==',
    x: 10,
    y: 20,
    width: 320,
    height: 240,
    originalWidth: 320,
    originalHeight: 240,
    zIndex: 3,
    sourceType: 'uploaded',
    sourceAssetId: 'asset-a',
  };
}

function ProjectPersistenceHarness() {
  const [layers, setLayers] = useState<CanvasLayer[]>([]);
  const [viewport, setViewport] = useState<CanvasViewport>({
    x: 0,
    y: 0,
    scale: 1,
  });
  const [projectTitle, setProjectTitle] = useState('');
  const [projectRenameValue, setProjectRenameValue] = useState('');
  const layersRef = useRef(layers);
  const viewportRef = useRef(viewport);
  const selectedLayerRef = useRef<string | null>(null);
  const layerCounterRef = useRef(0);

  layersRef.current = layers;
  viewportRef.current = viewport;

  const persistence = useImageCanvasProjectPersistence({
    refs: {
      layersRef,
      viewportRef,
    },
    setters: {
      setProjectTitle,
      setProjectRenameValue,
      setViewport,
      setLayers,
      selectSingleLayer: (layerId) => {
        selectedLayerRef.current = layerId;
      },
      setLayerCounter: (value) => {
        layerCounterRef.current = value;
      },
    },
    layers,
    viewport,
    openEditorLoginModal: vi.fn(),
  });

  return (
    <div>
      <span data-testid="project-id">{persistence.projectId ?? '-'}</span>
      <span data-testid="project-title">{projectTitle}</span>
      <span data-testid="project-rename">{projectRenameValue}</span>
      <span data-testid="layers">
        {layers.map((layer) => `${layer.id}:${layer.resourceId}`).join(',')}
      </span>
      <span data-testid="selected">{selectedLayerRef.current ?? '-'}</span>
      <span data-testid="counter">{layerCounterRef.current}</span>
      <button
        type="button"
        onClick={() => {
          persistence.appendCanvasLayersWithResources([createLayer('layer-a')]);
        }}
      >
        append
      </button>
    </div>
  );
}

describe('useImageCanvasProjectPersistence', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    loadOrCreateRecentEditorProjectMock.mockResolvedValue({
      projectId: 'editor-project-default',
      title: '空画布项目',
      viewport: { x: 0, y: 0, scale: 1 },
      layers: [],
      resources: [],
      updatedAt: '2026-06-12T00:00:00.000Z',
    });
    createEditorProjectResourceMock.mockResolvedValue({
      resourceId: 'resource-added-asset-a',
      projectId: 'editor-project-default',
      imageSrc: 'data:image/png;base64,YQ==',
      width: 320,
      height: 240,
      sourceType: 'uploaded',
    });
    saveEditorProjectLayoutMock.mockResolvedValue({
      projectId: 'editor-project-default',
      title: '空画布项目',
      viewport: { x: 0, y: 0, scale: 1 },
      layers: [],
      resources: [],
      updatedAt: '2026-06-12T00:00:00.000Z',
    });
  });

  it('saves appended layers with the server resource id immediately after resource creation', async () => {
    render(<ProjectPersistenceHarness />);

    expect(await screen.findByText('editor-project-default')).toBeTruthy();
    expect(screen.getByTestId('project-title').textContent).toBe('空画布项目');
    expect(screen.getByTestId('project-rename').textContent).toBe(
      '空画布项目',
    );

    act(() => {
      screen.getByRole('button', { name: 'append' }).click();
    });

    expect(screen.getByTestId('layers').textContent).toBe(
      'layer-a:local-layer-a',
    );
    await waitFor(() => {
      expect(screen.getByTestId('layers').textContent).toBe(
        'layer-a:resource-added-asset-a',
      );
    });
    await waitFor(() => {
      expect(saveEditorProjectLayoutMock).toHaveBeenCalledWith(
        'editor-project-default',
        expect.objectContaining({
          layers: expect.arrayContaining([
            expect.objectContaining({
              layerId: 'layer-a',
              resourceId: 'resource-added-asset-a',
              sourceAssetId: 'asset-a',
            }),
          ]),
        }),
      );
    });
  });
});
