/**
 * Frontend Builder Agent Test — SF-03
 */

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, "..");

let buildFrontend, loadJSON, writeFrontend;

before(async () => {
  const mod = await import("../scripts/frontend-builder-agent.mjs");
  buildFrontend = mod.buildFrontend;
  loadJSON = mod.loadJSON;
  writeFrontend = mod.writeFrontend;
});

function loadPRD(name) {
  return JSON.parse(readFileSync(resolve(WORKSPACE, `test/fixtures/architecture-agent/${name}-prd.json`), "utf-8"));
}
function loadArch(name) {
  return JSON.parse(readFileSync(resolve(WORKSPACE, `architecture-example.json`), "utf-8"));
}

// ═══════════════════════════════════════════════════════
describe("1 — 宠物管理前端生成", () => {
  it("生成完整项目结构", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(!result.error);
    assert.ok(result.stats.totalFiles >= 20, `Expected >= 20 files, got ${result.stats.totalFiles}`);
    assert.ok(result.stats.pages >= 4);
    assert.ok(result.stats.components >= 6);
    assert.ok(result.stats.hooks >= 2);
    assert.ok(result.stats.services >= 2);
  });

  it("所有页面文件非空", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    for (const pf of result.stats.pageFiles) {
      const content = result.files[pf];
      assert.ok(content, `Missing file: ${pf}`);
      assert.ok(content.length > 100, `${pf}: content too short (${content.length} chars)`);
      assert.ok(content.includes("export default"), `${pf}: missing component export`);
    }
  });

  it("页面包含有意义的 JSX", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    // Each page must have at least a heading, a Button or real elements
    for (const pf of result.stats.pageFiles) {
      const c = result.files[pf];
      // Should contain JSX elements, not just "TODO" or "stub"
      const jsxCount = (c.match(/</g) || []).length;
      const minJSX = pf.includes("order") || pf.includes("tag") || pf.includes("settings") ? 3 : 5;
      assert.ok(jsxCount > minJSX, `${pf}: too few JSX elements (${jsxCount}, min ${minJSX})`);
    }
  });
});

// ═══════════════════════════════════════════════════════
describe("2 — TypeScript 类型生成", () => {
  it("生成类型文件", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["types/index.ts"]);
    const types = result.files["types/index.ts"];
    assert.ok(types.includes("export interface"));
    assert.ok(types.includes("User"));
  });

  it("电商域含 Product/Order 类型", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);
    const types = result.files["types/index.ts"];

    assert.ok(types.includes("Product"));
    assert.ok(types.includes("Order"));
    assert.ok(types.includes("Logistics"));
  });

  it("企业域含 Approval/Attendance 类型", () => {
    const prd = loadPRD("enterprise");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);
    const types = result.files["types/index.ts"];

    assert.ok(types.includes("Approval"));
    assert.ok(types.includes("Attendance"));
    assert.ok(types.includes("Department"));
  });

  it("API 响应类型存在", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);
    const types = result.files["types/index.ts"];

    assert.ok(types.includes("ApiResponse"));
    assert.ok(types.includes("PaginatedResponse"));
    assert.ok(types.includes("ApiError"));
  });
});

// ═══════════════════════════════════════════════════════
describe("3 — 组件生成", () => {
  it("生成所有共享 UI 组件", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    const uiComponents = ["Button", "Card", "Input", "Modal", "EmptyState"];
    for (const name of uiComponents) {
      assert.ok(result.files[`components/ui/${name}.tsx`], `Missing UI component: ${name}`);
      assert.ok(result.files[`components/ui/${name}.tsx`].includes("export function"), `${name}: missing export`);
    }
  });

  it("域名匹配的领域组件被生成", () => {
    // ecommerce PRD → ecommerce components
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);
    assert.ok(result.files["components/ecommerce/ProductCard.tsx"]);
  });

  it("Layout 组件包含 Sidebar 和 BottomNav", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["components/layout/Sidebar.tsx"]);
    assert.ok(result.files["components/layout/BottomNav.tsx"]);
    const sidebar = result.files["components/layout/Sidebar.tsx"];
    assert.ok(sidebar.includes("usePathname"));
  });
});

// ═══════════════════════════════════════════════════════
describe("4 — API Services", () => {
  it("生成 API 客户端", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["services/api.ts"]);
    const apiClient = result.files["services/api.ts"];
    assert.ok(apiClient.includes("get:") || apiClient.includes("api.get"), "api client missing get method");
    assert.ok(apiClient.includes("post:") || apiClient.includes("api.post"), "api client missing post method");
    assert.ok(apiClient.includes("put:") || apiClient.includes("api.put"), "api client missing put method");
    assert.ok(apiClient.includes("delete:") || apiClient.includes("api.delete"), "api client missing delete method");
  });

  it("按资源生成 service 文件", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    // PetCare arch has: albums, daily-logs, hospitals, pets, schedules
    assert.ok(result.files["services/pets.ts"]);
    assert.ok(result.files["services/schedules.ts"]);
  });
});

