/**
 * E2E Regression Test Suite
 *
 * 固化所有 benchmark 为回归测试。
 * 每个领域验证完整链路：需求 → PRD → 架构 → 前端 → 后端 → 全栈 → Electron → Release
 *
 * 运行: node --test test/e2e-regression.test.mjs
 */

import { describe, it, before } from "node:test";
import assert from "node:assert/strict";
import { resolve, join } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync, readFileSync, mkdirSync, readdirSync } from "node:fs";
import { execSync } from "node:child_process";

const __dirname = resolve(fileURLToPath(new URL(".", import.meta.url)));
const WORKSPACE = resolve(__dirname, "..");
const BENCH_ROOT = resolve(WORKSPACE, ".benchmark");
const SCRIPTS = resolve(WORKSPACE, "scripts");

// ═══════════════════════════════════════════════════════
// Domain Definitions
// ═══════════════════════════════════════════════════════

const DOMAINS = [
  { id: "petcare", input: "做一个宠物护理管理平台，宠物主人可以管理宠物档案、健康日程、日常记录和成长相册", minFeatures: 5, minPages: 4, minApis: 3 },
  { id: "crm", input: "做一个客户关系管理系统，支持客户管理、销售漏斗、跟进记录和数据分析仪表盘", minFeatures: 4, minPages: 3, minApis: 3 },
  { id: "inventory", input: "做一个库存管理系统，支持商品入库出库、库存盘点、供应商管理和库存预警", minFeatures: 4, minPages: 3, minApis: 3 },
  { id: "ticket", input: "做一个工单系统，支持工单创建、分配、处理流程、优先级管理和工单归档", minFeatures: 4, minPages: 3, minApis: 3 },
  { id: "blog-cms", input: "做一个博客内容管理系统，支持文章发布、分类标签、评论管理和媒体库", minFeatures: 4, minPages: 2, minApis: 2 },
  { id: "project-mgmt", input: "做一个项目管理系统，支持项目看板、任务分配、甘特图和团队协作", minFeatures: 3, minPages: 2, minApis: 3 },
  { id: "hr", input: "做一个人力资源管理系统，支持员工档案、考勤管理、招聘流程和绩效评估", minFeatures: 4, minPages: 3, minApis: 3 },
  { id: "asset", input: "做一个固定资产管理系统，支持资产登记、领用归还、折旧计算和盘点统计", minFeatures: 3, minPages: 2, minApis: 3 },
  { id: "course", input: "做一个在线课程管理系统，支持课程发布、章节管理、学员进度和作业批改", minFeatures: 4, minPages: 3, minApis: 3 },
  { id: "appointment", input: "做一个预约管理系统，支持服务项目、时间段预约、客户通知和预约统计", minFeatures: 3, minPages: 2, minApis: 3 },
];

// ═══════════════════════════════════════════════════════
// Helpers
// ═══════════════════════════════════════════════════════

function runAgent(script, args) {
  try {
    const result = execSync(`node ${join(SCRIPTS, script)} ${args}`, {
      cwd: WORKSPACE, encoding: "utf8", stdio: "pipe", timeout: 30_000,
    });
    return JSON.parse(result.trim());
  } catch (e) {
    try { return JSON.parse(e.stdout?.trim() || "{}"); } catch { return { error: e.message }; }
  }
}

function countFiles(dir) {
  try {
    return parseInt(execSync(`find ${dir} -type f | wc -l`, { encoding: "utf8" }).trim());
  } catch { return 0; }
}

function loadJSON(path) {
  try { return JSON.parse(readFileSync(path, "utf8")); } catch { return {}; }
}

function scanDir(dir) {
  try { return readdirSync(dir, { withFileTypes: true }); } catch { return []; }
}

// ═══════════════════════════════════════════════════════
// Full Pipeline Runner
// ═══════════════════════════════════════════════════════

