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

function readJSON(p) {
  return JSON.parse(fs.readFileSync(p, "utf8"));
}

function getInstalledVersion(pkgName) {
  try {
    const parts = pkgName.split("/");
    const pkgPath = path.join("node_modules", ...parts, "package.json");
    const meta = readJSON(pkgPath);
    return meta.version;
  } catch (e) {
    return null;
  }
}

function collect(sectionName, obj) {
  if (!obj) return [];
  return Object.entries(obj).map(([name, declared]) => {
    const installed = getInstalledVersion(name);
    return {
      section: sectionName,
      name,
      from: declared,
      to: installed || null,
      strategy: "installed",
    };
  });
}

function main() {
  // If node_modules is missing, skip to avoid generating noisy diffs
  if (!fs.existsSync("node_modules")) {
    console.log("Skip: node_modules not found. Run npm install first.");
    return;
  }

  const pkg = readJSON("package.json");
  const entries = [
    ...collect("dependencies", pkg.dependencies),
    ...collect("devDependencies", pkg.devDependencies),
  ];

  // Stable sort by section then name
  entries.sort((a, b) =>
    a.section === b.section
      ? a.name.localeCompare(b.name)
      : a.section.localeCompare(b.section),
  );

  const outPath = path.join(process.cwd(), "dependency-log.json");
  // Merge with previous to preserve generatedAt when unchanged
  let prevMap = new Map();
  if (fs.existsSync(outPath)) {
    try {
      const prev = JSON.parse(fs.readFileSync(outPath, "utf8"));
      for (const e of prev) {
        prevMap.set(`${e.section}::${e.name}`, e);
      }
    } catch {}
  }

  const now = new Date().toISOString();
  const merged = entries.map((e) => {
    const key = `${e.section}::${e.name}`;
    const prev = prevMap.get(key);
    if (!prev) {
      // New entry: keep declared as from
      return { ...e, generatedAt: now };
    }

    // If installed version changed, set from to previous installed version
    if (prev.to !== e.to) {
      return { ...e, from: prev.to, generatedAt: now };
    }

    // Otherwise preserve previous 'from' and timestamp
    return { ...e, from: prev.from, generatedAt: prev.generatedAt || now };
  });

  const nextContent = JSON.stringify(merged, null, 2) + "\n";
  if (fs.existsSync(outPath)) {
    try {
      const prevContent = fs.readFileSync(outPath, "utf8");
      if (prevContent === nextContent) {
        console.log(`No changes for ${outPath} (entries: ${entries.length}).`);
        return;
      }
    } catch {}
  }
  fs.writeFileSync(outPath, nextContent);
  console.log(`Updated ${outPath} with ${entries.length} entries.`);
}

main();
