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

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

const evidence = path.resolve(__dirname, "../../docs/evidence/stage-9");
test.beforeAll(() => fs.mkdirSync(evidence, { recursive: true }));

async function authenticate(page, username, password) {
  await page.goto("/");
  await page.getByLabel("账号名").fill(username);
  await page.getByLabel("密码").fill(password);
  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();
  }
}

const formula = {
  universe: { exclude_st: true, listed_days_min: 120 },
  filters: [{ field: "amount_billion", op: ">=", value: 1 }],
  score: [
    { field: "return_20d", weight: 0.6, direction: "desc" },
    { field: "sector_strength", weight: 0.4, direction: "desc" },
  ],
  limit: 30,
  min_score: 0.5,
  meta: {
    category: "趋势追踪",
    suitable_environment: "趋势明确且成交活跃的市场",
    failure_risk: "震荡轮动过快时信号容易反复",
  },
};

const stageStrategy = {
  id: "stage-ice",
  version: 1,
  kind: "stage",
  name: "冰点抗跌先手",
  display_name: "冰点抗跌先手",
  description: "寻找冰点中保持相对强度、低波动且有板块承接的个股，允许无结果。",
  regimes: ["ice"],
  formula,
};

const stageStrategyTwo = {
  ...stageStrategy,
  id: "stage-divergence",
  name: "分化去弱留强",
  display_name: "分化去弱留强",
  regimes: ["divergence"],
};

const curated = [
  { ...stageStrategy, id: "curated-01", kind: "curated", name: "连续分红质量", display_name: "连续分红质量" },
  { ...stageStrategy, id: "curated-02", kind: "curated", name: "动态多因子（基础版）", display_name: "动态多因子（基础版）", formula: { ...formula, meta: { ...formula.meta, category: "多因子" } } },
];

const candidate = {
  identifier: "000001.SZ",
  code: "000001",
  name: "平安银行",
  sector: "银行",
  close: 12.35,
  pct_chg: 2.4,
  amount_billion: 18.6,
  score_display: 86.5,
  reason: "中期动量与行业强度居前",
  risk_flags: [],
};

function candidateNamed(code, name, reason) {
  return { ...candidate, identifier: `${code}.SZ`, code, name, reason };
}

function run(id, mode, name, status = "completed", strategyId = "", item = candidate) {
  return {
    id,
    mode,
    strategy_id: strategyId || (mode === "custom" ? "custom-7" : mode === "stage" ? "stage-ice" : `curated-0${id - 1}`),
    strategy_name: name,
    selection_date: "2026-07-30",
    status,
    coverage: 1,
    missing_fields: [],
    items: status === "completed" ? [item] : [],
    error_message: "",
  };
}

const catalog = {
  factor_groups: { "行情与动量": ["return_20d", "amount_billion"], "板块与行业": ["sector_strength"] },
  factors: { return_20d: "20日涨幅", amount_billion: "成交额", sector_strength: "板块强度" },
  stage: [stageStrategy, stageStrategyTwo],
  curated,
};

const workspace = {
  trade_date: "2026-07-30",
  message: "",
  catalog,
  stage_runs: [
    run(1, "stage", "冰点抗跌先手", "completed", "stage-ice", candidateNamed("000011", "冰点样本", "冰点阶段相对抗跌")),
    run(5, "stage", "分化去弱留强", "completed", "stage-divergence", candidateNamed("000012", "分化样本", "分化阶段承接较强")),
  ],
  curated_runs: [
    run(2, "curated", "连续分红质量", "completed", "curated-01", candidateNamed("000021", "分红样本", "分红与质量条件满足")),
    run(3, "curated", "动态多因子（基础版）", "completed", "curated-02", candidateNamed("000022", "多因子样本", "综合因子得分居前")),
  ],
  custom_strategies: [{ id: 7, name: "我的选股策略", version: 2, formula }],
  custom_runs: [{
    ...run(4, "custom", "我的选股策略", "completed", "custom-7", candidateNamed("000031", "自定义样本", "用户条件确定性命中")),
    backtest: {
      sample_size: 7,
      evaluated_dates: 7,
      stable: false,
      win_rate: null,
      average_return_3d: null,
      period_start: "2026-07-01",
      period_end: "2026-07-09",
      message: "小样本：当前7个有效样本，满20个后显示胜率与平均收益",
    },
  }],
};