function runFullPipeline(domain) {
  const B = join(BENCH_ROOT, domain.id);
  mkdirSync(B, { recursive: true });

  const result = { stages: {} };

  // SF-01
  const s1 = runAgent("project-intake-agent.mjs", `--input "${domain.input}" --output "${join(B, "prd.json")}"`);
  result.stages.prd = { ok: !s1.error && !!s1.projectName, projectName: s1.projectName };

  // SF-02
  const s2 = runAgent("architecture-agent.mjs", `--input "${join(B, "prd.json")}" --output "${join(B, "arch.json")}"`);
  result.stages.arch = { ok: !s2.error };

  // SF-03
  const feOut = join(B, "frontend");
  const s3 = runAgent("frontend-builder-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${feOut}"`);
  result.stages.frontend = { ok: !s3.error, files: countFiles(feOut) };

  // SF-04
  const beOut = join(B, "backend");
  const s4 = runAgent("backend-builder-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${beOut}"`);
  result.stages.backend = { ok: !s4.error, files: countFiles(beOut) };

  // SF-05
  const fsOut = join(B, "fullstack");
  const s5 = runAgent("fullstack-composer-agent.mjs", `--prd "${join(B, "prd.json")}" --arch "${join(B, "arch.json")}" --output "${fsOut}"`);
  result.stages.fullstack = { ok: !s5.error, files: countFiles(fsOut) };

  // SF-06
  const elOut = join(B, "electron");
  const s6 = runAgent("electron-builder-agent.mjs", `--input "${fsOut}" --output "${elOut}" --prd "${join(B, "prd.json")}"`);
  result.stages.electron = { ok: !s6.error, files: countFiles(elOut) };

  // SF-07
  const rlOut = join(B, "release");
  const s7 = runAgent("release-builder-agent.mjs", `--input "${fsOut}" --output "${rlOut}"`);
  result.stages.release = { ok: !s7.error, files: countFiles(rlOut) };

  return result;
}

// ═══════════════════════════════════════════════════════
// Validation Helpers
// ═══════════════════════════════════════════════════════

function validatePRD(domain) {
  const prd = loadJSON(join(BENCH_ROOT, domain.id, "prd.json"));
  return {
    hasProjectName: !!prd.projectName,
    hasDomain: !!prd.domain,
    featureCount: prd.features?.length || 0,
    pageCount: prd.pages?.length || 0,
    apiCount: prd.apiRequirements?.length || 0,
    pass: !!prd.projectName && !!prd.domain && (prd.features?.length || 0) >= domain.minFeatures,
  };
}

function validateFrontend(domain) {
  const dir = join(BENCH_ROOT, domain.id, "frontend");
  const checks = {
    packageJson: existsSync(join(dir, "package.json")),
    appDir: existsSync(join(dir, "app")),
    appPages: scanDir(join(dir, "app")).filter(e => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).length,
    services: existsSync(join(dir, "services")) || existsSync(join(dir, "src", "services")),
    types: existsSync(join(dir, "types")) || existsSync(join(dir, "src", "types")),
    fileCount: countFiles(dir),
  };
  checks.pass = checks.packageJson && checks.appDir && checks.fileCount > 10 && checks.appPages >= domain.minPages;
  return checks;
}

function validateBackend(domain) {
  const dir = join(BENCH_ROOT, domain.id, "backend");
  const routesDir = join(dir, "src", "routes");
  const routeFiles = scanDir(routesDir).filter(e => e.isFile() && e.name.endsWith(".ts") && e.name !== "index.ts");
  const checks = {
    packageJson: existsSync(join(dir, "package.json")),
    routes: existsSync(routesDir),
    routeCount: routeFiles.length,
    services: existsSync(join(dir, "src", "services")),
    auth: routeFiles.some(e => e.name.includes("auth")),
    db: existsSync(join(dir, "src", "db")) || existsSync(join(dir, "src", "schema")),
    fileCount: countFiles(dir),
  };
  checks.pass = checks.packageJson && checks.routes && checks.services && checks.routeCount >= domain.minApis;
  return checks;
}

function validateFullstack(domain) {
  const dir = join(BENCH_ROOT, domain.id, "fullstack");
  const checks = {
    packageJson: existsSync(join(dir, "package.json")),
    web: existsSync(join(dir, "apps", "web")),
    api: existsSync(join(dir, "apps", "api")),
    shared: existsSync(join(dir, "packages")),
    fileCount: countFiles(dir),
  };
  checks.pass = checks.packageJson && checks.web && checks.api && checks.fileCount > 30;
  return checks;
}

