#!/usr/bin/env node

/**
 * Release Builder Agent — SF-07
 *
 * 将 SF-06 Electron Project 封装为可发布软件包结构。
 *
 * 职责：Electron Project → Release Package Structure
 * 不做：自动更新、CI/CD、Docker、云发布、安装器、签名、证书
 *
 * Usage:
 *   node scripts/release-builder-agent.mjs --input <electron-dir> [options]
 *
 * @module release-builder-agent
 */

import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
import { resolve, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { createHash } from "node:crypto";

const __dirname = dirname(resolve(fileURLToPath(import.meta.url)));
const WORKSPACE = resolve(__dirname, "..");

const GENERATOR_VERSION = "1.0.0";
const PLATFORMS = ["windows", "macos", "linux"];

// ═══════════════════════════════════════════════════════
// 1. Metadata Extraction
// ═══════════════════════════════════════════════════════



function extractMetadataSync(projectDir) {
  const meta = {
    projectName: "app",
    version: "1.0.0",
    buildNumber: 1,
    domain: "unknown",
    entities: [],
    routes: [],
    screens: [],
  };

  // Read root package.json
  const rootPkgPath = join(projectDir, "package.json");
  if (existsSync(rootPkgPath)) {
    try {
      const pkg = JSON.parse(readFileSync(rootPkgPath, "utf8"));
      meta.projectName = pkg.name || meta.projectName;
      meta.version = pkg.version || meta.version;
    } catch {}
  }

  // Read desktop package.json
  const desktopPkgPath = join(projectDir, "apps", "desktop", "package.json");
  if (existsSync(desktopPkgPath)) {
    try {
      const pkg = JSON.parse(readFileSync(desktopPkgPath, "utf8"));
      if (pkg.name) meta.projectName = pkg.name.replace(/-desktop$/, "");
      if (pkg.version) meta.version = pkg.version;
    } catch {}
  }

  // Read PRD if exists
  const prdPath = join(projectDir, "prd.json");
  if (existsSync(prdPath)) {
    try {
      const prd = JSON.parse(readFileSync(prdPath, "utf8"));
      if (prd.projectName) meta.projectName = prd.projectName;
      if (prd.domain) meta.domain = prd.domain;
      if (prd.features) meta.entities = prd.features.map(f => f.name || f);
      if (prd.apiRequirements) meta.routes = prd.apiRequirements.map(r => `${r.method} ${r.path}`);
      if (prd.pages) meta.screens = prd.pages.map(p => p.name || p);
    } catch {}
  }

  // Scan for entities in backend routes
  const routesDir = join(projectDir, "apps", "api", "src", "routes");
  if (existsSync(routesDir)) {
    try {
      const files = readdirSync(routesDir).filter(f => f.endsWith(".ts") || f.endsWith(".js"));
      if (meta.entities.length === 0) {
        meta.entities = files.map(f => f.replace(/\.(ts|js)$/, "").replace(/-route$/, "").replace(/\.route$/, ""));
      }
    } catch {}
  }

  // Scan for screens in frontend
  const appDir = join(projectDir, "apps", "web", "app");
  if (existsSync(appDir)) {
    try {
      const entries = readdirSync(appDir, { withFileTypes: true });
      if (meta.screens.length === 0) {
        meta.screens = entries
          .filter(e => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith("."))
          .map(e => e.name);
      }
    } catch {}
  }

  // Count API routes from index.ts
  const apiSrcDir = join(projectDir, "apps", "api", "src");
  if (existsSync(apiSrcDir) && meta.routes.length === 0) {
    try {
      const indexContent = readFileSync(join(apiSrcDir, "index.ts"), "utf8");
      const routeMatches = indexContent.match(/app\.(use|get|post|put|patch|delete)\s*\(\s*["']([^"']+)/g);
      if (routeMatches) meta.routes = routeMatches;
    } catch {}
  }

  return meta;
}

// ═══════════════════════════════════════════════════════
// 2. Version Generator
// ═══════════════════════════════════════════════════════

function generateVersion(meta) {
  return JSON.stringify({
    name: meta.projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
    version: meta.version,
    buildNumber: meta.buildNumber,
    platforms: [...PLATFORMS],
  }, null, 2);
}

// ═══════════════════════════════════════════════════════
// 3. Release Manifest
// ═══════════════════════════════════════════════════════

function generateManifest(meta, artifacts) {
  const files = [];
  for (const art of artifacts) {
    files.push({
      path: art.path,
      size: art.size,
      sha256: art.sha256,
    });
  }

  return JSON.stringify({
    project: meta.projectName,
    version: meta.version,
    buildNumber: meta.buildNumber,
    generatedAt: new Date().toISOString(),
    generator: "release-builder-agent",
    generatorVersion: GENERATOR_VERSION,
    platforms: [...PLATFORMS],
    files,
    totalFiles: files.length,
    totalSize: files.reduce((s, f) => s + f.size, 0),
  }, null, 2);
}

// ═══════════════════════════════════════════════════════
// 4. Checksum Generator
// ═══════════════════════════════════════════════════════

function sha256(content) {
  return createHash("sha256").update(typeof content === "string" ? content : content).digest("hex");
}

function generateChecksums(artifacts) {
  return artifacts.map(a => `${a.sha256}  ${a.path}`).join("\n") + "\n";
}

// ═══════════════════════════════════════════════════════
// 5. Release Notes
// ═══════════════════════════════════════════════════════

function generateReleaseNotes(meta) {
  const now = new Date();
  const dateStr = now.toISOString().split("T")[0];
  const timeStr = now.toISOString().split("T")[1].split(".")[0];

  const lines = [];
  const h = (t) => { lines.push(t); return lines; };

  h(`# ${meta.projectName} v${meta.version}`);
  h(``);
  h(`> Release Builder Agent — SF-07`);
  h(`> Generated: ${dateStr} ${timeStr} UTC`);
  h(``);

  h(`## Version`);
  h(``);
  h(`- **Name:** ${meta.projectName}`);
  h(`- **Version:** ${meta.version}`);
  h(`- **Build:** #${meta.buildNumber}`);
  h(``);

  h(`## Features`);
  h(``);
  if (meta.entities.length > 0) {
    for (const e of meta.entities) {
      h(`- ${e}`);
    }
  } else {
    h(`- (No features detected)`);
  }
  h(``);

  h(`## Build Info`);
  h(``);
  h(`- **Build Time:** ${now.toISOString()}`);
  h(`- **Generator:** release-builder-agent v${GENERATOR_VERSION}`);
  h(`- **Node Version:** ${process.version}`);
  h(``);

  h(`## Platforms`);
  h(``);
  for (const p of PLATFORMS) {
    h(`- ✅ ${p}`);
  }
  h(``);

  h(`## Installation`);
  h(``);
  h(`### Windows`);
  h(`\`\`\``);
  h(`Download the .exe installer from release/windows/`);
  h(`\`\`\``);
  h(``);
  h(`### macOS`);
  h(`\`\`\``);
  h(`Download the .dmg from release/macos/`);
  h(`\`\`\``);
  h(``);
  h(`### Linux`);
  h(`\`\`\``);
  h(`Download the .AppImage from release/linux/`);
  h(`\`\`\``);
  h(``);

  h(`## Checksums`);
  h(``);
  h(`See \`checksums/checksums.txt\` for SHA256 verification.`);
  h(``);

  h(`---`);
  h(`*Generated by Release Builder Agent — SF-07*`);

  return lines.join("\n");
}

// ═══════════════════════════════════════════════════════
// 6. Build Metadata
// ═══════════════════════════════════════════════════════

function generateBuildInfo(meta) {
  return JSON.stringify({
    nodeVersion: process.version,
    generatorVersion: GENERATOR_VERSION,
    buildTime: new Date().toISOString(),
    domain: meta.domain,
    entityCount: meta.entities.length,
    routeCount: meta.routes.length,
    screenCount: meta.screens.length,
    platforms: [...PLATFORMS],
    project: meta.projectName,
    version: meta.version,
  }, null, 2);
}

// ═══════════════════════════════════════════════════════
// 7. Platform Artifact Layout
// ═══════════════════════════════════════════════════════

function generatePlatformArtifacts(meta) {
  const safeName = meta.projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
  const artifacts = {};

  // Windows
  artifacts[`windows/${safeName}-setup-${meta.version}.exe`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} Windows Installer\n` +
    `This is a placeholder file for the Windows NSIS installer.\n` +
    `Generated: ${new Date().toISOString()}\n`;
  artifacts[`windows/${safeName}-${meta.version}-portable.exe`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} Windows Portable\n` +
    `This is a placeholder file for the Windows portable executable.\n` +
    `Generated: ${new Date().toISOString()}\n`;

  // macOS
  artifacts[`macos/${safeName}-${meta.version}.dmg`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} macOS Disk Image\n` +
    `This is a placeholder file for the macOS DMG installer.\n` +
    `Generated: ${new Date().toISOString()}\n`;
  artifacts[`macos/${safeName}-${meta.version}-arm64.dmg`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} macOS ARM64 Disk Image\n` +
    `This is a placeholder file for the macOS ARM64 DMG.\n` +
    `Generated: ${new Date().toISOString()}\n`;

  // Linux
  artifacts[`linux/${safeName}-${meta.version}.AppImage`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} Linux AppImage\n` +
    `This is a placeholder file for the Linux AppImage.\n` +
    `Generated: ${new Date().toISOString()}\n`;
  artifacts[`linux/${safeName}_${meta.version}_amd64.deb`] =
    `[PLACEHOLDER] ${meta.projectName} v${meta.version} Linux Debian Package\n` +
    `This is a placeholder file for the Linux .deb package.\n` +
    `Generated: ${new Date().toISOString()}\n`;

  return artifacts;
}

// ═══════════════════════════════════════════════════════
// 8. Main Build Function
// ═══════════════════════════════════════════════════════

/**
 * Build release package structure for an Electron project.
 *
 * @param {object} options
 * @param {string} options.projectDir — Electron project root (with apps/web, apps/api, apps/desktop)
 * @param {string} [options.projectName] — Override project name
 * @param {string} [options.version] — Override version
 * @param {number} [options.buildNumber] — Override build number
 * @returns {object} { files, stats, error, message }
 */
export function buildRelease(options = {}) {
  const {
    projectDir = null,
    projectName = null,
    version = null,
    buildNumber = 1,
  } = options;

  // Extract metadata from project
  const meta = projectDir ? extractMetadataSync(projectDir) : {
    projectName: "app",
    version: "1.0.0",
    buildNumber: 1,
    domain: "unknown",
    entities: [],
    routes: [],
    screens: [],
  };

  // Apply overrides
  if (projectName) meta.projectName = projectName;
  if (version) meta.version = version;
  meta.buildNumber = buildNumber;

  const files = {};

  // Generate platform artifacts (placeholders)
  const platformArtifacts = generatePlatformArtifacts(meta);

  // Add platform artifacts to files
  for (const [path, content] of Object.entries(platformArtifacts)) {
    files[`release/${path}`] = content;
  }

  // Compute artifact metadata for manifest
  const artifactMeta = Object.entries(platformArtifacts).map(([path, content]) => ({
    path,
    size: Buffer.byteLength(content, "utf8"),
    sha256: sha256(content),
  }));

  // Generate version.json
  files["release/version.json"] = generateVersion(meta);

  // Generate manifest.json
  files["release/manifests/manifest.json"] = generateManifest(meta, artifactMeta);

  // Generate checksums.txt
  files["release/checksums/checksums.txt"] = generateChecksums(artifactMeta);

  // Generate release-notes.md
  files["release/release-notes/release-notes.md"] = generateReleaseNotes(meta);

  // Generate build-info.json
  files["release/build-info.json"] = generateBuildInfo(meta);

  // Generate README
  files["release/README.md"] = generateReadme(meta);

  // Stats
  const allPaths = Object.keys(files);
  const stats = {
    totalFiles: allPaths.length,
    platformFiles: allPaths.filter(p => p.startsWith("release/windows/") || p.startsWith("release/macos/") || p.startsWith("release/linux/")).length,
    manifestFiles: allPaths.filter(p => p.includes("manifest")).length,
    checksumFiles: allPaths.filter(p => p.includes("checksum")).length,
    releaseNotesFiles: allPaths.filter(p => p.includes("release-notes")).length,
    metadataFiles: allPaths.filter(p => p.endsWith(".json")).length,
    totalSize: Object.values(files).reduce((s, c) => s + Buffer.byteLength(c, "utf8"), 0),
  };

  return { files, stats, meta, error: null, message: null };
}

// ═══════════════════════════════════════════════════════
// 9. README Generator
// ═══════════════════════════════════════════════════════

function generateReadme(meta) {
  const lines = [];
  const h = (t) => { lines.push(t); return lines; };

  h(`# ${meta.projectName} — Release Package`);
  h(``);
  h(`> Version: ${meta.version} | Build: #${meta.buildNumber}`);
  h(`> Generated: ${new Date().toISOString()}`);
  h(``);

  h(`## Directory Structure`);
  h(``);
  h(`\`\`\``);
  h(`release/`);
  h(`├── version.json          — Version metadata`);
  h(`├── build-info.json       — Build environment info`);
  h(`├── README.md             — This file`);
  h(`├── manifests/`);
  h(`│   └── manifest.json     — Release manifest with file hashes`);
  h(`├── checksums/`);
  h(`│   └── checksums.txt     — SHA256 checksums for verification`);
  h(`├── release-notes/`);
  h(`│   └── release-notes.md  — Human-readable release notes`);
  h(`├── windows/              — Windows installers`);
  h(`├── macos/                — macOS disk images`);
  h(`└── linux/                — Linux packages`);
  h(`\`\`\``);
  h(``);

  h(`## Verification`);
  h(``);
  h(`\`\`\`bash`);
  h(`# Verify checksums`);
  h(`cd release/checksums`);
  h(`shasum -a 256 -c checksums.txt`);
  h(`\`\`\``);
  h(``);

  h(`---`);
  h(`*Generated by Release Builder Agent — SF-07*`);

  return lines.join("\n");
}

// ═══════════════════════════════════════════════════════
// 10. File I/O
// ═══════════════════════════════════════════════════════

export function writeRelease(result, outputDir) {
  mkdirSync(outputDir, { recursive: true });
  for (const [relPath, content] of Object.entries(result.files)) {
    const fullPath = resolve(outputDir, relPath);
    mkdirSync(dirname(fullPath), { recursive: true });
    writeFileSync(fullPath, content, "utf-8");
  }
}

// ═══════════════════════════════════════════════════════
// 11. CLI Entry
// ═══════════════════════════════════════════════════════

function parseArgs() {
  const args = process.argv.slice(2);
  const opts = {
    input: null,
    output: null,
    projectName: null,
    version: null,
    buildNumber: 1,
    verbose: false,
    help: false,
  };
  for (let i = 0; i < args.length; i++) {
    if (args[i] === "--input" && args[i + 1]) opts.input = args[++i];
    else if (args[i] === "--output" && args[i + 1]) opts.output = args[++i];
    else if (args[i] === "--name" && args[i + 1]) opts.projectName = args[++i];
    else if (args[i] === "--version" && args[i + 1]) opts.version = args[++i];
    else if (args[i] === "--build-number" && args[i + 1]) opts.buildNumber = parseInt(args[++i], 10);
    else if (args[i] === "--verbose") opts.verbose = true;
    else if (args[i] === "--help" || args[i] === "-h") opts.help = true;
  }
  return opts;
}

async function main() {
  const opts = parseArgs();

  if (opts.help) {
    console.log(
      "\nRelease Builder Agent — SF-07\n\n" +
      "Generate release package structure for an Electron project.\n\n" +
      "Usage:\n" +
      "  node scripts/release-builder-agent.mjs --input <electron-dir> [options]\n\n" +
      "Options:\n" +
      "  --input <dir>         Project directory (with apps/web, apps/api, apps/desktop)\n" +
      "  --output <dir>        Output directory (default: <input>/release)\n" +
      "  --name <name>         Override project name\n" +
      "  --version <ver>       Override version\n" +
      "  --build-number <n>    Build number (default: 1)\n" +
      "  --verbose             Verbose output\n" +
      "  --help                Show this help\n"
    );
    return;
  }

  const result = buildRelease({
    projectDir: opts.input,
    projectName: opts.projectName,
    version: opts.version,
    buildNumber: opts.buildNumber,
  });

  if (result.error) {
    console.error(result.message);
    process.exit(1);
  }

  const outputDir = opts.output
    ? resolve(opts.output)
    : opts.input
    ? resolve(opts.input, "release")
    : resolve(WORKSPACE, "release");

  writeRelease(result, outputDir);

  if (opts.verbose) {
    console.error("Project: " + result.meta.projectName);
    console.error("Version: " + result.meta.version);
    console.error("Files: " + result.stats.totalFiles);
    console.error("Platform files: " + result.stats.platformFiles);
    console.error("Total size: " + result.stats.totalSize + " bytes");
  }

  console.log(JSON.stringify({
    projectName: result.meta.projectName,
    version: result.meta.version,
    outputDir,
    stats: result.stats,
  }, null, 2));
}

if (process.argv[1] && (import.meta.url === "file://" + process.argv[1] || import.meta.url.endsWith(process.argv[1]))) {
  main();
}
