/**
 * Project Intake & PRD Agent Test — SF-01
 *
 * 验证场景：
 * 1. 宠物管理 App → 正确识别领域
 * 2. 电商小程序 → 平台检测 + 物流/支付
 * 3. 教育平台 → 课程相关 PRD
 * 4. 企业 OA → 审批流/考勤
 * 5. 极简输入 → 笔记应用
 * 6. 健身打卡 iOS → 平台偏好
 * 7. 空输入 → 错误处理
 * 8. 通用/不识别输入 → default domain
 * 9. PRD 输出结构完整性
 * 10. 任务拆解
 */

import { describe, it, before } from "node:test";
import assert from "node:assert/strict";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync, rmSync, readFileSync } from "node:fs";

const __dirname = resolve(fileURLToPath(new URL(".", import.meta.url)));
const WORKSPACE = resolve(__dirname, "..");
const FIXTURES_DIR = resolve(WORKSPACE, "test/fixtures/project-intake");

let generatePRD, matchDomain, detectPlatforms, detectExtraFeatures;
let generateUserStories, determineTechConstraints, generateDevTasks;
let loadInput, writeOutput;

before(async () => {
  const mod = await import("../scripts/project-intake-agent.mjs");
  generatePRD = mod.generatePRD;
  matchDomain = mod.matchDomain;
  detectPlatforms = mod.detectPlatforms;
  detectExtraFeatures = mod.detectExtraFeatures;
  generateUserStories = mod.generateUserStories;
  determineTechConstraints = mod.determineTechConstraints;
  generateDevTasks = mod.generateDevTasks;
  loadInput = mod.loadInput;
  writeOutput = mod.writeOutput;
});

// ═══════════════════════════════════════════════════════
// 1 — 宠物管理 App
// ═══════════════════════════════════════════════════════
describe("1 — 宠物管理 App", () => {
  it("识别为 pet 领域", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "pet-management.json"));
    const prd = generatePRD(data.input);

    assert.equal(prd.domain, "pet");
    assert.equal(prd.matchConfidence, "medium");  // matchCount=1 but no keywords extracted from user input
    assert.equal(prd.projectName, "PetCare");
    assert.ok(prd.summary.includes("宠物"));
  });

  it("包含宠物相关特征", () => {
    const prd = generatePRD("做一个宠物管理 App");

    assert.ok(prd.features.some(f => f.name.includes("宠物")));
    assert.ok(prd.features.some(f => f.name.includes("健康")));
    assert.ok(prd.pages.some(p => p.name.includes("宠物")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("pet")));
  });

  it("生成用户故事", () => {
    const prd = generatePRD("做一个宠物管理 App");

    assert.ok(prd.userStories.length >= 2, `Expected >= 2 stories, got ${prd.userStories.length}`);
    for (const story of prd.userStories) {
      assert.ok(story.id.startsWith("US-"));
      assert.ok(story.as);
      assert.ok(story.want);
      assert.ok(story.soThat);
      assert.ok(story.priority);
    }
  });

  it("生成 MVP 范围", () => {
    const prd = generatePRD("做一个宠物管理 App");

    assert.ok(prd.mvpScope);
    assert.ok(prd.mvpScope.features.length > 0);
    assert.ok(prd.mvpScope.estimatedWeeks > 0);
    assert.ok(prd.mvpScope.description);
  });
});

// ═══════════════════════════════════════════════════════
// 2 — 电商小程序
// ═══════════════════════════════════════════════════════
describe("2 — 电商小程序（支付+物流）", () => {
  it("识别为 ecommerce 领域", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "ecommerce-miniapp.json"));
    const prd = generatePRD(data.input);

    assert.equal(prd.domain, "ecommerce");
    assert.equal(prd.projectName, "ShopApp");
  });

  it("检测到小程序平台", () => {
    const platforms = detectPlatforms("做一个电商小程序，需要支持微信支付和物流追踪");
    assert.ok(platforms.includes("miniapp") || platforms.includes("wechat-miniapp"));
  });

  it("检测到支付和物流特性", () => {
    const features = detectExtraFeatures("做一个电商小程序，需要支持微信支付和物流追踪");
    assert.ok(features.includes("wechat-pay"));
    assert.ok(features.includes("logistics-tracking"));
  });

  it("技术约束包含微信支付和物流", () => {
    const prd = generatePRD("做一个电商小程序，需要支持微信支付和物流追踪");
    const considerations = prd.techConstraints.considerations;

    assert.ok(considerations.some(c => c.includes("微信支付")));
    assert.ok(considerations.some(c => c.includes("物流")));
  });

  it("包含电商 API 端点（domain template fallback）", () => {
    const prd = generatePRD("做一个电商小程序，需要支持微信支付和物流追踪");
    // Domain template APIs used (no explicit API/接口 mentions in input)
    assert.ok(prd.apiRequirements.some(a => a.path.includes("products")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("orders")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("payments")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("logistics")));
    // Verify extra features are still detected
    assert.ok(prd.techConstraints.considerations.some(c => c.includes("微信支付")));
    assert.ok(prd.techConstraints.considerations.some(c => c.includes("物流")));
  });
});