function validateElectron(domain) {
  const dir = join(BENCH_ROOT, domain.id, "electron");
  const checks = {
    packageJson: existsSync(join(dir, "package.json")),
    main: existsSync(join(dir, "electron", "main.ts")),
    preload: existsSync(join(dir, "electron", "preload.ts")),
    ipc: existsSync(join(dir, "electron", "ipc.ts")),
    yml: existsSync(join(dir, "electron-builder.yml")),
    fileCount: countFiles(dir),
  };
  checks.pass = checks.packageJson && checks.main && checks.preload && checks.fileCount >= 8;
  return checks;
}

function validateRelease(domain) {
  const dir = join(BENCH_ROOT, domain.id, "release", "release");
  const checks = {
    versionJson: existsSync(join(dir, "version.json")),
    manifest: existsSync(join(dir, "manifests", "manifest.json")),
    checksums: existsSync(join(dir, "checksums", "checksums.txt")),
    releaseNotes: existsSync(join(dir, "release-notes", "release-notes.md")),
    buildInfo: existsSync(join(dir, "build-info.json")),
    windows: existsSync(join(dir, "windows")),
    macos: existsSync(join(dir, "macos")),
    linux: existsSync(join(dir, "linux")),
  };
  // Validate content
  if (checks.versionJson) {
    const v = loadJSON(join(dir, "version.json"));
    checks.versionValid = !!(v.name && v.version && v.platforms);
  }
  if (checks.manifest) {
    const m = loadJSON(join(dir, "manifests", "manifest.json"));
    checks.manifestValid = !!(m.project && m.files?.length > 0);
  }
  if (checks.checksums) {
    const content = readFileSync(join(dir, "checksums", "checksums.txt"), "utf8");
    const lines = content.trim().split("\n").filter(l => l.length > 0);
    checks.checksumCount = lines.length;
    checks.checksumsValid = lines.every(l => l.split(/\s+/)[0]?.length === 64);
  }
  if (checks.releaseNotes) {
    const notes = readFileSync(join(dir, "release-notes", "release-notes.md"), "utf8");
    checks.notesValid = notes.includes("Version") && notes.includes("Installation");
  }
  if (checks.buildInfo) {
    const bi = loadJSON(join(dir, "build-info.json"));
    checks.buildInfoValid = !!(bi.nodeVersion && bi.generatorVersion);
  }
  for (const p of ["windows", "macos", "linux"]) {
    if (checks[p]) {
      try { checks[p + "Count"] = readdirSync(join(dir, p)).length; } catch { checks[p + "Count"] = 0; }
    }
  }
  checks.pass = checks.versionJson && checks.versionValid && checks.manifest && checks.manifestValid &&
    checks.checksums && checks.checksumsValid && checks.releaseNotes && checks.notesValid &&
    checks.buildInfo && checks.buildInfoValid && checks.windows && checks.macos && checks.linux;
  return checks;
}

// ═══════════════════════════════════════════════════════
// Tests
// ═══════════════════════════════════════════════════════

describe("E2E Regression — 全链路生成", () => {
  for (const domain of DOMAINS) {
    describe(`${domain.id}`, () => {
      let pipeline;

      before(() => {
        pipeline = runFullPipeline(domain);
      });

      it("SF-01 PRD 生成成功", () => {
        assert.ok(pipeline.stages.prd.ok, `${domain.id}: PRD generation failed`);
      });

      it("SF-02 Architecture 生成成功", () => {
        assert.ok(pipeline.stages.arch.ok, `${domain.id}: Architecture generation failed`);
      });

      it("SF-03 Frontend 生成成功", () => {
        assert.ok(pipeline.stages.frontend.ok, `${domain.id}: Frontend generation failed`);
        assert.ok(pipeline.stages.frontend.files > 10, `${domain.id}: Too few frontend files`);
      });

      it("SF-04 Backend 生成成功", () => {
        assert.ok(pipeline.stages.backend.ok, `${domain.id}: Backend generation failed`);
        assert.ok(pipeline.stages.backend.files > 10, `${domain.id}: Too few backend files`);
      });

      it("SF-05 Fullstack 组装成功", () => {
        assert.ok(pipeline.stages.fullstack.ok, `${domain.id}: Fullstack composition failed`);
        assert.ok(pipeline.stages.fullstack.files > 30, `${domain.id}: Too few fullstack files`);
      });

      it("SF-06 Electron 封装成功", () => {
        assert.ok(pipeline.stages.electron.ok, `${domain.id}: Electron wrapper failed`);
        assert.ok(pipeline.stages.electron.files >= 8, `${domain.id}: Too few electron files`);
      });

      it("SF-07 Release 打包成功", () => {
        assert.ok(pipeline.stages.release.ok, `${domain.id}: Release packaging failed`);
      });
    });
  }
});

