import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ImageWorkspaceApiError } from '@/lib/image-workspace';
import { ImageCanvas } from '@/pages/ImageCanvas';
import { useAuthStore } from '@/stores/auth';
import { useImageWorkspaceStore } from '@/stores/image-workspace';
import type {
  DesignGenerationTask,
  DesignWorkspace,
  DesignWorkspaceBootstrap,
} from '../../shared/image-workspace';

const fetchImageWorkspaceMock = vi.hoisted(() => vi.fn());
const fetchImageWorkspaceProjectMock = vi.hoisted(() => vi.fn());
const fetchImageWorkspaceTasksMock = vi.hoisted(() => vi.fn());
const sendImageWorkspaceMessageMock = vi.hoisted(() => vi.fn());
const confirmImageWorkspaceGenerationMock = vi.hoisted(() => vi.fn());
const resolveImageWorkspaceAssetUrlMock = vi.hoisted(() => vi.fn());

vi.mock('@/lib/image-workspace', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/lib/image-workspace')>();
  return {
    ...actual,
    fetchImageWorkspace: (...args: unknown[]) => fetchImageWorkspaceMock(...args),
    fetchImageWorkspaceProject: (...args: unknown[]) => fetchImageWorkspaceProjectMock(...args),
    fetchImageWorkspaceTasks: (...args: unknown[]) => fetchImageWorkspaceTasksMock(...args),
    sendImageWorkspaceMessage: (...args: unknown[]) => sendImageWorkspaceMessageMock(...args),
    confirmImageWorkspaceGeneration: (...args: unknown[]) => (
      confirmImageWorkspaceGenerationMock(...args)
    ),
    resolveImageWorkspaceAssetUrl: (...args: unknown[]) => (
      resolveImageWorkspaceAssetUrlMock(...args)
    ),
  };
});

const bootstrapFixture: DesignWorkspaceBootstrap = {
  capabilities: {
    conversation: true,
    generation: true,
    image: true,
    video: true,
  },
  workspaces: [{
    workspaceId: 'workspace-cloud',
    title: '云端角色设定',
    turnRevision: 1,
    viewRevision: 1,
    phase: 'awaiting_confirmation',
    brief: {
      version: 1,
      status: 'ready',
      medium: 'image',
      summary: '夜色中的机械城堡角色海报',
      ready: true,
      missingDecision: null,
    },
    updatedAt: '2026-07-31T10:00:00Z',
  }],
};

function workspaceFixture(): DesignWorkspace {
  return {
    ...bootstrapFixture.workspaces[0],
    messages: [
      {
        id: 'message-user',
        role: 'user',
        kind: 'user',
        text: '做一张夜色中的机械城堡角色海报',
        quickReplies: [],
        generationQuote: null,
        turnRevision: 1,
        createdAt: '2026-07-31T10:00:00Z',
      },
      {
        id: 'message-assistant',
        role: 'assistant',
        kind: 'confirmation',
        text: '方向已经明确，确认后开始生成。',
        quickReplies: ['确认生成', '继续调整'],
        generationQuote: {
          quoteId: 'quote-one',
          status: 'active',
          medium: 'image',
          briefVersion: 1,
          briefSummary: '夜色中的机械城堡角色海报',
          quotedDesignPoints: 1,
          expiresAt: '2026-07-31T11:00:00Z',
        },
        turnRevision: 1,
        createdAt: '2026-07-31T10:00:01Z',
      },
    ],
  };
}

const taskFixture: DesignGenerationTask = {
  taskId: 'task-one',
  workspaceId: 'workspace-cloud',
  medium: 'image',
  status: 'succeeded',
  briefVersion: 1,
  briefSummary: '夜色中的机械城堡角色海报',
  quoteId: 'quote-old',
  quotedDesignPoints: 1,
  failureCode: null,
  resultAssets: [{
    assetId: 'asset-one',
    workspaceId: 'workspace-cloud',
    mediaType: 'image',
    mimeType: 'image/png',
    width: 1024,
    height: 1024,
    durationMilliseconds: null,
    createdAt: '2026-07-31T09:00:00Z',
    contentPath: '/api/works/image-workspace/workspaces/workspace-cloud/assets/asset-one/content',
  }],
  createdAt: '2026-07-31T09:00:00Z',
  updatedAt: '2026-07-31T09:01:00Z',
};

