/**
 * Electron Builder Agent Test — SF-06
 *
 * 验证 Electron Wrapper 生成器的所有关键功能。
 */

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 buildElectron, writeElectron;

before(async () => {
  const mod = await import("../scripts/electron-builder-agent.mjs");
  buildElectron = mod.buildElectron;
  writeElectron = mod.writeElectron;
});

// ═══════════════════════════════════════════════════════
// 1 — Desktop Project 生成
// ═══════════════════════════════════════════════════════

describe("1 — Desktop Project 结构生成", () => {
  let result;
  before(() => {
    result = buildElectron({ projectName: "PetCare" });
  });

  it("生成成功（无错误）", () => {
    assert.ok(!result.error, `Unexpected error: ${result.error}`);
    assert.ok(result.stats.totalFiles >= 8, `Expected >= 8 files, got ${result.stats.totalFiles}`);
  });

  it("包含 electron/ 目录下的所有核心文件", () => {
    assert.ok(result.files["electron/main.ts"], "Missing main.ts");
    assert.ok(result.files["electron/preload.ts"], "Missing preload.ts");
    assert.ok(result.files["electron/ipc.ts"], "Missing ipc.ts");
    assert.ok(result.files["electron/tray.ts"], "Missing tray.ts");
    assert.ok(result.files["electron/updater.ts"], "Missing updater.ts");
    assert.ok(result.files["electron/utils.ts"], "Missing utils.ts");
  });

  it("包含 assets/icon.png", () => {
    assert.ok(result.files["assets/icon.png"], "Missing icon.png");
    assert.ok(result.files["assets/icon.png"].length > 0, "Empty icon.png");
  });
});

// ═══════════════════════════════════════════════════════
// 2 — main.ts 内容验证
// ═══════════════════════════════════════════════════════

describe("2 — main.ts 内容验证", () => {
  let result;
  before(() => {
    result = buildElectron({ projectName: "PetCare" });
  });

  it("包含 BrowserWindow", () => {
    assert.ok(result.files["electron/main.ts"].includes("BrowserWindow"));
  });

  it("包含 Menu", () => {
    assert.ok(result.files["electron/main.ts"].includes("Menu"));
  });

  it("包含 Tray setup", () => {
    assert.ok(result.files["electron/main.ts"].includes("setupTray"));
  });

  it("包含 IPC setup", () => {
    assert.ok(result.files["electron/main.ts"].includes("setupIPC"));
  });

  it("包含 Single Instance Lock", () => {
    assert.ok(result.files["electron/main.ts"].includes("checkSingleInstance"));
    assert.ok(result.files["electron/utils.ts"].includes("requestSingleInstanceLock"));
  });

  it("包含 Auto Start 配置", () => {
    assert.ok(result.files["electron/main.ts"].includes("activate"));
  });

  it("包含 contextIsolation: true", () => {
    assert.ok(result.files["electron/main.ts"].includes("contextIsolation: true"));
  });

  it("包含 nodeIntegration: false", () => {
    assert.ok(result.files["electron/main.ts"].includes("nodeIntegration: false"));
  });

  it("开发环境加载 localhost URL", () => {
    assert.ok(result.files["electron/main.ts"].includes("localhost"));
  });

  it("生产环境加载 file 或 resources 路径", () => {
    const main = result.files["electron/main.ts"];
    assert.ok(main.includes("resourcesPath") || main.includes("loadFile"));
  });

  it("包含外部链接处理", () => {
    assert.ok(result.files["electron/main.ts"].includes("setWindowOpenHandler"));
  });

  it("包含 GPU crash 处理", () => {
    assert.ok(result.files["electron/main.ts"].includes("gpu-process-crashed"));
  });

  it("包含 API server 生产环境启动", () => {
    assert.ok(result.files["electron/main.ts"].includes("startApiServer"));
  });
});

// ═══════════════════════════════════════════════════════
// 3 — preload.ts 内容验证
// ═══════════════════════════════════════════════════════

