#!/usr/bin/env node
/**
 * Orchestrator Test Suite — V2 × Factory Fusion
 *
 * Tests the minimal orchestrator pipeline:
 *   - Empty input → error
 *   - Valid input → PRD + Arch + Full-stack generation
 *   - progress.log heartbeat entries are written
 *   - Task Tree + Plan structure are returned
 *   - Governance integration (heartbeat frequency, status flow)
 *
 * Run: node --test test/orchestrator.test.mjs
 */

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

const __dirname = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
const ROOT = __dirname;
const SCRIPTS = join(ROOT, "scripts");

let runOrchestrator;

// ═══════════════════════════════════════════════════════
// 1 — Empty / Invalid Input
// ═══════════════════════════════════════════════════════
describe("1 — Empty / Invalid Input", () => {
  before(async () => {
    const mod = await import(join(SCRIPTS, "orchestrator.mjs"));
    runOrchestrator = mod.run;
  });

  it("空输入抛出 EMPTY_INPUT 错误", async () => {
    try {
      await runOrchestrator("");
      assert.fail("Should have thrown");
    } catch (e) {
      assert.ok(e.message.includes("empty input"));
    }
  });

  it("空格输入抛出 EMPTY_INPUT 错误", async () => {
    try {
      await runOrchestrator("   ");
      assert.fail("Should have thrown");
    } catch (e) {
      assert.ok(e.message.includes("empty input"));
    }
  });

  it("极简输入仍能生成完整项目", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-test");
    const result = await runOrchestrator("做一个宠物管理 App", { output: tmpDir });
    assert.ok(result.summary.projectName);
    assert.ok(result.summary.outputDir);
    assert.ok(result.summary.stats.totalFiles > 50);
    rmSync(tmpDir, { recursive: true, force: true });
  });
});

// ═══════════════════════════════════════════════════════
// 2 — Task Tree + Plan Structure
// ═══════════════════════════════════════════════════════
describe("2 — Task Tree + Plan Structure", () => {
  it("返回 plan 包含 goal / subtasks / risks", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-plan-test");
    const result = await runOrchestrator("做一个在线教育平台", { output: tmpDir });
    assert.ok(result.plan.goal);
    assert.ok(Array.isArray(result.plan.subtasks));
    assert.ok(result.plan.subtasks.length === 4);
    assert.ok(Array.isArray(result.plan.risks));
    assert.ok(result.plan.heartbeat);
    rmSync(tmpDir, { recursive: true, force: true });
  });

  it("返回 taskTree 包含 root + tasks", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-tasktree-test");
    const result = await runOrchestrator("做一个电商小程序", { output: tmpDir });
    assert.ok(result.taskTree.root);
    assert.equal(result.taskTree.root.id, "T0");
    assert.ok(Array.isArray(result.taskTree.tasks));
    assert.ok(result.taskTree.tasks.length === 4);
    assert.ok(result.taskTree.injectedRisks.length > 0);
    rmSync(tmpDir, { recursive: true, force: true });
  });
});

