const fs = require("node:fs");
const path = require("node:path");

const { expect, test } = require("@playwright/test");

const evidence = path.resolve(__dirname, "../../docs/evidence/stage-5");

test.beforeAll(() => fs.mkdirSync(evidence, { recursive: true }));

async function authenticate(page) {
  await page.goto("/");
  await page.getByLabel("账号名").fill("stage5admin");
  await page.getByLabel("密码").fill("Stage5-pass-123!");
  await page.getByRole("button", { name: "登录", exact: true }).click();
  await expect(page.locator(".sidebar, .field-error")).toBeVisible();
  if (!(await page.locator(".sidebar").isVisible())) {
    await page.getByRole("tab", { name: "注册" }).click();
    await page.getByRole("button", { name: "注册并登录" }).click();
    await expect(page.locator(".sidebar")).toBeVisible();
  }
}

function chartPayload(interval) {
  const daily = [
    ["2026-07-23", 3500, 3540, 3480, 3530],
    ["2026-07-24", 3530, 3568, 3510, 3552],
    ["2026-07-25", 3550, 3580, 3524, 3540],
    ["2026-07-28", 3542, 3590, 3530, 3582],
    ["2026-07-29", 3585, 3612, 3570, 3604],
  ];
  const minute = [
    ["09:30", 3600, 3608, 3594, 3604, 3602],
    ["10:30", 3604, 3616, 3600, 3610, 3607],
    ["11:30", 3610, 3614, 3602, 3606, 3608],
    ["14:00", 3606, 3620, 3604, 3618, 3610],
    ["15:00", 3618, 3622, 3608, 3612, 3613],
  ];
  return {
    entity_type: "index",
    identifier: "000001.SH",
    code: "000001",
    name: "上证指数",
    interval,
    trade_date: "2026-07-29",
    observed_at: "2026-07-29T15:00:00+08:00",
    previous_close: 3594,
    range_start: interval === "minute" ? "09:30" : null,
    range_end: interval === "minute" ? "15:00" : null,
    points: (interval === "day" ? daily : minute).map((row) => ({
      time: row[0],
      open: row[1],
      high: row[2],
      low: row[3],
      close: row[4],
      volume: 100000,
      amount: 360000000,
      average: interval === "minute" ? row[5] : null,
    })),
  };
}