describe("3 — preload.ts 内容验证", () => {
  let result;
  before(() => {
    result = buildElectron({ projectName: "CRM" });
  });

  it("使用 contextBridge", () => {
    assert.ok(result.files["electron/preload.ts"].includes("contextBridge"));
  });

  it("使用 ipcRenderer", () => {
    assert.ok(result.files["electron/preload.ts"].includes("ipcRenderer"));
  });

  it("暴露 electronAPI", () => {
    assert.ok(result.files["electron/preload.ts"].includes("electronAPI"));
  });

  it("包含窗口控制 API", () => {
    const preload = result.files["electron/preload.ts"];
    assert.ok(preload.includes("minimizeWindow"));
    assert.ok(preload.includes("maximizeWindow"));
    assert.ok(preload.includes("closeWindow"));
  });

  it("包含文件对话框 API", () => {
    const preload = result.files["electron/preload.ts"];
    assert.ok(preload.includes("openFile"));
    assert.ok(preload.includes("saveFile"));
  });

  it("包含通知 API", () => {
    assert.ok(result.files["electron/preload.ts"].includes("showNotification"));
  });

  it("包含持久化存储 API", () => {
    const preload = result.files["electron/preload.ts"];
    assert.ok(preload.includes("storeGet"));
    assert.ok(preload.includes("storeSet"));
    assert.ok(preload.includes("storeDelete"));
  });

  it("包含更新 API", () => {
    const preload = result.files["electron/preload.ts"];
    assert.ok(preload.includes("checkForUpdates"));
    assert.ok(preload.includes("downloadUpdate"));
    assert.ok(preload.includes("quitAndInstall"));
  });

  it("包含 TypeScript 类型声明", () => {
    assert.ok(result.files["electron/preload.ts"].includes("interface ElectronAPI"));
    assert.ok(result.files["electron/preload.ts"].includes("declare global"));
  });
});

// ═══════════════════════════════════════════════════════
// 4 — package.json 验证
// ═══════════════════════════════════════════════════════

describe("4 — package.json 验证", () => {
  let result, pkg;
  before(() => {
    result = buildElectron({ projectName: "Inventory" });
    pkg = JSON.parse(result.files["package.json"]);
  });

  it("包含正确名称", () => {
    assert.ok(pkg.name.includes("inventory-desktop"));
  });

  it("包含 main 入口", () => {
    assert.equal(pkg.main, "dist/electron/main.js");
  });

  it("包含 dev 脚本", () => {
    assert.ok(pkg.scripts.dev, "Missing dev script");
    assert.ok(pkg.scripts.dev.includes("concurrently"));
  });

  it("包含 build 脚本", () => {
    assert.ok(pkg.scripts.build, "Missing build script");
  });

  it("包含平台构建脚本", () => {
    assert.ok(pkg.scripts["build:electron:win"], "Missing win build");
    assert.ok(pkg.scripts["build:electron:mac"], "Missing mac build");
    assert.ok(pkg.scripts["build:electron:linux"], "Missing linux build");
  });

  it("依赖 electron", () => {
    assert.ok(pkg.devDependencies.electron, "Missing electron");
  });

  it("依赖 electron-builder", () => {
    assert.ok(pkg.devDependencies["electron-builder"], "Missing electron-builder");
  });

  it("依赖 electron-updater", () => {
    assert.ok(pkg.dependencies["electron-updater"], "Missing electron-updater");
  });

  it("依赖 concurrently", () => {
    assert.ok(pkg.devDependencies.concurrently, "Missing concurrently");
  });

  it("依赖 typescript", () => {
    assert.ok(pkg.devDependencies.typescript, "Missing typescript");
  });

  it("build 配置包含 appId", () => {
    assert.ok(pkg.build.appId.includes("inventory.desktop"));
  });

  it("build 配置包含 productName", () => {
    assert.equal(pkg.build.productName, "Inventory");
  });
});