// ═══════════════════════════════════════════════════════
// 3 — 在线教育平台
// ═══════════════════════════════════════════════════════
describe("3 — 在线教育平台", () => {
  it("识别为 education 领域", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "edu-platform.json"));
    const prd = generatePRD(data.input);

    assert.equal(prd.domain, "education");
    assert.equal(prd.projectName, "EduPlatform");
  });

  it("包含课程和题库功能", () => {
    const prd = generatePRD("做一个在线教育平台");

    assert.ok(prd.features.some(f => f.name.includes("课程")));
    assert.ok(prd.features.some(f => f.name.includes("题库")));
    // Domain template pages used (no explicit page mentions in input)
    assert.ok(prd.pages.some(p => p.route.includes("course")));
    assert.ok(prd.pages.some(p => p.route.includes("learn")));
    assert.ok(prd.pages.some(p => p.route.includes("exercises")));
  });

  it("API 包含课程和进度端点", () => {
    const prd = generatePRD("做一个在线教育平台");
    assert.ok(prd.apiRequirements.some(a => a.path.includes("courses")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("progress")));
  });
});

// ═══════════════════════════════════════════════════════
// 4 — 企业 OA
// ═══════════════════════════════════════════════════════
describe("4 — 企业 OA 系统", () => {
  it("识别为 enterprise 领域", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "enterprise-oa.json"));
    const prd = generatePRD(data.input);

    assert.equal(prd.domain, "enterprise");
    assert.equal(prd.projectName, "OAFlow");
  });

  it("包含审批流和考勤功能", () => {
    const prd = generatePRD("做一个企业办公自动化系统，包含审批流、考勤打卡、部门管理");

    assert.ok(prd.features.some(f => f.name.includes("审批")));
    assert.ok(prd.features.some(f => f.name.includes("考勤")));
    assert.ok(prd.features.some(f => f.name.includes("部门")));
  });

  it("技术约束包含 RBAC", () => {
    const prd = generatePRD("做一个企业办公自动化系统，包含审批流、考勤打卡、部门管理");
    assert.ok(prd.techConstraints.considerations.some(c => c.includes("RBAC")));
  });

  it("API 包含审批端点", () => {
    const prd = generatePRD("做一个企业办公自动化系统，包含审批流、考勤打卡、部门管理");
    assert.ok(prd.apiRequirements.some(a => a.path.includes("approvals")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("attendance")));
  });

  it("包含三个用户画像", () => {
    const prd = generatePRD("做一个企业办公自动化系统");
    assert.ok(prd.personas.length >= 3, `Expected >= 3 personas, got ${prd.personas.length}`);
  });
});

// ═══════════════════════════════════════════════════════
// 5 — 极简输入
// ═══════════════════════════════════════════════════════
describe("5 — 极简输入", () => {
  it("两个词的输入也能生成 PRD", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "minimal.json"));
    const prd = generatePRD(data.input);

    assert.ok(prd);
    assert.ok(!prd.error);
    assert.equal(prd.projectName, "NoteApp");
    assert.equal(prd.domain, "note");
  });

  it("极简输入有合理的结构", () => {
    const prd = generatePRD("笔记应用");

    assert.ok(prd.features.length >= 3);
    assert.ok(prd.pages.length >= 2);
    assert.ok(prd.apiRequirements.length >= 3);
    assert.ok(prd.userStories.length >= 2);
  });
});

// ═══════════════════════════════════════════════════════
// 6 — 健身打卡 iOS
// ═══════════════════════════════════════════════════════
describe("6 — 健身打卡 + 平台偏好", () => {
  it("检测到 iOS 平台偏好", () => {
    const platforms = detectPlatforms("做一个健身打卡 App，iOS 优先");
    assert.ok(platforms.includes("ios"));
  });

  it("识别为 fitness 领域", () => {
    const prd = generatePRD("做一个健身打卡 App，iOS 优先");
    assert.equal(prd.domain, "fitness");
    assert.equal(prd.projectName, "FitTracker");
  });

  it("技术栈推荐 iOS 原生", () => {
    const prd = generatePRD("做一个健身打卡 App，iOS 优先");
    assert.ok(prd.techConstraints.recommendedStack.includes("SwiftUI"));
  });

  it("包含打卡和统计功能", () => {
    const prd = generatePRD("做一个健身打卡 App");

    assert.ok(prd.features.some(f => f.name.includes("打卡")));
    assert.ok(prd.features.some(f => f.name.includes("统计")));
    assert.ok(prd.apiRequirements.some(a => a.path.includes("checkins")));
  });
});