const track = {
  id: 9,
  code: "000001",
  name: "平安银行",
  sector: "银行",
  selection_date: "2026-07-30",
  strategy_name: "连续分红质量",
  entry_price: 12.35,
  t1_open_return: 1.2,
  t1_return: 2.4,
  t3_return: 4.1,
  t5_return: 6.8,
  max_gain: 8.2,
  max_drawdown: -1.6,
  observed_days: 5,
};

async function mockScreener(page) {
  await page.route("**/api/screener/catalog", (route) => route.fulfill({ contentType: "application/json", body: JSON.stringify(catalog) }));
  await page.route("**/api/screener?*", (route) => route.fulfill({ contentType: "application/json", body: JSON.stringify(workspace) }));
  await page.route("**/api/screener/tracks", (route) => {
    const body = route.request().method() === "GET" ? [track] : { id: 9 };
    return route.fulfill({ contentType: "application/json", body: JSON.stringify(body) });
  });
  await page.route("**/api/screener/formula/compile", (route) => route.fulfill({
    contentType: "application/json",
    body: JSON.stringify({
      prompt_version: "screener:formula-compiler:v1",
      formula: {
        universe: { exclude_st: true, listed_days_min: 120 },
        filters: [{ field: "amount_billion", op: ">=", value: 3 }],
        score: [{ field: "return_20d", weight: 1, direction: "desc" }],
        limit: 20,
        min_score: 0.55,
      },
    }),
  }));
}

test("stage 9 screening preserves deterministic modes, explicit tracking and responsive layout", async ({ page }) => {
  const consoleErrors = [];
  page.on("console", (message) => {
    if (message.type() === "error" && !message.text().includes("401 (Unauthorized)")) consoleErrors.push(message.text());
  });
  await mockScreener(page);
  await authenticate(page, "stage4admin", "Stage4-pass-123!");
  await page.goto("/workspace/screener");

  await expect(page.getByText("当前阶段自动候选")).toBeVisible();
  await expect(page.locator(".screener-results")).toContainText("冰点样本");
  await page.getByRole("button", { name: "分化去弱留强", exact: true }).click();
  await expect(page.locator(".screener-results")).toContainText("分化样本");
  await expect(page.locator(".screener-results")).not.toContainText("冰点样本");
  await page.getByRole("button", { name: "加入跟踪" }).click();
  await expect(page.getByRole("status")).toContainText("已加入策略跟踪");
  await page.screenshot({ path: path.join(evidence, "stage-light-1920x1080.jpg"), type: "jpeg", quality: 82 });

  await page.getByRole("button", { name: "策略选股" }).click();
  await expect(page.getByText("策略库")).toBeVisible();
  await expect(page.getByText("动态多因子（基础版）", { exact: true })).toBeVisible();
  await expect(page.getByText("风险等级", { exact: true })).toBeVisible();
  await expect(page.getByText("评分权重", { exact: true })).toBeVisible();
  await expect(page.locator(".strategy-detail")).toContainText("20日涨幅 · 60% · 高优");
  await expect(page.locator(".screener-results")).toContainText("分红样本");
  await page.locator(".strategy-items").getByRole("button", { name: /动态多因子/ }).click();
  await expect(page.locator(".screener-results")).toContainText("多因子样本");
  await expect(page.locator(".screener-results")).not.toContainText("分红样本");
  await page.getByTitle("图标排列").click();
  await expect(page.locator(".strategy-items")).toHaveClass(/is-grid/);

  await page.getByRole("button", { name: "自定义选股" }).click();
  await expect(page.getByText("合计 100%")).toBeVisible();
  await expect(page.getByRole("button", { name: "我的选股策略 第 2 版" })).toBeVisible();
  await expect(page.locator(".screener-results")).toContainText("自定义样本");
  await expect(page.locator(".screener-results")).not.toContainText("多因子样本");
  await expect(page.getByText(/小样本：当前7个有效样本/)).toBeVisible();
  await page.getByPlaceholder(/近20日走势较强/).fill("成交额至少3亿元，偏重20日走势");
  await page.getByRole("button", { name: "转换", exact: true }).click();
  await expect(page.getByRole("status")).toContainText("已转换为受控公式");
  await expect(page.locator(".factor-list input[type=number]").first()).toHaveValue("100");
  await expect(page.locator(".filter-list input[type=number]").first()).toHaveValue("3");
  await page.getByRole("button", { name: "夜间" }).click();
  await page.setViewportSize({ width: 390, height: 844 });
  expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(0);
  await page.screenshot({ path: path.join(evidence, "custom-dark-390x844.jpg"), type: "jpeg", quality: 82 });

  await page.getByRole("button", { name: "持续 策略跟踪" }).click();
  await expect(page.getByRole("heading", { name: "策略持续跟踪" })).toBeVisible();
  await expect(page.locator(".topbar-title")).toHaveText("智能选股");
  await expect(page.getByText("6.80", { exact: true })).toBeVisible();
  expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(0);
  await page.screenshot({ path: path.join(evidence, "tracking-dark-390x844.jpg"), type: "jpeg", quality: 82 });
  expect(consoleErrors).toEqual([]);
});