// ═══════════════════════════════════════════════════════
// 5 — electron-builder.yml 验证
// ═══════════════════════════════════════════════════════

describe("5 — electron-builder.yml 验证", () => {
  let result, yml;
  before(() => {
    result = buildElectron({ projectName: "PetCare" });
    yml = result.files["electron-builder.yml"];
  });

  it("包含 appId", () => {
    assert.ok(yml.includes("appId: com.petcare.desktop"));
  });

  it("包含 productName", () => {
    assert.ok(yml.includes("productName: PetCare"));
  });

  it("包含 macOS 配置", () => {
    assert.ok(yml.includes("mac:"));
    assert.ok(yml.includes("dmg"));
    assert.ok(yml.includes("arm64"));
  });

  it("包含 Windows 配置", () => {
    assert.ok(yml.includes("win:"));
    assert.ok(yml.includes("nsis"));
  });

  it("包含 Linux 配置", () => {
    assert.ok(yml.includes("linux:"));
    assert.ok(yml.includes("AppImage"));
    assert.ok(yml.includes("deb"));
  });

  it("包含 extraResources（web + api）", () => {
    assert.ok(yml.includes("extraResources"));
    assert.ok(yml.includes("../web/out"));
    assert.ok(yml.includes("../api/dist"));
  });

  it("包含 publish 配置", () => {
    assert.ok(yml.includes("publish:"));
    assert.ok(yml.includes("provider: generic"));
  });
});

// ═══════════════════════════════════════════════════════
// 6 — tsconfig.json 验证
// ═══════════════════════════════════════════════════════

describe("6 — tsconfig.json 验证", () => {
  let result, tsconfig;
  before(() => {
    result = buildElectron();
    tsconfig = JSON.parse(result.files["tsconfig.json"]);
  });

  it("target 是 ES2022", () => {
    assert.equal(tsconfig.compilerOptions.target, "ES2022");
  });

  it("module 是 commonjs（Electron 主进程需要）", () => {
    assert.equal(tsconfig.compilerOptions.module, "commonjs");
  });

  it("strict 模式开启", () => {
    assert.equal(tsconfig.compilerOptions.strict, true);
  });

  it("outDir 是 dist", () => {
    assert.equal(tsconfig.compilerOptions.outDir, "dist");
  });

  it("include 包含 electron/", () => {
    assert.ok(tsconfig.include.some(p => p.includes("electron")));
  });
});

// ═══════════════════════════════════════════════════════
// 7 — 其他文件验证
// ═══════════════════════════════════════════════════════

describe("7 — 辅助文件验证", () => {
  let result;
  before(() => {
    result = buildElectron({ projectName: "CRM" });
  });

  it("README.md 存在且包含关键内容", () => {
    const readme = result.files["README.md"];
    assert.ok(readme, "Missing README.md");
    assert.ok(readme.includes("CRM"), "Missing project name");
    assert.ok(readme.includes("Development"), "Missing Development section");
    assert.ok(readme.includes("Build"), "Missing Build section");
    assert.ok(readme.includes("IPC API"), "Missing IPC API section");
  });

  it("entitlements.mac.plist 存在", () => {
    const plist = result.files["entitlements.mac.plist"];
    assert.ok(plist, "Missing entitlements.mac.plist");
    assert.ok(plist.includes("com.apple.security.network.client"));
  });

  it("icon.png 存在且非空", () => {
    assert.ok(result.files["assets/icon.png"], "Missing icon.png");
    assert.ok(result.files["assets/icon.png"].length > 0);
  });
});

// ═══════════════════════════════════════════════════════
// 8 — 自定义端口配置
// ═══════════════════════════════════════════════════════