// ═══════════════════════════════════════════════════════
// 7 — 空输入 / 错误处理
// ═══════════════════════════════════════════════════════
describe("7 — 空输入与错误处理", () => {
  it("空字符串返回错误", () => {
    const prd = generatePRD("");
    assert.equal(prd.error, "EMPTY_INPUT");
    assert.ok(prd.message);
  });

  it("null 输入返回错误", () => {
    const prd = generatePRD(null);
    assert.equal(prd.error, "EMPTY_INPUT");
  });

  it("仅空白字符返回错误", () => {
    const prd = generatePRD("   ");
    assert.equal(prd.error, "EMPTY_INPUT");
  });
});

// ═══════════════════════════════════════════════════════
// 8 — 通用/不匹配输入
// ═══════════════════════════════════════════════════════
describe("8 — 通用领域降级", () => {
  it("不匹配任何领域时返回 EXTRACTION_FAILED 但包含基础字段", () => {
    const prd = generatePRD("做一个区块链数据分析平台");

    assert.equal(prd.error, "EXTRACTION_FAILED");
    assert.ok(prd.message.includes("no features/pages/apis detected"));
    assert.equal(prd.domain, "generic");
    assert.equal(prd.projectName, "MyProject");
    assert.equal(prd.features.length, 0);
    assert.equal(prd.pages.length, 0);
  });

  it("无法提取特征时返回错误而非虚空 PRD", () => {
    const prd = generatePRD("做一个很酷的东西");

    // P0 硬化后：无法提取特征时不会生成虚空 PRD
    assert.equal(prd.error, "EXTRACTION_FAILED");
    assert.ok(prd.projectName);
    assert.equal(prd.domain, "generic");
  });

  it("有领域匹配时生成完整 PRD（正常分支）", () => {
    const prd = generatePRD("做一个宠物管理 App");

    assert.equal(prd.error, undefined);
    assert.ok(prd.projectName);
    assert.ok(prd.summary);
    assert.ok(prd.personas.length > 0);
    assert.ok(prd.userStories.length > 0);
  });
});

// ═══════════════════════════════════════════════════════
// 9 — PRD 结构完整性
// ═══════════════════════════════════════════════════════
describe("9 — PRD 结构完整性", () => {
  it("PRD 包含所有必需字段", () => {
    const prd = generatePRD("做一个宠物管理 App");

    const requiredFields = [
      "projectName", "chineseName", "domain", "matchConfidence",
      "summary", "personas", "userStories", "mvpScope",
      "features", "pages", "apiRequirements",
      "techConstraints", "extraFeatures", "devTasks", "meta",
    ];

    for (const field of requiredFields) {
      assert.ok(prd[field] !== undefined, `Missing required field: ${field}`);
    }
  });

  it("features 包含 priority 字段", () => {
    const prd = generatePRD("做一个宠物管理 App");
    for (const f of prd.features) {
      assert.ok(f.name);
      assert.ok(f.description);
      assert.ok(f.priority);
      assert.ok(["P0", "P1", "P2"].includes(f.priority));
    }
  });

  it("pages 包含 route 字段", () => {
    const prd = generatePRD("做一个宠物管理 App");
    for (const p of prd.pages) {
      assert.ok(p.name);
      assert.ok(p.route);
      assert.ok(p.description);
    }
  });

  it("apiRequirements 包含 method 和 path", () => {
    const prd = generatePRD("做一个宠物管理 App");
    for (const a of prd.apiRequirements) {
      assert.ok(a.method);
      assert.ok(a.path);
      assert.ok(a.description);
      assert.ok(["GET", "POST", "PUT", "DELETE"].includes(a.method));
    }
  });

  it("devTasks 包含所有必需字段", () => {
    const prd = generatePRD("做一个宠物管理 App");
    for (const t of prd.devTasks) {
      assert.ok(t.id.startsWith("T-"));
      assert.ok(t.title);
      assert.ok(t.description);
      assert.ok(t.phase);
      assert.ok(typeof t.estimatedHours === "number");
      assert.ok(t.estimatedHours > 0);
      assert.ok(t.priority);
    }
  });

  it("MVP scope 仅包含 P0 功能", () => {
    const prd = generatePRD("做一个电商小程序");
    const p0Features = prd.features.filter(f => f.priority === "P0").map(f => f.name);
    for (const f of prd.mvpScope.features) {
      assert.ok(p0Features.includes(f), `${f} should be P0`);
    }
  });

  it("meta 包含时间戳和输入长度", () => {
    const prd = generatePRD("做一个宠物管理 App");
    assert.ok(prd.meta.generatedAt);
    assert.ok(typeof prd.meta.inputLength === "number");
    assert.ok(typeof prd.meta.domainMatchCount === "number");
  });
});

