#!/usr/bin/env node
"use strict";

const fs = require("fs");
const path = require("path");

const repoRoot = path.resolve(__dirname, "..");
const stdDir = path.join(repoRoot, "lib", "std");
const outputPath = path.join(repoRoot, "docs", "language", "STDLIB_API.md");
const cargoTomlPath = path.join(repoRoot, "compiler", "Cargo.toml");

function tokenize(source) {
  const tokens = [];
  const pattern = /\(|\)|[^\s()]+/g;
  let match;
  while ((match = pattern.exec(source)) !== null) {
    tokens.push(match[0]);
  }
  return tokens;
}

function parseList(tokens, cursor) {
  if (tokens[cursor.index] !== "(") {
    throw new Error(`expected list at token ${cursor.index}`);
  }
  cursor.index += 1;
  const list = [];
  while (cursor.index < tokens.length && tokens[cursor.index] !== ")") {
    if (tokens[cursor.index] === "(") {
      list.push(parseList(tokens, cursor));
    } else {
      list.push(tokens[cursor.index]);
      cursor.index += 1;
    }
  }
  if (tokens[cursor.index] !== ")") {
    throw new Error("unterminated list");
  }
  cursor.index += 1;
  return list;
}

function moduleForm(source, file) {
  const tokens = tokenize(source);
  if (tokens[0] !== "(") {
    throw new Error(`${file}: expected module form`);
  }
  const form = parseList(tokens, { index: 0 });
  if (form[0] !== "module" || typeof form[1] !== "string") {
    throw new Error(`${file}: expected (module name ...) form`);
  }
  const exportForm = form.find((item) => Array.isArray(item) && item[0] === "export");
  if (!exportForm) {
    throw new Error(`${file}: module must have an explicit export list`);
  }
  return {
    name: form[1],
    exports: exportForm.slice(1),
  };
}

function stdModules() {
  return fs
    .readdirSync(stdDir)
    .filter((name) => name.endsWith(".slo"))
    .sort()
    .map((fileName) => {
      const relativePath = path.join("lib", "std", fileName);
      const source = fs.readFileSync(path.join(stdDir, fileName), "utf8");
      const module = moduleForm(source, relativePath);
      return {
        fileName,
        relativePath,
        name: module.name,
        exports: module.exports,
      };
    });
}

function packageVersion() {
  const cargoToml = fs.readFileSync(cargoTomlPath, "utf8");
  const versionMatch = cargoToml.match(/^version\s*=\s*"([^"]+)"$/m);
  if (!versionMatch) {
    throw new Error("compiler/Cargo.toml must declare a package version");
  }
  return versionMatch[1];
}

function render(modules, version) {
  const totalExports = modules.reduce((count, module) => count + module.exports.length, 0);
  const out = [];
  out.push("# Slovo Standard Library API Catalog");
  out.push("");
  out.push("Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.sh`.");
  out.push("Do not edit this file by hand.");
  out.push("");
  out.push("## Stability Tiers");
  out.push("");
  out.push("- `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line.");
  out.push(`- \`experimental\`: not used for exported \`lib/std\` helpers in \`${version}\`; future releases may mark new helpers this way before they graduate.`);
  out.push("- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog.");
  out.push("");
  out.push("The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze.");
  out.push("");
  out.push("## Summary");
  out.push("");
  out.push(`- Modules: ${modules.length}`);
  out.push(`- Exported helpers: ${totalExports}`);
  out.push("- Default tier: `beta-supported`");
  out.push("");
  out.push("## Modules");
  out.push("");
  for (const module of modules) {
    out.push(`### std.${module.name}`);
    out.push("");
    out.push(`- Path: \`${module.relativePath}\``);
    out.push("- Tier: `beta-supported`");
    out.push(`- Exported helpers: ${module.exports.length}`);
    out.push("");
    for (const exported of module.exports) {
      out.push(`- \`${exported}\``);
    }
    out.push("");
  }
  return `${out.join("\n")}\n`;
}

fs.writeFileSync(outputPath, render(stdModules(), packageVersion()));
