#!/usr/bin/env node
/**
 * Lockfile license gate for every installed package, including workspaces.
 *
 * The allowed set is intentionally small and explicit. A new license is a
 * review event, not a string this script tries to interpret optimistically.
 * This does not replace third-party notices; it prevents a dependency update
 * from quietly introducing terms outside Tera's permissive distribution model.
 */

import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const lock = JSON.parse(readFileSync(resolve(root, "package-lock.json"), "utf8"));
const allowed = new Set(["Apache-2.0", "BSD-3-Clause", "ISC", "MIT"]);
const problems = [];
const counts = new Map();

for (const [path, entry] of Object.entries(lock.packages ?? {})) {
  if (path === "") continue;

  let license = entry.license;
  if (!license && entry.link === true && typeof entry.resolved === "string") {
    try {
      const workspace = JSON.parse(
        readFileSync(resolve(root, entry.resolved, "package.json"), "utf8"),
      );
      license = workspace.license;
    } catch (error) {
      problems.push(`${path}: could not read linked workspace license (${String(error)})`);
      continue;
    }
  }

  if (typeof license !== "string" || license.length === 0) {
    problems.push(`${path}: no license recorded in the lockfile or linked workspace`);
    continue;
  }
  if (!allowed.has(license)) {
    problems.push(`${path}: ${license} is not in the reviewed allowlist`);
    continue;
  }
  counts.set(license, (counts.get(license) ?? 0) + 1);

  const resolved = entry.resolved;
  if (typeof resolved === "string" && /^(?:git\+|github:|gitlab:|bitbucket:)/i.test(resolved)) {
    problems.push(`${path}: source dependency ${resolved} is not a registry artifact`);
  }
  if (path.startsWith("node_modules/") && entry.link !== true) {
    if (typeof resolved !== "string" || !/^https:\/\/registry\.npmjs\.org\//.test(resolved)) {
      problems.push(`${path}: dependency is not pinned to the npm registry in package-lock.json`);
    }
    if (typeof entry.integrity !== "string" || !/^sha(?:256|384|512)-/.test(entry.integrity)) {
      problems.push(`${path}: dependency has no recognized lockfile integrity hash`);
    }
  }
}

if (problems.length > 0) {
  console.error("\ncheck-dependency-licenses: FAIL\n");
  for (const problem of problems) console.error(`  ${problem}`);
  console.error(`\nReviewed allowlist: ${[...allowed].join(", ")}\n`);
  process.exit(1);
}

const total = [...counts.values()].reduce((sum, count) => sum + count, 0);
console.log(`check-dependency-licenses: ok — ${total} locked packages reviewed.`);
for (const license of [...counts.keys()].sort()) {
  console.log(`  ${license}: ${counts.get(license)}`);
}