// ═══════════════════════════════════════════════════════
// 10 — 任务拆解
// ═══════════════════════════════════════════════════════
describe("10 — 任务拆解", () => {
  it("任务包含 Foundation 阶段", () => {
    const prd = generatePRD("做一个宠物管理 App");
    const foundationTasks = prd.devTasks.filter(t => t.phase === "Foundation");
    assert.ok(foundationTasks.length >= 2);
  });

  it("任务包含 UI Development 和 Backend Development", () => {
    const prd = generatePRD("做一个电商小程序");
    const phases = new Set(prd.devTasks.map(t => t.phase));
    assert.ok(phases.has("UI Development"));
    assert.ok(phases.has("Backend Development"));
  });

  it("任务包含 Integration 和 Testing", () => {
    const prd = generatePRD("做一个在线教育平台");
    const phases = new Set(prd.devTasks.map(t => t.phase));
    assert.ok(phases.has("Integration"));
    assert.ok(phases.has("Testing"));
  });

  it("总工时计算合理", () => {
    const prd = generatePRD("做一个企业办公自动化系统");
    const totalHours = prd.devTasks.reduce((s, t) => s + t.estimatedHours, 0);
    assert.ok(totalHours > 40, `Expected > 40 hours, got ${totalHours}`);
    assert.ok(totalHours < 500, `Expected < 500 hours, got ${totalHours}`);
  });

  it("所有领域的任务都有合理的工时", () => {
    const inputs = [
      "做一个宠物管理 App",
      "做一个电商小程序",
      "做一个在线教育平台",
      "笔记应用",
      "做一个企业办公自动化系统",
      "做一个健身打卡 App",
    ];

    for (const input of inputs) {
      const prd = generatePRD(input);
      // Just verify no zero-hour tasks
      for (const t of prd.devTasks) {
        assert.ok(t.estimatedHours > 0, `${input}: task ${t.id} has 0 hours`);
      }
    }
  });
});

// ═══════════════════════════════════════════════════════
// 11 — Output File I/O
// ═══════════════════════════════════════════════════════
describe("11 — File I/O", () => {
  it("loadInput 读取正确的 input 字段", () => {
    const { data } = loadInput(resolve(FIXTURES_DIR, "pet-management.json"));
    assert.ok(data.input);
    assert.equal(data.input, "做一个宠物管理 App");
  });

  it("loadInput 对不存在的文件返回 error", () => {
    const { data, error } = loadInput("/nonexistent/path.json");
    assert.equal(data, null);
    assert.ok(error);
  });

  it("writeOutput 写入文件", () => {
    const prd = generatePRD("做一个宠物管理 App");
    const tmpPath = resolve(WORKSPACE, ".tmp-prd-test-output.json");

    writeOutput(prd, tmpPath, true);
    assert.ok(existsSync(tmpPath));

    // Clean up
    rmSync(tmpPath);
  });
});

// ═══════════════════════════════════════════════════════
// 12 — 领域匹配引擎
// ═══════════════════════════════════════════════════════
describe("12 — 领域匹配引擎", () => {
  it("matchDomain 正确识别各领域", () => {
    const cases = [
      { input: "宠物", expected: "pet" },
      { input: "电商商城购物", expected: "ecommerce" },
      { input: "在线课程教学", expected: "education" },
      { input: "企业OA审批", expected: "enterprise" },
      { input: "健身运动打卡", expected: "fitness" },
      { input: "笔记备忘录", expected: "note" },
      { input: "社交朋友圈", expected: "social" },
      { input: "外卖点餐", expected: "food" },
      { input: "旅游酒店机票", expected: "travel" },
    ];

    for (const { input, expected } of cases) {
      const { domainKey } = matchDomain(input);
      assert.equal(domainKey, expected, `"${input}" → expected ${expected}, got ${domainKey}`);
    }
  });

  it("多领域关键词选最多匹配的", () => {
    // "宠物电商" → pet keywords: [宠物] = 1, ecommerce keywords: [电商] = 1 → tie, first match wins (pet)
    const { domainKey } = matchDomain("宠物电商平台");
    // pet has 1 match, ecommerce also 1 match but pet comes first
    assert.ok(["pet", "ecommerce"].includes(domainKey));
  });

  it("无匹配回退到 generic", () => {
    const { domainKey, matchCount } = matchDomain("量子计算区块链");
    assert.equal(domainKey, "generic");
    assert.equal(matchCount, 0);
  });
});