test("latest snapshot, grouped search, chart preview and entity detail", async ({ page }) => {
  const consoleErrors = [];
  let delayStockChart = false;
  page.on("console", (message) => {
    if (message.type() === "error" && !message.text().includes("401 (Unauthorized)")) {
      consoleErrors.push(message.text());
    }
  });
  await page.route("**/api/market/summary", (route) =>
    route.fulfill({
      contentType: "application/json",
      body: JSON.stringify({
        context: {
          requested_date: "2026-07-30",
          actual_date: "2026-07-29",
          previous_date: "2026-07-28",
          observed_at: "2026-07-29T15:00:00+08:00",
          state: "final",
          carried_forward: true,
          message: "沿用最近真实收盘快照",
        },
        values: { temperature: 42, limit_up: 68, limit_down: 4, broken: 24, seal_rate: 73.9, amount: 1628000000000 },
      }),
    }),
  );
  await page.route("**/api/market/entities/index/000001.SH/charts/*", (route) => {
    const interval = route.request().url().endsWith("/minute") ? "minute" : "day";
    return route.fulfill({ contentType: "application/json", body: JSON.stringify(chartPayload(interval)) });
  });
  await page.route("**/api/market/entities/stock/000001.SZ/charts/*", async (route) => {
    if (delayStockChart) await new Promise((resolve) => setTimeout(resolve, 250));
    const interval = route.request().url().endsWith("/minute") ? "minute" : "day";
    return route.fulfill({ contentType: "application/json", body: JSON.stringify({ ...chartPayload(interval), entity_type: "stock", identifier: "000001.SZ", code: "000001", name: "平安银行" }) });
  });
  await page.route("**/api/market/entities/*/*/detail?*", (route) => {
    const stock = route.request().url().includes("/stock/");
    return route.fulfill({
      contentType: "application/json",
      body: JSON.stringify({
        entity: stock
          ? { entity_type: "stock", identifier: "000001.SZ", code: "000001", name: "平安银行", sector: "银行" }
          : { entity_type: "index", identifier: "000001.SH", code: "000001", name: "上证指数", sector: null },
        trade_date: "2026-07-29", observed_at: "2026-07-29T15:00:00+08:00",
        price: stock ? 12.35 : 3604, previous_close: stock ? 12.05 : 3594,
        change: stock ? 2.4896 : 0.2782,
        metrics: [
          { key: "open", label: "开盘", value: stock ? 12.1 : 3585, unit: "元" },
          { key: "high", label: "最高", value: stock ? 12.5 : 3612, unit: "元" },
          { key: "low", label: "最低", value: stock ? 12.0 : 3570, unit: "元" },
          { key: "amount", label: "成交额", value: 1860000000, unit: "元" },
        ],
        money_flow: stock ? { available: true, net_million: 12.5, large_million: 8.1, net_5d_million: 35.2, flow_to_circ_mv_5d: 0.0185 } : null,
        event: stock ? { status: "涨停", reason: "银行板块走强", streak: 1, first_time: "09:35", last_time: "14:20", open_times: 0, seal_amount: 50000000 } : null,
      }),
    });
  });
  await page.route("**/api/review?*", (route) => route.fulfill({ contentType: "application/json", body: JSON.stringify({ trade_date: "2026-07-29", watchlist: [], daily: null, history: [], trades: [], trade_summary: { total: 0, realized: 0, win_rate: null, pnl_amount: null, average_position: null } }) }));
  await page.route("**/api/review/stock-notes/*", (route) => route.fulfill({ contentType: "application/json", body: "[]" }));
  await page.route("**/api/review/watchlist", (route) => route.fulfill({ contentType: "application/json", body: JSON.stringify({ identifier: "000001.SZ", name: "平安银行" }) }));

  await authenticate(page);
  await expect(page.locator(".market-strip-row")).toContainText("涨停 68");
  await expect(page.locator(".market-strip-row")).toContainText("07/29 15:00");

  await page.keyboard.press("Control+K");
  const dialog = page.getByRole("dialog", { name: "全局搜索" });
  await dialog.getByPlaceholder("搜索股票、板块、题材或指数").fill("上证");
  await expect(dialog.getByRole("button", { name: /上证指数/ })).toBeVisible();
  await expect(dialog.locator(".market-chart")).toBeVisible();
  await expect(dialog).toContainText("数据日期 2026-07-29");
  const risingCandle = dialog.locator(".candle-up").first();
  const candleGeometry = await risingCandle.evaluate((group) => {
    const lines = [...group.querySelectorAll("line")];
    const rect = group.querySelector("rect");
    return {
      upperEnd: Number(lines[0]?.getAttribute("y2")),
      bodyTop: Number(rect?.getAttribute("y")),
      lowerStart: Number(lines[1]?.getAttribute("y1")),
      bodyBottom: Number(rect?.getAttribute("y")) + Number(rect?.getAttribute("height")),
      bodyFill: getComputedStyle(rect).fill,
      bodyStroke: getComputedStyle(rect).stroke,
    };
  });
  expect(candleGeometry.upperEnd).toBeCloseTo(candleGeometry.bodyTop, 5);
  expect(candleGeometry.lowerStart).toBeCloseTo(candleGeometry.bodyBottom, 5);
  expect(candleGeometry.bodyFill).not.toBe(candleGeometry.bodyStroke);
  await page.screenshot({ path: path.join(evidence, "search-preview-light-1920x1080.jpg"), type: "jpeg", quality: 82 });

  await dialog.getByRole("button", { name: /上证指数/ }).click();
  await expect(page).toHaveURL(/\/market\/index\/000001.SH/);
  await expect(page.getByRole("heading", { name: "上证指数" })).toBeVisible();
  await expect(page.getByRole("heading", { name: "资金流" })).toHaveCount(0);
  await expect(page.getByRole("heading", { name: "事件逻辑" })).toHaveCount(0);
  await expect(page.getByRole("heading", { name: "个股复盘笔记" })).toHaveCount(0);
  await page.getByRole("button", { name: "分时" }).click();
  await expect(page.locator(".chart-zero")).toHaveCount(1);
  await expect(page.locator(".preview-meta")).toContainText("09:30–15:00");

  await page.getByRole("button", { name: "夜间" }).click();
  const darkSurfaces = await page.evaluate(() => ({
    chart: getComputedStyle(document.querySelector(".market-chart")).backgroundColor,
    panel: getComputedStyle(document.querySelector(".market-preview")).backgroundColor,
    canvas: getComputedStyle(document.documentElement).backgroundColor,
  }));
  expect(darkSurfaces.chart).toBe(darkSurfaces.panel);
  expect(darkSurfaces.chart).not.toBe("rgb(255, 255, 255)");
  expect(darkSurfaces.canvas).not.toBe("rgb(255, 255, 255)");
  await page.screenshot({ path: path.join(evidence, "entity-detail-dark-1920x1080.jpg"), type: "jpeg", quality: 82 });
  expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBe(0);

  await page.goto("/market/stock/000001.SZ?code=000001&name=%E5%B9%B3%E5%AE%89%E9%93%B6%E8%A1%8C&sector=%E9%93%B6%E8%A1%8C");
  await expect(page.getByRole("heading", { name: "平安银行" })).toBeVisible();
  await expect(page.locator(".entity-detail-price")).toContainText("+2.49%");
  await expect(page.getByRole("heading", { name: "资金流" })).toBeVisible();
  await expect(page.getByText("银行板块走强")).toBeVisible();
  await expect(page.getByRole("heading", { name: "个股复盘笔记" })).toBeVisible();
  await expect(page.getByRole("button", { name: "加入自选" })).toBeVisible();
  await expect(page.getByRole("button", { name: "进入观势" })).toBeVisible();
  await page.getByRole("button", { name: "加入自选" }).click();
  await expect(page.getByRole("button", { name: "移出自选" })).toBeVisible();

  delayStockChart = true;
  await page.reload({ waitUntil: "domcontentloaded" });
  await expect(page.locator(".preview-state")).toHaveText("正在读取真实行情");
  const loadingSurfaces = await page.evaluate(() => ({
    theme: document.documentElement.dataset.theme,
    canvas: getComputedStyle(document.documentElement).backgroundColor,
    preview: getComputedStyle(document.querySelector(".market-preview")).backgroundColor,
    loading: getComputedStyle(document.querySelector(".preview-state")).backgroundColor,
  }));
  expect(loadingSurfaces.theme).toBe("dark");
  expect(loadingSurfaces.canvas).not.toBe("rgb(255, 255, 255)");
  expect(loadingSurfaces.preview).not.toBe("rgb(255, 255, 255)");
  expect(loadingSurfaces.loading).not.toBe("rgb(255, 255, 255)");
  delayStockChart = false;
  await expect(page.locator(".market-chart")).toBeVisible();

  await page.setViewportSize({ width: 390, height: 844 });
  await expect(page.locator(".market-chart")).toBeVisible();
  expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBe(0);
  await page.screenshot({ path: path.join(evidence, "entity-detail-dark-390x844.jpg"), type: "jpeg", quality: 82 });
  expect(consoleErrors).toEqual([]);
});
