import {
  expect,
  test,
  type Locator,
  type Page,
  type TestInfo,
} from "@playwright/test";
import { v4 as uuid } from "uuid";

import {
  newChatEntry,
  openChat,
  sendMessage,
  waitForMessageListReady,
} from "./support/chat-helpers";

const FILE_CASES = [
  { kind: "html", label: "html", regex: /\.html?/i },
  {
    kind: "image",
    label: "image",
    regex: /\.(png|jpe?g|gif|webp|bmp|svg|ico|avif|tiff?)/i,
  },
  { kind: "md", label: "md", regex: /\.md/i },
  { kind: "docx", label: "docx", regex: /\.docx?/i },
  { kind: "pptx", label: "pptx", regex: /\.pptx?/i },
  { kind: "xlsx", label: "xlsx", regex: /\.xlsx?/i },
] as const;

test.use({
  video: "on",
  screenshot: "on",
});

test.describe("聊天工作台 / 智能体产物生成预览与下载", () => {
  test("DF-ART-GEN-001 生成并逐个点击 html/image/md/docx/pptx/xlsx 卡片截图", async ({
    page,
  }, testInfo) => {
    const startedAt = Date.now();
    test.setTimeout(12 * 60 * 1000);
    const threadId = uuid();
    logStatus("开始测试", `threadId=${threadId}`);
    await openChat(page, newChatEntry(threadId));
    await waitForMessageListReady(page, { requireMessages: false });

    logStatus("发送生成指令");
    await sendMessage(page, buildGeneratePrompt());
    await waitForArtifactsReady(page, FILE_CASES, startedAt);

    await openArtifactPanel(page);
    logStatus("Artifacts 列表已就绪，开始逐类校验");
    await capture(page, testInfo, "artifact-list-ready");

    for (const file of FILE_CASES) {
      logStatus("校验文件类型", file.label);
      const card = artifactCardByPattern(page, file.regex);
      await expect(card).toBeVisible();
      await card.click();
      logStatus("点击并截图", file.label);
      await waitAfterCardClick(page, file.kind);
      await capture(page, testInfo, `card-clicked-${file.label}`);
      logStatus("类型处理完成", file.label);
    }
    logStatus("测试完成");
  });
});

function buildGeneratePrompt(): string {
  return [
    "请一次性创建以下 6 个文件到 /mnt/user-data/outputs，并在完成后调用 present_files：",
    "1) e2e-artifacts-page.html：包含标题 DF_E2E_HTML 和一段正文。",
    "2) e2e-artifacts-image.png：生成一张包含文字 DF_E2E_IMAGE 的图片。",
    "3) e2e-artifacts-notes.md：标题为 DF_E2E_MD，并引用上面的图片。",
    "4) e2e-artifacts-report.docx：包含标题 DF_E2E_DOCX 和一段文字。",
    "5) e2e-artifacts-slides.pptx：至少 2 页，包含 DF_E2E_PPTX。",
    "6) e2e-artifacts-table.xlsx：至少 2 列 3 行，并包含 DF_E2E_XLSX。",
    "注意：所有文件都要真实写入输出目录，不要只在回复里描述。",
  ].join("\n");
}

async function openArtifactPanel(page: Page): Promise<void> {
  const button = page.getByTestId("artifacts-open-button");
  await expect(button).toBeVisible({ timeout: 120_000 });
  await button.click();
  await expect(page.getByTestId("artifact-file-list").first()).toBeVisible();
}