test("nonmembers see the same screening structure in a disabled state", async ({ page }) => {
  await page.route("**/api/screener/catalog", (route) => route.fulfill({ contentType: "application/json", body: JSON.stringify(catalog) }));
  await authenticate(page, "stage4user", "Stage4-user-123!");
  await page.goto("/workspace/screener");
  await expect(page.getByText("智能选股仅对会员开放")).toBeVisible();
  await expect(page.getByText("当前阶段自动候选")).toBeVisible();
  await expect(page.locator(".stage-overview")).toHaveAttribute("aria-disabled", "true");
  await expect(page.locator(".stage-flow .done")).toHaveCount(0);
  await page.getByRole("button", { name: "自定义选股" }).click();
  await expect(page.getByText("因子与权重")).toBeVisible();
  await expect(page.locator(".custom-builder")).toHaveAttribute("aria-disabled", "true");
});

test("stage workflow reports idle, running, failed and completed truthfully", async ({ page }) => {
  let status = "pending";
  await page.route("**/api/screener/catalog", (route) => route.fulfill({
    contentType: "application/json", body: JSON.stringify(catalog),
  }));
  await page.route("**/api/screener?*", (route) => {
    const stageRuns = status === "pending" ? [] : [run(
      11,
      "stage",
      "冰点抗跌先手",
      status,
      "stage-ice",
      candidate,
    )];
    if (stageRuns[0] && status === "failed") stageRuns[0].error_message = "因子快照损坏";
    return route.fulfill({
      contentType: "application/json",
      body: JSON.stringify({ ...workspace, stage_runs: stageRuns }),
    });
  });
  await authenticate(page, "stage4admin", "Stage4-pass-123!");
  await page.goto("/workspace/screener");

  await expect(page.locator(".stage-heading .tag")).toHaveText("未执行");
  await expect(page.locator(".stage-flow .done")).toHaveCount(0);
  await expect(page.getByText("盘后选股尚未执行")).toBeVisible();

  status = "running";
  await page.reload();
  await expect(page.locator(".stage-heading .tag")).toHaveText("执行中");
  await expect(page.locator(".stage-flow .done")).toHaveCount(3);
  await expect(page.getByText("正在计算候选结果")).toBeVisible();

  status = "failed";
  await page.reload();
  await expect(page.locator(".stage-heading .tag")).toHaveText("失败");
  await expect(page.locator(".stage-flow .done")).toHaveCount(2);
  await expect(page.locator(".screener-results")).toContainText("因子快照损坏");

  status = "completed";
  await page.reload();
  await expect(page.locator(".stage-heading .tag")).toHaveText("已完成");
  await expect(page.locator(".stage-flow .done")).toHaveCount(4);
  await expect(page.locator(".screener-results")).toContainText("平安银行");
});