// ═══════════════════════════════════════════════════════
// 13 — 平台检测
// ═══════════════════════════════════════════════════════
describe("13 — 平台检测", () => {
  it("默认返回 mobile + web", () => {
    const platforms = detectPlatforms("做一个任意应用");
    assert.deepEqual(platforms, ["mobile", "web"]);
  });

  it("检测所有已知平台", () => {
    const cases = [
      { input: "小程序", expected: "miniapp" },
      { input: "iOS App", expected: "ios" },
      { input: "安卓应用", expected: "android" },
      { input: "Web 网页", expected: "web" },
      { input: "桌面端PC", expected: "desktop" },
    ];

    for (const { input, expected } of cases) {
      const platforms = detectPlatforms(input);
      assert.ok(platforms.includes(expected), `"${input}" should include ${expected}`);
    }
  });

  it("检测多个平台", () => {
    const platforms = detectPlatforms("做一个 iOS 和 Android 应用");
    assert.ok(platforms.includes("ios"));
    assert.ok(platforms.includes("android"));
  });
});

// ═══════════════════════════════════════════════════════
// 14 — 额外特性检测
// ═══════════════════════════════════════════════════════
describe("14 — 额外特性检测", () => {
  it("检测到支付特性", () => {
    const features = detectExtraFeatures("需要微信支付和支付宝");
    assert.ok(features.includes("wechat-pay"));
    assert.ok(features.includes("alipay"));
  });

  it("检测到 AI 和推送", () => {
    const features = detectExtraFeatures("AI 智能推荐 + 推送通知");
    assert.ok(features.includes("ai-powered"));
    assert.ok(features.includes("smart-recommendation"));
    assert.ok(features.includes("push-notification"));
  });

  it("检测到离线模式和多语言", () => {
    const features = detectExtraFeatures("需要支持离线模式和多语言国际化");
    assert.ok(features.includes("offline-mode"));
    assert.ok(features.includes("i18n"));
  });

  it("无特性时返回空数组", () => {
    const features = detectExtraFeatures("做一个普通应用");
    assert.deepEqual(features, []);
  });
});

// ═══════════════════════════════════════════════════════
// 15 — CLI 端到端
// ═══════════════════════════════════════════════════════
describe("15 — CLI 端到端", () => {
  it("通过 --input 直接传入需求", async () => {
    const { execSync } = await import("node:child_process");
    const result = execSync(
      `node scripts/project-intake-agent.mjs --input "做一个宠物管理 App" --pretty`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    const parsed = JSON.parse(result);
    assert.equal(parsed.projectName, "PetCare");
    assert.equal(parsed.domain, "pet");
  });

  it("通过 --input-file 传入需求", async () => {
    const { execSync } = await import("node:child_process");
    const result = execSync(
      `node scripts/project-intake-agent.mjs --input-file test/fixtures/project-intake/edu-platform.json --pretty`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    const parsed = JSON.parse(result);
    assert.equal(parsed.projectName, "EduPlatform");
  });

  it("--help 显示帮助", async () => {
    const { execSync } = await import("node:child_process");
    const result = execSync(
      `node scripts/project-intake-agent.mjs --help`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    assert.ok(result.includes("Usage"));
    assert.ok(result.includes("project-intake-agent"));
  });

  it("缺少 input 时返回错误", async () => {
    const { execSync } = await import("node:child_process");
    try {
      execSync(`node scripts/project-intake-agent.mjs`, { cwd: WORKSPACE, encoding: "utf-8" });
      assert.fail("Should have thrown");
    } catch (e) {
      assert.ok(e.stderr.includes("required") || e.status !== 0);
    }
  });

  it("--output 写入文件", async () => {
    const { execSync } = await import("node:child_process");
    const tmpPath = resolve(WORKSPACE, ".tmp-prd-e2e-output.json");
    execSync(
      `node scripts/project-intake-agent.mjs --input "笔记应用" --output ${tmpPath} --pretty`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    assert.ok(existsSync(tmpPath));

    const content = JSON.parse(readFileSync(tmpPath, "utf-8"));
    assert.equal(content.projectName, "NoteApp");

    rmSync(tmpPath);
  });
});