describe("E2E Regression — PRD 质量", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: PRD 包含必要的字段和最低特性数`, () => {
      const v = validatePRD(domain);
      assert.ok(v.hasProjectName, `${domain.id}: missing projectName`);
      assert.ok(v.hasDomain, `${domain.id}: missing domain`);
      assert.ok(v.featureCount >= domain.minFeatures, `${domain.id}: ${v.featureCount} features < ${domain.minFeatures}`);
    });
  }
});

describe("E2E Regression — Frontend 结构", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: Frontend 包含 package.json, app/, services/, types/`, () => {
      const v = validateFrontend(domain);
      assert.ok(v.pass, `${domain.id}: FE validation failed — pages:${v.appPages} files:${v.fileCount}`);
    });
  }
});

describe("E2E Regression — Backend 结构", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: Backend 包含 routes, services, auth, db`, () => {
      const v = validateBackend(domain);
      assert.ok(v.pass, `${domain.id}: BE validation failed — routes:${v.routeCount} files:${v.fileCount}`);
    });
  }
});

describe("E2E Regression — Fullstack 结构", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: Fullstack 包含 apps/web, apps/api, packages/`, () => {
      const v = validateFullstack(domain);
      assert.ok(v.pass, `${domain.id}: FS validation failed — files:${v.fileCount}`);
    });
  }
});

describe("E2E Regression — Electron 结构", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: Electron 包含 main.ts, preload.ts, ipc.ts`, () => {
      const v = validateElectron(domain);
      assert.ok(v.pass, `${domain.id}: EL validation failed — files:${v.fileCount}`);
    });
  }
});

describe("E2E Regression — Release 结构", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: Release 包含 version, manifest, checksums, notes, build-info, 3 platforms`, () => {
      const v = validateRelease(domain);
      assert.ok(v.pass, `${domain.id}: Release validation failed`);
    });
  }
});

describe("E2E Regression — Release 内容完整性", () => {
  for (const domain of DOMAINS) {
    it(`${domain.id}: version.json 包含 name, version, platforms`, () => {
      const dir = join(BENCH_ROOT, domain.id, "release", "release");
      const v = loadJSON(join(dir, "version.json"));
      assert.ok(v.name, `${domain.id}: missing version.name`);
      assert.ok(v.version, `${domain.id}: missing version.version`);
      assert.deepEqual(v.platforms, ["windows", "macos", "linux"]);
    });

    it(`${domain.id}: manifest.json 文件列表与 checksums.txt 一致`, () => {
      const dir = join(BENCH_ROOT, domain.id, "release", "release");
      const m = loadJSON(join(dir, "manifests", "manifest.json"));
      const c = readFileSync(join(dir, "checksums", "checksums.txt"), "utf8");
      const cLines = c.trim().split("\n").filter(l => l.length > 0);
      assert.equal(m.files.length, cLines.length, `${domain.id}: manifest/checksums count mismatch`);
    });

    it(`${domain.id}: SHA256 格式正确 (64 hex chars)`, () => {
      const dir = join(BENCH_ROOT, domain.id, "release", "release");
      const c = readFileSync(join(dir, "checksums", "checksums.txt"), "utf8");
      for (const line of c.trim().split("\n").filter(l => l.length > 0)) {
        const sha = line.split(/\s+/)[0];
        assert.match(sha, /^[0-9a-f]{64}$/, `${domain.id}: invalid SHA256: ${sha}`);
      }
    });

    it(`${domain.id}: Platform 目录包含占位文件`, () => {
      const dir = join(BENCH_ROOT, domain.id, "release", "release");
      for (const p of ["windows", "macos", "linux"]) {
        const files = readdirSync(join(dir, p));
        assert.ok(files.length >= 2, `${domain.id}: ${p}/ has ${files.length} files, expected >= 2`);
      }
    });
  }
});
