#!/usr/bin/env node

import { access, readFile, stat } from "node:fs/promises";
import { dirname, isAbsolute, join, resolve } from "node:path";

const manifestPath = resolve(process.argv[2] || "");
if (!process.argv[2]) {
  console.error("Usage: npm run audit:engine-cache -- /absolute/path/to/cesium/manifest.json");
  process.exitCode = 1;
} else {
  await audit(manifestPath);
}

async function audit(path) {
  let manifest;
  try {
    manifest = JSON.parse(await readFile(path, "utf8"));
  } catch (error) {
    throw new Error(`Cannot read legacy cache manifest: ${error instanceof Error ? error.message : "unknown error"}`);
  }

  const root = dirname(path);
  const entries = Object.entries(manifest?.entries || {});
  const providers = new Map();
  let bytesFromManifest = 0;
  let presentFiles = 0;
  let missingFiles = 0;
  let bytesOnDisk = 0;

  for (const [rawUrl, entry] of entries) {
    let hostname = "invalid-url";
    try { hostname = new URL(rawUrl).hostname.toLowerCase() || "invalid-url"; } catch {}
    const provider = providers.get(hostname) || { entries: 0, bytes: 0, presentFiles: 0, missingFiles: 0 };
    const entryBytes = Math.max(0, Number(entry?.size || 0));
    provider.entries += 1;
    provider.bytes += entryBytes;
    bytesFromManifest += entryBytes;

    const relativeFile = String(entry?.path || "");
    const candidate = resolve(root, relativeFile);
    if (!relativeFile || !candidate.startsWith(`${root}/`) || isAbsolute(relativeFile) || !(await fileExists(candidate))) {
      provider.missingFiles += 1;
      missingFiles += 1;
      providers.set(hostname, provider);
      continue;
    }

    const fileInfo = await stat(candidate);
    presentFiles += 1;
    provider.presentFiles += 1;
    bytesOnDisk += fileInfo.size;
    providers.set(hostname, provider);
  }

  const report = {
    kind: "nodedc-legacy-cesium-cache-audit",
    generatedAt: new Date().toISOString(),
    manifestPath: path,
    manifestVersion: manifest?.version ?? null,
    entries: entries.length,
    declaredBytes: bytesFromManifest,
    declaredMegabytes: toMegabytes(bytesFromManifest),
    presentFiles,
    missingFiles,
    bytesOnDisk,
    megabytesOnDisk: toMegabytes(bytesOnDisk),
    providers: [...providers.entries()]
      .map(([hostname, value]) => ({ hostname, ...value, megabytes: toMegabytes(value.bytes) }))
      .sort((left, right) => right.bytes - left.bytes),
    migrationPolicy: {
      automaticImport: false,
      reason: "Audit only. Import requires an explicit licence/provider decision for every source host and must preserve required attribution.",
      target: "Map Gateway persistent volume or approved object storage; never Git history or a Docker image layer."
    }
  };

  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
}

async function fileExists(path) {
  try {
    await access(path);
    return true;
  } catch {
    return false;
  }
}

function toMegabytes(bytes) {
  return Number((bytes / 1024 / 1024).toFixed(2));
}
