/**
 * 单元测试：TriggerManager 页面
 *
 * _Requirements: 4.1, 4.3, 4.4, 4.7_
 *
 * - 测试 4 个 Tab 正确渲染
 * - 测试"全部"Tab 为只读（无编辑按钮）
 * - 测试"业务"Tab 编辑表单仅包含 cron_expression 和 interval_seconds
 * - 测试 422 错误在表单中展示具体错误信息
 */

import { describe, it, expect, vi, beforeAll, afterEach } from "vitest";
import { render, screen, cleanup, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import { MemoryRouter } from "react-router-dom";

/* ------------------------------------------------------------------ */
/*  Ant Design jsdom 兼容：polyfill window.matchMedia                   */
/* ------------------------------------------------------------------ */

beforeAll(() => {
  Object.defineProperty(window, "matchMedia", {
    writable: true,
    value: vi.fn().mockImplementation((query: string) => ({
      matches: false,
      media: query,
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn(),
    })),
  });
});

/* ------------------------------------------------------------------ */
/*  Mock API 模块                                                       */
/* ------------------------------------------------------------------ */

const mockFetchUnifiedTriggers = vi.fn().mockResolvedValue([
  {
    id: 1,
    name: "同步会员数据",
    source: "biz",
    trigger_condition: "cron",
    status: "running",
    last_run_at: "2026-07-15T10:00:00",
    next_run_at: "2026-07-15T12:00:00",
    last_error: null,
  },
  {
    id: 2,
    name: "AI 事件链",
    source: "ai",
    trigger_condition: "event",
    status: "idle",
    last_run_at: null,
    next_run_at: null,
    last_error: null,
  },
]);

const mockFetchTriggerJobs = vi.fn().mockResolvedValue([
  {
    id: 101,
    job_type: "sync",
    job_name: "sync_members",
    trigger_condition: "cron",
    trigger_config: { cron_expression: "0 */2 * * *", interval_seconds: 7200 },
    last_run_at: "2026-07-15T10:00:00",
    next_run_at: "2026-07-15T12:00:00",
    status: "enabled",
    description: "同步会员数据",
    last_error: null,
    created_at: "2026-01-01T00:00:00",
  },
]);

const mockUpdateTriggerConfig = vi.fn().mockResolvedValue({
  id: 101,
  job_type: "sync",
  job_name: "sync_members",
  trigger_condition: "cron",
  trigger_config: { cron_expression: "0 */3 * * *", interval_seconds: 7200 },
  last_run_at: "2026-07-15T10:00:00",
  next_run_at: "2026-07-15T15:00:00",
  status: "enabled",
  description: "同步会员数据",
  last_error: null,
  created_at: "2026-01-01T00:00:00",
});

const mockFetchSchedules = vi.fn().mockResolvedValue([]);

vi.mock("../api/triggers", () => ({
  fetchUnifiedTriggers: (...args: unknown[]) => mockFetchUnifiedTriggers(...args),
}));

vi.mock("../api/triggerJobs", () => ({
  fetchTriggerJobs: (...args: unknown[]) => mockFetchTriggerJobs(...args),
  updateTriggerConfig: (...args: unknown[]) => mockUpdateTriggerConfig(...args),
}));

vi.mock("../api/schedules", () => ({
  fetchSchedules: (...args: unknown[]) => mockFetchSchedules(...args),
}));

vi.mock("../pages/AIOperations", () => ({
  default: () => <div data-testid="mock-ai-operations">AIOperations Mock</div>,
}));

vi.mock("../pages/AITriggerJobs", () => ({
  default: () => <div data-testid="mock-ai-trigger-jobs">AITriggerJobs Mock</div>,
}));

import TriggerManager from "../pages/TriggerManager";

afterEach(() => {
  cleanup();
  vi.clearAllMocks();
});

/* ------------------------------------------------------------------ */
/*  测试：4 个 Tab 正确渲染（Requirement 4.1）                           */
/* ------------------------------------------------------------------ */

describe("TriggerManager — 4 个 Tab 渲染 (Requirement 4.1)", () => {
  it("渲染 4 个 Tab 标签：全部、业务、AI、ETL", async () => {
    render(
      <MemoryRouter initialEntries={["/triggers"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    await waitFor(() => {
      const tabs = document.querySelectorAll(".ant-tabs-tab");
      expect(tabs).toHaveLength(4);
    });

    const tabs = document.querySelectorAll(".ant-tabs-tab");
    const tabTexts = Array.from(tabs).map((t) => t.textContent);
    expect(tabTexts).toContain("全部");
    expect(tabTexts).toContain("业务");
    expect(tabTexts).toContain("AI");
    expect(tabTexts).toContain("ETL");
  });

  it("默认激活「全部」Tab", () => {
    render(
      <MemoryRouter initialEntries={["/triggers"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    const activeTab = document.querySelector(".ant-tabs-tab-active");
    expect(activeTab).not.toBeNull();
    expect(activeTab!.textContent).toContain("全部");
  });

  it("页面标题包含「触发器管理」", () => {
    render(
      <MemoryRouter initialEntries={["/triggers"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    expect(screen.getByText("触发器管理")).toBeInTheDocument();
  });
});

/* ------------------------------------------------------------------ */
/*  测试："全部"Tab 为只读（Requirement 4.3）                            */
/* ------------------------------------------------------------------ */

describe("TriggerManager — 全部 Tab 只读 (Requirement 4.3)", () => {
  it("「全部」Tab 表格中无「编辑」按钮", async () => {
    render(
      <MemoryRouter initialEntries={["/triggers?tab=all"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    // 等待统一视图数据加载完成
    await waitFor(() => {
      expect(mockFetchUnifiedTriggers).toHaveBeenCalled();
    });

    // 等待表格渲染数据
    await waitFor(() => {
      expect(screen.getByText("同步会员数据")).toBeInTheDocument();
    });

    // 全部 Tab 不应有编辑按钮
    const editButtons = screen.queryAllByRole("button", { name: /编辑/ });
    expect(editButtons).toHaveLength(0);
  });
});

/* ------------------------------------------------------------------ */
/*  测试："业务"Tab 编辑表单字段（Requirement 4.4, 4.7）                  */
/* ------------------------------------------------------------------ */

describe("TriggerManager — 业务 Tab 编辑表单 (Requirements 4.4, 4.7)", () => {
  it("编辑 Modal 仅包含 cron_expression 和 interval_seconds 两个字段", async () => {
    render(
      <MemoryRouter initialEntries={["/triggers?tab=biz"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    // 等待业务触发器数据加载
    await waitFor(() => {
      expect(mockFetchTriggerJobs).toHaveBeenCalled();
    });

    // 等待表格渲染
    await waitFor(() => {
      expect(screen.getByText("同步会员数据")).toBeInTheDocument();
    });

    // 点击编辑按钮
    const editBtn = screen.getByRole("button", { name: /编辑/ });
    fireEvent.click(editBtn);

    // 等待 Modal 打开
    await waitFor(() => {
      expect(screen.getByText(/编辑触发器配置/)).toBeInTheDocument();
    });

    // 验证表单包含 cron_expression 字段
    expect(screen.getByLabelText(/Cron 表达式/)).toBeInTheDocument();

    // 验证表单包含 interval_seconds 字段
    expect(screen.getByLabelText(/间隔秒数/)).toBeInTheDocument();

    // 验证 Modal 中的 Form.Item 数量 — 只有 2 个
    const modal = document.querySelector(".ant-modal-body");
    expect(modal).not.toBeNull();
    const formItems = modal!.querySelectorAll(".ant-form-item");
    expect(formItems).toHaveLength(2);
  });
});

/* ------------------------------------------------------------------ */
/*  测试：422 错误展示具体错误信息（Requirement 4.7）                     */
/* ------------------------------------------------------------------ */

describe("TriggerManager — 422 错误展示 (Requirement 4.7)", () => {
  it("422 错误时调用 updateTriggerConfig 并提取 detail 信息", async () => {
    const error422 = {
      response: {
        status: 422,
        data: { detail: "cron 表达式格式无效，需要 5 字段格式" },
      },
    };
    mockUpdateTriggerConfig.mockRejectedValueOnce(error422);

    render(
      <MemoryRouter initialEntries={["/triggers?tab=biz"]}>
        <TriggerManager />
      </MemoryRouter>,
    );

    // 等待数据加载
    await waitFor(() => {
      expect(screen.getByText("同步会员数据")).toBeInTheDocument();
    });

    // 点击编辑按钮
    const editBtn = screen.getByRole("button", { name: /编辑/ });
    fireEvent.click(editBtn);

    // 等待 Modal 打开
    await waitFor(() => {
      expect(screen.getByText(/编辑触发器配置/)).toBeInTheDocument();
    });

    // 填写一个无效的 cron 表达式
    const cronInput = screen.getByLabelText(/Cron 表达式/);
    fireEvent.change(cronInput, { target: { value: "invalid-cron" } });

    // 点击保存（Modal 的确定按钮，Ant Design 渲染为"保 存"带空格）
    const okBtn = screen.getByRole("button", { name: /保\s*存/ });
    fireEvent.click(okBtn);

    // 验证 updateTriggerConfig 被调用（触发了 422 错误路径）
    await waitFor(() => {
      expect(mockUpdateTriggerConfig).toHaveBeenCalledWith(101, {
        cron_expression: "invalid-cron",
        interval_seconds: 7200,
      });
    });

    // 验证 422 错误后 Modal 仍然打开（未关闭，说明错误被处理而非忽略）
    expect(screen.getByText(/编辑触发器配置/)).toBeInTheDocument();
  });
});