describe("8 — 自定义端口配置", () => {
  it("使用自定义 web 端口", () => {
    const result = buildElectron({ webPort: 4000 });
    assert.ok(result.files["electron/main.ts"].includes("4000"));
  });

  it("使用自定义 api 端口", () => {
    const result = buildElectron({ apiPort: 4001 });
    assert.ok(result.files["electron/main.ts"].includes("4001"));
    assert.ok(result.files["electron/utils.ts"].includes("4001"));
  });

  it("默认端口是 3000/3001", () => {
    const result = buildElectron();
    assert.ok(result.files["electron/main.ts"].includes("3000"));
    assert.ok(result.files["electron/main.ts"].includes("3001"));
  });
});

// ═══════════════════════════════════════════════════════
// 9 — 写入 I/O
// ═══════════════════════════════════════════════════════

describe("9 — 文件写入 I/O", () => {
  it("writeElectron 写入完整目录树", () => {
    const result = buildElectron({ projectName: "TestApp" });
    const tmpDir = resolve(WORKSPACE, ".tmp-electron-test");

    writeElectron(result, tmpDir);

    assert.ok(existsSync(resolve(tmpDir, "package.json")));
    assert.ok(existsSync(resolve(tmpDir, "electron-builder.yml")));
    assert.ok(existsSync(resolve(tmpDir, "tsconfig.json")));
    assert.ok(existsSync(resolve(tmpDir, "electron/main.ts")));
    assert.ok(existsSync(resolve(tmpDir, "electron/preload.ts")));
    assert.ok(existsSync(resolve(tmpDir, "electron/ipc.ts")));
    assert.ok(existsSync(resolve(tmpDir, "electron/tray.ts")));
    assert.ok(existsSync(resolve(tmpDir, "electron/updater.ts")));
    assert.ok(existsSync(resolve(tmpDir, "electron/utils.ts")));
    assert.ok(existsSync(resolve(tmpDir, "assets/icon.png")));
    assert.ok(existsSync(resolve(tmpDir, "entitlements.mac.plist")));
    assert.ok(existsSync(resolve(tmpDir, "README.md")));

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

  it("所有生成的文件非空", () => {
    const result = buildElectron();
    for (const [path, content] of Object.entries(result.files)) {
      const len = typeof content === "string" ? content.length : content.byteLength;
      assert.ok(len > 0, `${path}: empty content`);
    }
  });

  it("所有 package.json 可解析", () => {
    const result = buildElectron();
    const pkg = JSON.parse(result.files["package.json"]);
    assert.ok(pkg.name, "Missing package name");
    assert.ok(pkg.scripts, "Missing scripts");
  });
});

// ═══════════════════════════════════════════════════════
// 10 — 多领域覆盖
// ═══════════════════════════════════════════════════════

describe("10 — 多领域覆盖（PetCare / CRM / Inventory）", () => {
  const domains = [
    { name: "PetCare", expected: "petcare" },
    { name: "CRM", expected: "crm" },
    { name: "Inventory", expected: "inventory" },
  ];

  for (const { name, expected } of domains) {
    it(`${name} — 生成成功`, () => {
      const result = buildElectron({ projectName: name });
      assert.ok(!result.error, `${name}: ${result.message}`);
      assert.ok(result.files["electron/main.ts"], `${name}: missing main.ts`);
      assert.ok(result.files["electron/preload.ts"], `${name}: missing preload.ts`);
      assert.ok(result.files["package.json"], `${name}: missing package.json`);
      assert.ok(result.files["electron-builder.yml"], `${name}: missing electron-builder.yml`);
    });

    it(`${name} — package.json 包含正确名称`, () => {
      const result = buildElectron({ projectName: name });
      const pkg = JSON.parse(result.files["package.json"]);
      assert.ok(pkg.name.includes(expected), `${name}: expected '${expected}' in name, got '${pkg.name}'`);
    });

    it(`${name} — electron-builder.yml 包含正确 appId`, () => {
      const result = buildElectron({ projectName: name });
      assert.ok(
        result.files["electron-builder.yml"].includes(`com.${expected}.desktop`),
        `${name}: expected 'com.${expected}.desktop' in appId`
      );
    });
  }
});