async function waitForArtifactsReady(
  page: Page,
  requiredCases: ReadonlyArray<(typeof FILE_CASES)[number]>,
  startedAt: number,
): Promise<void> {
  let pollRound = 0;
  await expect
    .poll(
      async () => {
        pollRound += 1;
        const elapsedSeconds = Math.round((Date.now() - startedAt) / 1000);
        const list = page.getByTestId("artifact-file-list").first();

        // 1) 优先直接检查已展示的 artifact-file-list
        if (!(await list.isVisible().catch(() => false))) {
          // 2) 列表不存在时再尝试通过按钮打开
          const openButton = page.getByTestId("artifacts-open-button").first();
          if (!(await openButton.isVisible().catch(() => false))) {
            logStatus(
              "等待 artifacts 入口或列表出现",
              `轮次=${pollRound}, 已耗时=${elapsedSeconds}s`,
            );
            return false;
          }
          await openButton.click();
          await expect(
            page.getByTestId("artifact-file-list").first(),
          ).toBeVisible({
            timeout: 5_000,
          });
        }

        const allFileNames = await getArtifactFileNames(page);

        const found = requiredCases
          .filter((fileCase) =>
            allFileNames.some((name) => fileCase.regex.test(name)),
          )
          .map((fileCase) => fileCase.label);
        const missing = requiredCases
          .filter(
            (fileCase) =>
              !allFileNames.some((name) => fileCase.regex.test(name)),
          )
          .map((fileCase) => fileCase.label);

        logStatus(
          "等待文件类型齐全",
          `轮次=${pollRound}, 已耗时=${elapsedSeconds}s, 已找到=[${found.join(", ")}], 缺失=[${missing.join(", ")}]`,
        );
        return missing.length === 0;
      },
      {
        timeout: 8 * 60 * 1000,
        intervals: [1000, 2000, 3000, 5000],
      },
    )
    .toBeTruthy();
}

function artifactCardByPattern(page: Page, pattern: RegExp): Locator {
  return page
    .locator("[data-testid='artifact-file-card']")
    .filter({
      has: page.locator("[data-slot='card-title'] div[title]").filter({
        hasText: pattern,
      }),
    })
    .first();
}

async function waitAfterCardClick(page: Page, kind: string): Promise<void> {
  if (kind === "docx") {
    await expect(page.locator(".docx-preview-wrap").first()).toBeVisible({
      timeout: 60_000,
    });
    return;
  }
  if (kind === "xlsx") {
    await expect(page.locator("#artifact-xlsx-preview").first()).toBeVisible({
      timeout: 60_000,
    });
    return;
  }
  if (kind === "pptx") {
    await expect(
      page.getByText("请下载ppt文件以获得最佳效果").first(),
    ).toBeVisible({
      timeout: 60_000,
    });
    return;
  }
  if (kind === "md") {
    await page.waitForTimeout(1200);
    return;
  }

  await expect(page.getByTitle(/Artifact preview(?::.*)?$/i)).toBeVisible({
    timeout: 30_000,
  });
}

async function capture(
  page: Page,
  testInfo: TestInfo,
  name: string,
): Promise<void> {
  const path = testInfo.outputPath(`${name}.png`);
  await page.screenshot({ path, fullPage: true });
  await testInfo.attach(name, {
    path,
    contentType: "image/png",
  });
}

function logStatus(step: string, detail?: string): void {
  const timestamp = new Date().toISOString();
  if (detail) {
    console.log(`[E2E][${timestamp}] ${step} | ${detail}`);
    return;
  }
  console.log(`[E2E][${timestamp}] ${step}`);
}

async function getArtifactFileNames(page: Page): Promise<string[]> {
  const titleNodes = page.locator(
    "[data-testid='artifact-file-card'] [data-slot='card-title'] div[title]",
  );
  const titleCount = await titleNodes.count();
  if (titleCount > 0) {
    const names: string[] = [];
    for (let i = 0; i < titleCount; i += 1) {
      const value = (await titleNodes.nth(i).getAttribute("title"))?.trim();
      if (value) {
        names.push(value);
      }
    }
    return names;
  }

  // fallback: if title attr is absent, use first text line of each card
  const cardTexts = await page
    .getByTestId("artifact-file-card")
    .allTextContents();
  return cardTexts
    .map((text) => text.split("\n")[0]?.trim() ?? "")
    .filter(Boolean);
}