// ═══════════════════════════════════════════════════════
describe("5 — Custom Hooks", () => {
  it("生成通用 hooks", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["hooks/useForm.ts"]);
    assert.ok(result.files["hooks/useDebounce.ts"]);
  });

  it("按资源生成数据 hooks", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["hooks/usePets.ts"]);
    assert.ok(result.files["hooks/useSchedules.ts"]);

    const usePets = result.files["hooks/usePets.ts"];
    assert.ok(usePets.includes("useState"));
    assert.ok(usePets.includes("useEffect"));
  });
});

// ═══════════════════════════════════════════════════════
describe("6 — 项目配置", () => {
  it("生成 package.json", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["package.json"]);
    const pkg = JSON.parse(result.files["package.json"]);
    assert.ok(pkg.dependencies.next);
    assert.ok(pkg.dependencies.react);
    assert.ok(pkg.devDependencies.typescript);
  });

  it("生成 tsconfig.json", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    const tsconfig = JSON.parse(result.files["tsconfig.json"]);
    assert.equal(tsconfig.compilerOptions.strict, true);
    assert.ok(tsconfig.compilerOptions.paths["@/*"]);
  });

  it("生成 tailwind config", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    assert.ok(result.files["tailwind.config.ts"]);
    assert.ok(result.files["postcss.config.js"]);
    assert.ok(result.files["app/globals.css"]);
  });
});

// ═══════════════════════════════════════════════════════
describe("7 — 布局和路由", () => {
  it("Root layout 包含导航项", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    const layout = result.files["app/layout.tsx"];
    assert.ok(layout.includes("Sidebar"));
    assert.ok(layout.includes("BottomNav"));
    assert.ok(layout.includes("children"));
  });

  it("每个页面有独立 route 文件", () => {
    const prd = loadPRD("ecommerce");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);

    // Should have one page per route
    const routes = result.stats.pageFiles.map(f => f.replace("app/", "/").replace("/page.tsx", ""));
    assert.ok(routes.length >= 4);
  });
});

// ═══════════════════════════════════════════════════════
describe("8 — 错误处理", () => {
  it("invalid PRD 返回错误", () => {
    const result = buildFrontend({ error: "test" }, {});
    assert.equal(result.error, "INVALID_PRD");
  });

  it("invalid Arch 返回错误", () => {
    const prd = loadPRD("note");
    const result = buildFrontend(prd, { error: "test" });
    assert.equal(result.error, "INVALID_ARCH");
  });

  it("null 参数返回错误", () => {
    const result = buildFrontend(null, {});
    assert.equal(result.error, "INVALID_PRD");
  });
});

// ═══════════════════════════════════════════════════════
describe("9 — File I/O", () => {
  it("writeFrontend 写入文件系统", () => {
    const prd = loadPRD("note");
    const arch = loadArch("pet");
    const result = buildFrontend(prd, arch);
    const tmpDir = resolve(WORKSPACE, ".tmp-frontend-test");

    writeFrontend(result, tmpDir);

    assert.ok(existsSync(resolve(tmpDir, "package.json")));
    assert.ok(existsSync(resolve(tmpDir, "app/layout.tsx")));
    assert.ok(existsSync(resolve(tmpDir, "types/index.ts")));

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

  it("loadJSON 读取文件", () => {
    const { data } = loadJSON(resolve(WORKSPACE, "prd-example.json"));
    assert.ok(data.projectName);
  });

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

// ═══════════════════════════════════════════════════════
describe("10 — 全管道集成", () => {
  it("--prd + --arch CLI 模式", async () => {
    const { execSync } = await import("node:child_process");
    const tmpDir = resolve(WORKSPACE, ".tmp-cli-frontend");

    const result = execSync(
      `node scripts/frontend-builder-agent.mjs --prd prd-example.json --arch architecture-example.json --output ${tmpDir}`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    const parsed = JSON.parse(result);
    assert.equal(parsed.projectName, "PetCare");
    assert.ok(parsed.stats.totalFiles > 20);

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

  it("--input-text CLI 模式", async () => {
    const { execSync } = await import("node:child_process");
    const tmpDir = resolve(WORKSPACE, ".tmp-cli-frontend2");

    const result = execSync(
      `node scripts/frontend-builder-agent.mjs --input-text "做一个在线教育平台" --output ${tmpDir}`,
      { cwd: WORKSPACE, encoding: "utf-8" }
    );
    const parsed = JSON.parse(result);
    assert.equal(parsed.projectName, "EduPlatform");
    assert.ok(parsed.stats.totalFiles > 15);

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

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

// ═══════════════════════════════════════════════════════
describe("11 — 多领域覆盖", () => {
  const domains = ["ecommerce", "enterprise", "education", "note"];

  for (const domain of domains) {
    it(`${domain} 域生成有意义页面`, () => {
      const prd = loadPRD(domain);
      const arch = loadArch("pet"); // Any valid arch works
      const result = buildFrontend(prd, arch);

      assert.ok(!result.error);
      assert.ok(result.stats.pages >= 3, `${domain}: expected >= 3 pages, got ${result.stats.pages}`);

      // Verify at least one page has rich content
      const pageContents = result.stats.pageFiles.map(pf => result.files[pf]);
      const richPages = pageContents.filter(c => (c.match(/</g) || []).length > 8);
      const minRich = domain === "note" ? 1 : 2;
      assert.ok(richPages.length >= minRich, `${domain}: expected >= ${minRich} rich pages, got ${richPages.length}`);
    });
  }
});
