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

import { SearchProgressDialog } from '@/components/shared/SearchProgressDialog';
import { fetchSearchStatus } from '@/services/automationService';

vi.mock('@/services/automationService', async () => {
  const actual = await vi.importActual<typeof import('@/services/automationService')>('@/services/automationService');
  return {
    ...actual,
    fetchSearchStatus: vi.fn(),
    pauseSearchBatch: vi.fn(),
    resumeSearchBatch: vi.fn(),
    stopSearchBatch: vi.fn(),
  };
});

vi.mock('sonner', () => ({
  toast: {
    info: vi.fn(),
    success: vi.fn(),
    error: vi.fn(),
  },
}));

describe('SearchProgressDialog', () => {
  const fetchSearchStatusMock = vi.mocked(fetchSearchStatus);

  it('renders an empty state when the current user has no visible search task', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: '',
      status: 'idle',
      batchId: '',
      accountId: '',
      totalRows: 0,
      processedRows: 0,
      completedRows: 0,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: '',
      currentItem: '',
      errors: [],
      result: null,
      startedAt: null,
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId={null}
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('当前没有可查看的搜索任务')).toBeInTheDocument();
      expect(screen.getByText(/启动新任务后，可以在这里查看进度/)).toBeInTheDocument();
    });
  });

  it('renders processing controls for an active search task', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-001',
      status: 'processing',
      batchId: 'CG-TEST-001',
      accountId: 'account-001',
      totalRows: 10,
      processedRows: 2,
      completedRows: 2,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-002',
      currentItem: '正在处理第 3 行',
      errors: [],
      result: null,
      startedAt: '2026-04-21T08:00:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-001"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('搜索进行中')).toBeInTheDocument();
      expect(screen.getByRole('button', { name: '暂停搜索' })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: '停止搜索' })).toBeInTheDocument();
    });
  });

  it('renders LLM matching step text and display progress', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-llm',
      status: 'processing',
      batchId: 'CG-TEST-LLM',
      accountId: 'account-llm',
      totalRows: 1,
      processedRows: 0,
      completedRows: 0,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-llm',
      currentItem: '正在搜索: 测试物料',
      currentStepLabel: '正在调用大模型判断商品匹配，请稍候...',
      displayProgressPercent: 45,
      searchProgress: {
        message: '正在调用大模型判断商品匹配，请稍候...',
        materialId: 'material-llm',
        materialName: '测试物料',
        itemProgressPercent: 45,
        softCapPercent: null,
        stageStartedAt: '2026-04-22T08:00:00.000Z',
        updatedAt: '2026-04-22T08:00:00.000Z',
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T07:50:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-llm"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('正在调用大模型判断商品匹配，请稍候...')).toBeInTheDocument();
      expect(screen.getByText('45%')).toBeInTheDocument();
    });
  });

  it('renders realtime search metrics and hides missing metric fields', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-metrics',
      status: 'processing',
      batchId: 'CG-TEST-METRICS',
      accountId: 'account-metrics',
      totalRows: 1,
      processedRows: 0,
      completedRows: 0,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-metrics',
      currentItem: '正在补齐商家电话',
      currentStepLabel: '正在按大模型匹配顺序补齐商家电话/手机...',
      displayProgressPercent: 88,
      searchProgress: {
        message: '正在按大模型匹配顺序补齐商家电话/手机...',
        materialId: 'material-metrics',
        materialName: '测试物料',
        itemProgressPercent: 88,
        softCapPercent: 95,
        stageStartedAt: '2026-04-22T08:00:00.000Z',
        metrics: {
          candidateCount: 128,
          nameMatchedCount: 17,
          qualifiedResultCount: 3,
          targetResultCount: 5,
        },
        updatedAt: '2026-04-22T08:00:00.000Z',
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T07:50:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-metrics"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('已获取商品：').parentElement).toHaveTextContent('已获取商品：128');
      expect(screen.getByText('名称匹配：').parentElement).toHaveTextContent('名称匹配：17');
      expect(screen.getByText('有效商家：').parentElement).toHaveTextContent('有效商家：3 / 5');
      expect(screen.queryByText(/已判断/)).not.toBeInTheDocument();
    });
  });

  it('renders completed and running sub-stage durations', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-step-timing',
      status: 'processing',
      batchId: 'CG-TEST-STEPS',
      accountId: 'account-steps',
      totalRows: 1,
      processedRows: 0,
      completedRows: 0,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-steps',
      currentItem: '正在处理小阶段',
      currentStepLabel: '正在按大模型匹配顺序补齐商家电话/手机...',
      displayProgressPercent: 86,
      searchProgress: null,
      stageTiming: {
        currentStage: {
          taskId: 'task-step-timing',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 1,
          stageLabel: '关键词搜索（AI 自动多轮）',
          stageKind: 'keyword',
          startedAt: '2026-04-22T08:00:00.000Z',
          endedAt: null,
          durationMs: null,
          elapsedMs: 120000,
          status: 'running',
        },
        completedStages: [{
          taskId: 'task-step-timing',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 1,
          stageLabel: '关键词搜索（AI 自动多轮）',
          stageKind: 'keyword',
          startedAt: '2026-04-22T08:00:00.000Z',
          endedAt: '2026-04-22T08:01:30.000Z',
          durationMs: 90000,
          elapsedMs: 90000,
          status: 'completed',
        }],
        currentStep: {
          taskId: 'task-step-timing',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 1,
          stageLabel: '关键词搜索（AI 自动多轮）',
          stageKind: 'keyword',
          stepKey: 'contact_enrichment',
          stepLabel: '联系方式补全/有效商家筛选',
          startedAt: '2026-04-22T08:01:30.000Z',
          endedAt: null,
          durationMs: null,
          elapsedMs: 30000,
          status: 'running',
        },
        completedSteps: [{
          taskId: 'task-step-timing',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 1,
          stageLabel: '关键词搜索（AI 自动多轮）',
          stageKind: 'keyword',
          stepKey: 'llm_match',
          stepLabel: 'LLM 商品匹配候选筛选',
          startedAt: '2026-04-22T08:00:30.000Z',
          endedAt: '2026-04-22T08:01:20.000Z',
          durationMs: 50000,
          elapsedMs: 50000,
          status: 'completed',
        }],
        totalElapsedMs: 130000,
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T08:00:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-step-timing"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('LLM 商品匹配候选筛选')).toBeInTheDocument();
      expect(screen.getByText(/进行中 · 联系方式补全\/有效商家筛选/)).toBeInTheDocument();
      expect(screen.getByText(/进行中 · 联系方式补全\/有效商家筛选/).closest('div')).toHaveClass('border-blue-200');
      expect(screen.getByText('总耗时 2分10秒')).toBeInTheDocument();
      expect(screen.getByText('50秒')).toBeInTheDocument();
    });
  });

  it('renders completed stage and sub-stage durations as modular rows', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-completed-step-timing',
      status: 'completed',
      batchId: 'CG-TEST-COMPLETED-STEPS',
      accountId: 'account-steps',
      totalRows: 1,
      processedRows: 1,
      completedRows: 1,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-steps',
      currentItem: '搜索完成',
      displayProgressPercent: 100,
      searchProgress: null,
      stageTiming: {
        currentStage: null,
        completedStages: [{
          taskId: 'task-completed-step-timing',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 2,
          stageLabel: '商品筛选',
          stageKind: 'matching',
          startedAt: '2026-04-22T08:00:00.000Z',
          endedAt: '2026-04-22T08:14:06.000Z',
          durationMs: 846000,
          elapsedMs: 846000,
          status: 'completed',
        }],
        currentStep: null,
        completedSteps: [
          {
            taskId: 'task-completed-step-timing',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 2,
            stageLabel: '商品筛选',
            stageKind: 'matching',
            stepKey: 'candidate_collect',
            stepLabel: '候选商品收集',
            startedAt: '2026-04-22T08:00:00.000Z',
            endedAt: '2026-04-22T08:07:35.000Z',
            durationMs: 455000,
            elapsedMs: 455000,
            status: 'completed',
          },
          {
            taskId: 'task-completed-step-timing',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 2,
            stageLabel: '商品筛选',
            stageKind: 'matching',
            stepKey: 'candidate_dedupe',
            stepLabel: '候选商品去重',
            startedAt: '2026-04-22T08:07:35.000Z',
            endedAt: '2026-04-22T08:07:48.000Z',
            durationMs: 13000,
            elapsedMs: 13000,
            status: 'completed',
          },
          {
            taskId: 'task-completed-step-timing',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 2,
            stageLabel: '商品筛选',
            stageKind: 'matching',
            stepKey: 'llm_match',
            stepLabel: 'LLM 商品匹配候选筛选',
            startedAt: '2026-04-22T08:07:48.000Z',
            endedAt: '2026-04-22T08:08:57.000Z',
            durationMs: 69000,
            elapsedMs: 69000,
            status: 'completed',
          },
          {
            taskId: 'task-completed-step-timing',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 2,
            stageLabel: '商品筛选',
            stageKind: 'matching',
            stepKey: 'contact_enrichment',
            stepLabel: '联系方式补全/有效商家筛选',
            startedAt: '2026-04-22T08:08:57.000Z',
            endedAt: '2026-04-22T08:11:05.000Z',
            durationMs: 128000,
            elapsedMs: 128000,
            status: 'completed',
          },
        ],
        totalElapsedMs: 846000,
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T08:00:00.000Z',
      pausedAt: null,
      finishedAt: '2026-04-22T08:14:06.000Z',
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-completed-step-timing"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.queryByText('阶段 2 · 商品筛选')).not.toBeInTheDocument();
      expect(screen.getByText('总耗时 14分06秒')).toBeInTheDocument();
      expect(screen.getByText('候选商品收集')).toBeInTheDocument();
      expect(screen.getByText('候选商品去重')).toBeInTheDocument();
      expect(screen.getByText('LLM 商品匹配候选筛选')).toBeInTheDocument();
      expect(screen.getByText('联系方式补全/有效商家筛选')).toBeInTheDocument();
      expect(screen.getByText('7分35秒')).toBeInTheDocument();
      expect(screen.getByText('13秒')).toBeInTheDocument();
      expect(screen.getByText('1分09秒')).toBeInTheDocument();
      expect(screen.getByText('2分08秒')).toBeInTheDocument();
      expect(screen.getByText('候选商品收集').closest('.border-l')).toBeNull();
      const dialog = screen.getByRole('dialog');
      expect(dialog.className).toContain('w-[calc(100vw-2rem)]');
      expect(dialog).toHaveClass('overflow-x-hidden');
      const timingItem = screen.getByText('候选商品收集').closest('div');
      expect(timingItem).toHaveClass('min-w-0');
      expect(screen.getByText('候选商品收集')).toHaveClass('truncate');
      expect(timingItem?.parentElement?.className).toContain('grid-cols-[minmax(0,1fr)_minmax(0,1fr)]');
    });
  });

  it('renders sub-stage timing when completed stages are absent', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-steps-only',
      status: 'completed',
      batchId: 'CG-TEST-STEPS-ONLY',
      accountId: 'account-steps',
      totalRows: 1,
      processedRows: 1,
      completedRows: 1,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-steps',
      currentItem: '搜索完成',
      displayProgressPercent: 100,
      searchProgress: null,
      stageTiming: {
        currentStage: null,
        completedStages: [],
        currentStep: null,
        completedSteps: [{
          taskId: 'task-steps-only',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 3,
          stageLabel: '联系方式补全',
          stageKind: 'contact',
          stepKey: 'contact_enrichment',
          stepLabel: '联系方式补全/有效商家筛选',
          startedAt: '2026-04-22T08:00:00.000Z',
          endedAt: '2026-04-22T08:02:08.000Z',
          durationMs: 128000,
          elapsedMs: 128000,
          status: 'completed',
        }],
        totalElapsedMs: 128000,
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T08:00:00.000Z',
      pausedAt: null,
      finishedAt: '2026-04-22T08:02:08.000Z',
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-steps-only"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.queryByText('阶段 3 · 联系方式补全')).not.toBeInTheDocument();
      expect(screen.queryByText('小阶段 1')).not.toBeInTheDocument();
      expect(screen.getByText('联系方式补全/有效商家筛选')).toBeInTheDocument();
      expect(screen.getByText('2分08秒')).toBeInTheDocument();
    });
  });

  it('hides completed sub-stage durations that round down to zero seconds', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-step-zero',
      status: 'completed',
      batchId: 'CG-TEST-STEPS',
      accountId: 'account-steps',
      totalRows: 1,
      processedRows: 1,
      completedRows: 1,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-steps',
      currentItem: '搜索完成',
      displayProgressPercent: 100,
      searchProgress: null,
      stageTiming: {
        currentStage: null,
        completedStages: [{
          taskId: 'task-step-zero',
          materialId: 'material-steps',
          materialName: '测试物料',
          stageOrder: 1,
          stageLabel: '关键词搜索（AI 自动多轮）',
          stageKind: 'keyword',
          startedAt: '2026-04-22T08:00:00.000Z',
          endedAt: '2026-04-22T08:15:05.000Z',
          durationMs: 905000,
          elapsedMs: 905000,
          status: 'completed',
        }],
        currentStep: null,
        completedSteps: [
          {
            taskId: 'task-step-zero',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 1,
            stageLabel: '关键词搜索（AI 自动多轮）',
            stageKind: 'keyword',
            stepKey: 'keyword_generation',
            stepLabel: '关键词生成/读取',
            startedAt: '2026-04-22T08:00:00.000Z',
            endedAt: '2026-04-22T08:00:00.300Z',
            durationMs: 300,
            elapsedMs: 300,
            status: 'completed',
          },
          {
            taskId: 'task-step-zero',
            materialId: 'material-steps',
            materialName: '测试物料',
            stageOrder: 1,
            stageLabel: '关键词搜索（AI 自动多轮）',
            stageKind: 'keyword',
            stepKey: 'initial_search',
            stepLabel: '1688 初始搜索',
            startedAt: '2026-04-22T08:00:01.000Z',
            endedAt: '2026-04-22T08:06:04.000Z',
            durationMs: 363000,
            elapsedMs: 363000,
            status: 'completed',
          },
        ],
        totalElapsedMs: 956000,
      },
      errors: [],
      result: null,
      startedAt: '2026-04-22T08:00:00.000Z',
      pausedAt: null,
      finishedAt: '2026-04-22T08:15:56.000Z',
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-step-zero"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.queryByText('关键词生成/读取')).not.toBeInTheDocument();
      expect(screen.getByText('1688 初始搜索')).toBeInTheDocument();
      expect(screen.queryByText('0秒')).not.toBeInTheDocument();
    });
  });

  it('renders pause controls while waiting for manual intervention', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-002',
      status: 'waiting_for_manual',
      batchId: 'CG-TEST-002',
      accountId: 'account-002',
      totalRows: 10,
      processedRows: 4,
      completedRows: 4,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-004',
      currentItem: '等待人工处理验证码',
      errors: [],
      result: {
        success: false,
        stopped: false,
        processedRows: 4,
        totalRows: 10,
        completedRows: 4,
        failedRows: 0,
        stoppedRows: 0,
        errorCount: 0,
        error: '',
        manualIntervention: {
          active: true,
          taskId: 'task-002',
          batchId: 'CG-TEST-002',
          accountId: 'account-002',
          materialId: 'material-004',
          materialName: '测试物料',
          message: '等待人工处理验证码',
          detectedAt: '2026-04-22T08:00:00.000Z',
        },
      },
      startedAt: '2026-04-22T07:50:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-002"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('等待人工处理')).toBeInTheDocument();
      expect(screen.getByRole('button', { name: '暂停搜索' })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: '停止搜索' })).toBeInTheDocument();
    });
  });

  it('treats waiting_for_manual with inactive manual intervention as processing', async () => {
    fetchSearchStatusMock.mockResolvedValue({
      taskId: 'task-003',
      status: 'waiting_for_manual',
      batchId: 'CG-TEST-003',
      accountId: 'account-003',
      totalRows: 1,
      processedRows: 0,
      completedRows: 0,
      failedRows: 0,
      stoppedRows: 0,
      currentRow: 'material-003',
      currentItem: '登录态已确认，开始搜索...',
      currentStepLabel: '登录态已确认，开始搜索...',
      errors: [],
      result: {
        manualIntervention: {
          active: false,
          taskId: 'task-003',
          batchId: 'CG-TEST-003',
          accountId: 'account-003',
          materialId: 'material-003',
          materialName: '测试物料',
          message: '验证码已解除，继续执行自动搜索',
          detectedAt: '2026-04-28T07:00:00.000Z',
          resolvedAt: '2026-04-28T07:01:00.000Z',
        },
      },
      startedAt: '2026-04-28T07:00:00.000Z',
      pausedAt: null,
      finishedAt: null,
    });

    render(
      <SearchProgressDialog
        open
        onOpenChange={vi.fn()}
        taskId="task-003"
        onTaskIdChange={vi.fn()}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('搜索进行中')).toBeInTheDocument();
      expect(screen.queryByText('等待人工处理')).not.toBeInTheDocument();
      expect(screen.getByText('登录态已确认，开始搜索...')).toBeInTheDocument();
    });
  });
});