// ═══════════════════════════════════════════════════════
// 3 — progress.log Heartbeat (Governance Integration)
// ═══════════════════════════════════════════════════════
describe("3 — progress.log Heartbeat", () => {
  const LOG_PATH = join(ROOT, "progress.log");

  before(async () => {
    // Clean up
    rmSync(LOG_PATH, { force: true });
  });

  it("progress.log 写入 5 条心跳", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-heartbeat-test");
    await runOrchestrator("做一个宠物管理 App", { output: tmpDir });

    const content = readFileSync(LOG_PATH, "utf8");
    const lines = content.trim().split("\n").filter(Boolean);
    assert.equal(lines.length, 5);

    // Parse all entries
    const entries = lines.map(l => JSON.parse(l));

    // First: IN_PROGRESS (NOT_STARTED phase)
    assert.equal(entries[0].status, "IN_PROGRESS");
    assert.deepStrictEqual(entries[0].completed, []);

    // Middle: progressive completion
    assert.equal(entries[1].status, "IN_PROGRESS");
    assert.deepStrictEqual(entries[1].completed, ["T1"]);

    assert.equal(entries[2].status, "IN_PROGRESS");
    assert.deepStrictEqual(entries[2].completed, ["T1", "T2"]);

    assert.equal(entries[3].status, "IN_PROGRESS");
    assert.deepStrictEqual(entries[3].completed, ["T1", "T2", "T3"]);

    // Final: ARCHIVED
    assert.equal(entries[4].status, "ARCHIVED");
    assert.deepStrictEqual(entries[4].completed, ["T1", "T2", "T3", "T4"]);
    assert.deepStrictEqual(entries[4].remaining, []);
    assert.equal(entries[4].next_action, "none");

    rmSync(tmpDir, { recursive: true, force: true });
  });

  it("progress.log 每条心跳都有 ts/status/completed/remaining/next_action", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-schema-test");
    await runOrchestrator("做一个在线教育平台", { output: tmpDir });

    const content = readFileSync(LOG_PATH, "utf8");
    const lines = content.trim().split("\n").filter(Boolean);

    for (const line of lines) {
      const entry = JSON.parse(line);
      assert.ok(entry.ts, "Missing ts");
      assert.ok(entry.status, "Missing status");
      assert.ok(Array.isArray(entry.completed), "Missing completed array");
      assert.ok(Array.isArray(entry.remaining), "Missing remaining array");
      assert.ok("next_action" in entry, "Missing next_action");
    }

    rmSync(tmpDir, { recursive: true, force: true });
  });
});

// ═══════════════════════════════════════════════════════
// 4 — Pipeline Integration
// ═══════════════════════════════════════════════════════
describe("4 — Pipeline Integration", () => {
  it("输出目录包含 apps/web/package.json 和 apps/api/package.json", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-pipeline-test");
    await runOrchestrator("做一个宠物管理 App", { output: tmpDir });

    assert.ok(existsSync(join(tmpDir, "apps", "web", "package.json")), "Missing apps/web/package.json");
    assert.ok(existsSync(join(tmpDir, "apps", "api", "package.json")), "Missing apps/api/package.json");
    assert.ok(existsSync(join(tmpDir, "packages", "shared-types", "package.json")), "Missing shared-types/package.json");
    assert.ok(existsSync(join(tmpDir, "package.json")), "Missing root package.json");

    rmSync(tmpDir, { recursive: true, force: true });
  });

  it("多次运行不产生冲突（progress.log 追加）", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-multi-test");
    const logPath = join(ROOT, "progress.log");

    // Reset log for this test
    rmSync(logPath, { force: true });

    // Run 1
    await runOrchestrator("做一个宠物管理 App", { output: join(tmpDir, "v1") });
    // Run 2
    await runOrchestrator("做一个在线教育平台", { output: join(tmpDir, "v2") });

    const content = readFileSync(logPath, "utf8");
    const lines = content.trim().split("\n").filter(Boolean);
    // 2 runs × 5 heartbeats = 10
    assert.equal(lines.length, 10);

    rmSync(tmpDir, { recursive: true, force: true });
  });
});

// ═══════════════════════════════════════════════════════
// 5 — Summary Structure
// ═══════════════════════════════════════════════════════
describe("5 — Summary Structure", () => {
  it("summary 包含所有必需字段", async () => {
    const tmpDir = resolve(ROOT, ".tmp-orchestrator-summary-test");
    const result = await runOrchestrator("做一个宠物管理 App", { output: tmpDir });

    const s = result.summary;
    assert.ok(s.projectName, "Missing projectName");
    assert.ok(s.chineseName, "Missing chineseName");
    assert.ok(s.domain, "Missing domain");
    assert.ok(s.factory, "Missing factory");
    assert.ok(s.outputDir, "Missing outputDir");
    assert.ok(s.stats, "Missing stats");
    assert.ok(s.startTs, "Missing startTs");
    assert.ok(s.endTs, "Missing endTs");
    assert.ok(typeof s.durationMs === "number", "Missing durationMs");
    assert.ok(s.durationMs >= 0, "durationMs should be >= 0");

    rmSync(tmpDir, { recursive: true, force: true });
  });
});