describe('ImageCanvas Workspace-first design experience', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    useImageWorkspaceStore.getState().reset();
    fetchImageWorkspaceMock.mockResolvedValue(bootstrapFixture);
    fetchImageWorkspaceProjectMock.mockResolvedValue(workspaceFixture());
    fetchImageWorkspaceTasksMock.mockResolvedValue([taskFixture]);
    sendImageWorkspaceMessageMock.mockResolvedValue(workspaceFixture());
    confirmImageWorkspaceGenerationMock.mockResolvedValue({
      ...workspaceFixture(),
      turnRevision: 2,
      phase: 'shaping',
    });
    resolveImageWorkspaceAssetUrlMock.mockResolvedValue(
      'http://127.0.0.1:13210/content?token=host',
    );
  });

  it('shows an honest unavailable state without fabricating projects', async () => {
    fetchImageWorkspaceMock.mockRejectedValueOnce(new ImageWorkspaceApiError(
      501,
      'IMAGE_WORKSPACE_UNAVAILABLE',
      'AI 设计暂不可用',
    ));

    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);

    expect(await screen.findByTestId('image-workspace-unavailable'))
      .toHaveTextContent('AI 设计暂不可用');
    expect(screen.getByText(/暂时无法连接设计服务/)).toBeInTheDocument();
  });

  it('clears stale renderer auth when Main requires authentication', async () => {
    useAuthStore.setState({
      initialized: true,
      loading: false,
      error: null,
      accessToken: 'expired-access-token',
      refreshToken: 'invalid-refresh-token',
      tokenType: 'Bearer',
      expiresAt: Date.now() - 1,
      user: {
        username: 'brother7',
        userId: '1',
        tenantId: null,
        deptId: null,
        authorities: [],
      },
    });
    fetchImageWorkspaceMock.mockRejectedValueOnce(new ImageWorkspaceApiError(
      401,
      'AUTH_REQUIRED',
      '请先登录后再使用 AI 设计',
    ));

    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);

    await screen.findByTestId('image-workspace-unavailable');
    expect(useAuthStore.getState()).toMatchObject({
      accessToken: null,
      refreshToken: null,
      user: null,
    });
    expect(useImageWorkspaceStore.getState().status).toBe('auth-required');

    useAuthStore.setState({
      accessToken: 'fresh-access-token',
      refreshToken: 'fresh-refresh-token',
      tokenType: 'Bearer',
      expiresAt: Date.now() + 60_000,
      user: {
        username: 'brother7',
        userId: '1',
        tenantId: null,
        deptId: null,
        authorities: [],
      },
    });

    await waitFor(() => expect(fetchImageWorkspaceMock).toHaveBeenCalledTimes(2));
    await waitFor(() => expect(useImageWorkspaceStore.getState().status).toBe('ready'));
  });

  it('renders one fixed design Agent, a Quote, and the unified image/video task list', async () => {
    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);

    expect(await screen.findByText('云端角色设定')).toBeInTheDocument();
    expect(screen.getByTestId('image-workspace-conversation'))
      .toHaveTextContent('方向已经明确');
    expect(screen.getByText('设计 Agent')).toBeInTheDocument();
    expect(screen.getByTestId('design-quote-quote-one')).toHaveTextContent('1 设计点');
    expect(screen.getByTestId('design-task-list')).toHaveTextContent('图片与视频任务统一展示');
    expect(screen.getByTestId('design-task-task-one')).toHaveTextContent('已完成');
    await waitFor(() => expect(screen.getByRole('img', { name: 'AI 设计生成结果' }))
      .toHaveAttribute('src', 'http://127.0.0.1:13210/content?token=host'));
    expect(screen.queryByText('生成设置')).not.toBeInTheDocument();
    expect(screen.queryByText(/当前 Agent/)).not.toBeInTheDocument();
  });

  it('keeps the task list visible in a 1404px desktop workspace', async () => {
    const previousWidth = window.innerWidth;
    Object.defineProperty(window, 'innerWidth', { configurable: true, value: 1404 });

    try {
      render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
      await screen.findByTestId('image-workspace-conversation');

      expect(screen.getByTestId('design-task-list')).toBeInTheDocument();
      expect(screen.getByTestId('design-pane-divider')).toHaveClass('lg:block');
    } finally {
      Object.defineProperty(window, 'innerWidth', {
        configurable: true,
        value: previousWidth,
      });
    }
  });

  it('keeps conversation and task scrolling isolated', async () => {
    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
    await screen.findByTestId('image-workspace-conversation');

    expect(screen.getByTestId('image-canvas-page'))
      .toHaveClass('h-full', 'overflow-hidden');
    expect(screen.getByTestId('image-workspace-conversation'))
      .toHaveClass('overflow-y-auto', 'overscroll-contain');
    expect(screen.getByTestId('design-task-list'))
      .toHaveClass('lg:h-full', 'overflow-hidden');
    expect(screen.getByTestId('design-task-scroll'))
      .toHaveClass('lg:h-[calc(100%-57px)]', 'overflow-y-auto', 'overscroll-contain');
  });

  it('explains the project model before the first Workspace is created', async () => {
    fetchImageWorkspaceMock.mockResolvedValueOnce({
      ...bootstrapFixture,
      workspaces: [],
    });

    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);

    expect(await screen.findByText('创建第一个设计项目')).toBeInTheDocument();
    expect(screen.getByText(/持续保留与设计 Agent 的对话/)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: '新建设计项目' })).toBeInTheDocument();
  });

  it('sends text to the Agent with the current Turn revision instead of generating directly', async () => {
    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
    await screen.findByText('云端角色设定');

    fireEvent.change(screen.getByLabelText('设计需求'), {
      target: { value: '把主角换成暖色轮廓光' },
    });
    fireEvent.click(screen.getByRole('button', { name: '发送给设计 Agent' }));

    await waitFor(() => expect(sendImageWorkspaceMessageMock).toHaveBeenCalledWith(
      'workspace-cloud',
      1,
      '把主角换成暖色轮廓光',
    ));
    expect(confirmImageWorkspaceGenerationMock).not.toHaveBeenCalled();
  });

  it('refreshes tasks after an Agent turn creates a generation task', async () => {
    const queuedTask = {
      ...taskFixture,
      taskId: 'task-two',
      status: 'queued' as const,
      resultAssets: [],
    };
    fetchImageWorkspaceTasksMock
      .mockResolvedValueOnce([])
      .mockResolvedValueOnce([queuedTask]);

    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
    await screen.findByTestId('design-quote-quote-one');

    const confirmationReply = workspaceFixture().messages[1].quickReplies[0];
    fireEvent.change(screen.getByLabelText('设计需求'), {
      target: { value: confirmationReply },
    });
    fireEvent.click(screen.getByRole('button', { name: '发送给设计 Agent' }));

    await waitFor(() => expect(sendImageWorkspaceMessageMock).toHaveBeenCalledWith(
      'workspace-cloud',
      1,
      confirmationReply,
    ));
    await waitFor(() => expect(fetchImageWorkspaceTasksMock).toHaveBeenCalledTimes(2));
    expect(await screen.findByTestId('design-task-task-two')).toBeInTheDocument();
  });

  it('creates a generation task only through explicit Quote confirmation', async () => {
    fetchImageWorkspaceTasksMock
      .mockResolvedValueOnce([taskFixture])
      .mockResolvedValueOnce([{
        ...taskFixture,
        taskId: 'task-two',
        status: 'queued',
        resultAssets: [],
      }]);
    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
    await screen.findByTestId('design-quote-quote-one');

    fireEvent.click(screen.getByRole('button', { name: '确认并开始生成' }));

    await waitFor(() => expect(confirmImageWorkspaceGenerationMock)
      .toHaveBeenCalledWith('workspace-cloud', 1, 'quote-one'));
    await waitFor(() => expect(fetchImageWorkspaceTasksMock).toHaveBeenCalledTimes(2));
  });

  it('keeps checking after Quote confirmation until the new task becomes visible', async () => {
    const queuedTask = {
      ...taskFixture,
      taskId: 'task-delayed',
      status: 'queued' as const,
      resultAssets: [],
    };
    fetchImageWorkspaceTasksMock
      .mockResolvedValueOnce([])
      .mockResolvedValueOnce([])
      .mockResolvedValueOnce([queuedTask]);

    render(<MemoryRouter><ImageCanvas /></MemoryRouter>);
    await screen.findByTestId('design-quote-quote-one');

    fireEvent.click(screen.getByRole('button', { name: '确认并开始生成' }));

    await waitFor(() => expect(confirmImageWorkspaceGenerationMock)
      .toHaveBeenCalledWith('workspace-cloud', 1, 'quote-one'));
    await waitFor(() => expect(fetchImageWorkspaceTasksMock).toHaveBeenCalledTimes(3));
    expect(await screen.findByTestId('design-task-task-delayed'))
      .toBeInTheDocument();
  });
});
