import { copyFile, mkdir, readFile, readdir, writeFile, access } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

const project = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const manifest = JSON.parse(await readFile(path.join(project, "package.json"), "utf8"));
const destination = path.join(project, "THIRD_PARTY_LICENSES");
await mkdir(destination, { recursive: true });
const entries = new Map();

async function packageRoot(name, from = project) {
  let current = from;
  while (true) {
    const candidate = path.join(current, "node_modules", name);
    try { await access(path.join(candidate, "package.json")); return candidate; } catch {}
    const parent = path.dirname(current);
    if (parent === current) throw new Error(`Installed dependency missing: ${name}`);
    current = parent;
  }
}

async function visit(name, from, kind) {
  const root = await packageRoot(name, from);
  if (entries.has(root)) return;
  const pkg = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
  entries.set(root, { root, pkg, kind });
  // Next's server/native toolchain is not shipped in the static browser or SEA
  // website. Retain its direct license; traverse every client dependency.
  if (name === "next") return;
  for (const child of Object.keys(pkg.dependencies ?? {})) await visit(child, root, "transitive client");
}
for (const name of Object.keys(manifest.dependencies ?? {})) await visit(name, project, "direct");

const fallbackFiles = {
  "@radix-ui/react-compose-refs": "node_modules/radix-ui/LICENSE",
  "react-remove-scroll-bar": "vendor/react-remove-scroll-bar.LICENSE",
};
const rows = [];
const texts = [];
for (const { root, pkg, kind } of [...entries.values()].sort((a, b) => a.pkg.name.localeCompare(b.pkg.name))) {
  const names = (await readdir(root)).filter((name) => /^(license|licence|copying|notice)(\.(md|txt))?$/i.test(name));
  const files = names.map((name) => path.join(root, name));
  if (!files.length && fallbackFiles[pkg.name]) files.push(path.join(project, fallbackFiles[pkg.name]));
  if (!files.length) throw new Error(`No license text found for ${pkg.name}`);
  const copied = [];
  for (let i = 0; i < files.length; i++) {
    const outputName = `${pkg.name.replaceAll("/", "__")}--${pkg.version}${i ? `--${i + 1}` : ""}.txt`;
    await copyFile(files[i], path.join(destination, outputName));
    copied.push(outputName);
    texts.push(`${pkg.name} ${pkg.version} — ${path.basename(files[i])}\n\n${(await readFile(files[i], "utf8")).trim()}`);
  }
  rows.push(`| ${kind} | ${pkg.name} | ${pkg.version} | ${pkg.license ?? "See text"} | ${copied.join(", ")} |`);
}
const inventory = ["# Runtime dependency license inventory", "", "Exact direct dependencies and transitive client dependencies used by the static calculator. Next's installed server/native dependencies are not distributed in the website or Windows application.", "", "| Scope | Package | Version | Declared license | Included text |", "| --- | --- | ---: | --- | --- |", ...rows, ""].join("\n");
await writeFile(path.join(destination, "INDEX.md"), inventory);
const stylesheet = await readFile(path.join(project, "vendor/shadcn-tailwind-4.13.0.LICENSE.md"), "utf8");
await writeFile(path.join(destination, "ALL.txt"), `${inventory}\n${texts.join("\n\n--------------------\n\n")}\n\nVendored stylesheet\n\n${stylesheet}`);
console.log(`Collected notices for ${rows.length} direct and transitive client packages.`);
