import { execFileSync, spawnSync } from "node:child_process";

function runJson(args) {
  return JSON.parse(execFileSync("kubectl", args, { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 }));
}

function duBytesByPath(paths) {
  const allowed = [...new Set(paths.filter((path) => path?.startsWith("/var/lib/rancher/k3s/storage/")))];
  if (allowed.length === 0) {
    return { sizes: new Map(), summary: { requested: 0, measured: 0, timedOut: false, status: 0 } };
  }
  const result = spawnSync("du", ["-sb", ...allowed], {
    encoding: "utf8",
    timeout: 15000,
    maxBuffer: 8 * 1024 * 1024,
  });
  const sizes = new Map();
  for (const line of result.stdout?.split(/\r?\n/u) || []) {
    const match = line.match(/^(\d+)\s+(.+)$/u);
    if (match) sizes.set(match[2], Number(match[1]));
  }
  return {
    sizes,
    summary: {
      requested: allowed.length,
      measured: sizes.size,
      timedOut: result.error?.code === "ETIMEDOUT",
      status: result.status,
    },
  };
}

const namespace = process.env.NAMESPACE;
const confirm = process.env.CONFIRM === "true";
const enabled = process.env.ENABLED === "true";
const limit = Math.max(1, Math.min(Number(process.env.LIMIT || "100"), 1000));
const prefixes = JSON.parse(Buffer.from(process.env.PREFIXES_JSON_B64 || "W10=", "base64").toString("utf8"));

if (!enabled) {
  console.log(JSON.stringify({ ok: false, error: "session-pvc-retention-disabled", selectedPvcCount: 0, mutation: false }));
  process.exit(0);
}
if (!namespace || !Array.isArray(prefixes) || prefixes.length === 0) throw new Error("session PVC cleanup requires namespace and YAML prefixes");

const pvData = runJson(["get", "pv", "-o", "json"]);
const pvcData = runJson(["-n", namespace, "get", "pvc", "-o", "json"]);
const podData = runJson(["-n", namespace, "get", "pod", "-o", "json"]);
const pvs = new Map((pvData.items || []).map((pv) => [pv.metadata?.name, pv]));
const activeClaims = new Map();
for (const pod of podData.items || []) {
  const phase = pod.status?.phase;
  if (phase === "Succeeded" || phase === "Failed") continue;
  for (const volume of pod.spec?.volumes || []) {
    const claim = volume.persistentVolumeClaim?.claimName;
    if (!claim) continue;
    const list = activeClaims.get(claim) || [];
    list.push(pod.metadata?.name);
    activeClaims.set(claim, list);
  }
}

const candidateFacts = [];
const protectedFacts = [];
for (const pvc of pvcData.items || []) {
  const name = pvc.metadata?.name || "";
  const matchedPrefix = prefixes.find((prefix) => name.startsWith(prefix));
  if (!matchedPrefix) continue;
  const activeMountPods = activeClaims.get(name) || [];
  const pv = pvs.get(pvc.spec?.volumeName);
  const storageClass = pvc.spec?.storageClassName || pv?.spec?.storageClassName || null;
  const reclaimPolicy = pv?.spec?.persistentVolumeReclaimPolicy || null;
  const hostPath = pv?.spec?.hostPath?.path || pv?.spec?.local?.path || null;
  const row = {
    namespace,
    pvc: name,
    volume: pvc.spec?.volumeName || null,
    matchedPrefix,
    phase: pvc.status?.phase || null,
    pvPhase: pv?.status?.phase || null,
    storageClass,
    reclaimPolicy,
    activeMountCount: activeMountPods.length,
    activeMountPods: activeMountPods.slice(0, 5),
    hostPath,
  };
  if (activeMountPods.length > 0 || storageClass !== "local-path" || reclaimPolicy !== "Delete") {
    protectedFacts.push({ ...row, reason: activeMountPods.length > 0 ? "active-mount" : "not-local-path-delete" });
  } else {
    candidateFacts.push(row);
  }
}

const selectsAllCandidates = candidateFacts.length <= limit;
const sizeScan = confirm && selectsAllCandidates
  ? {
      sizes: new Map(),
      summary: {
        requested: candidateFacts.length,
        measured: 0,
        timedOut: false,
        status: null,
        skipped: true,
        reason: "confirmed-run-selects-all-candidates",
      },
    }
  : duBytesByPath(candidateFacts.map((item) => item.hostPath));
const candidates = candidateFacts.map(({ hostPath, ...item }) => ({
  ...item,
  estimatedBytes: sizeScan.sizes.get(hostPath) ?? null,
}));
const protectedRows = protectedFacts.map(({ hostPath, ...item }) => ({
  ...item,
  estimatedBytes: sizeScan.sizes.get(hostPath) ?? null,
}));
candidates.sort((a, b) => (b.estimatedBytes || 0) - (a.estimatedBytes || 0));
const selected = candidates.slice(0, limit);
const result = {
  ok: true,
  planKind: "agentrun-session-pvc-retention",
  namespace,
  dryRun: !confirm,
  mutation: confirm,
  criteria: { prefixes, storageClass: "local-path", reclaimPolicy: "Delete", requireNoActiveMount: true, limit },
  candidatePvcCount: candidates.length,
  selectedPvcCount: selected.length,
  protectedPvcCount: protectedRows.length,
  sizeScan: sizeScan.summary,
  estimatedReclaimBytes: selected.reduce((sum, item) => sum + (item.estimatedBytes || 0), 0),
  selectedPreview: selected.slice(0, 5),
  protectedPreview: protectedRows.slice(0, 5),
  deletedPvcCount: 0,
  valuesPrinted: false,
};

if (confirm && selected.length > 0) {
  for (let index = 0; index < selected.length; index += 50) {
    execFileSync("kubectl", ["-n", namespace, "delete", "pvc", "--wait=false", ...selected.slice(index, index + 50).map((item) => item.pvc)], { encoding: "utf8", maxBuffer: 1024 * 1024 });
  }
  result.deletedPvcCount = selected.length;
  result.deleteMode = "submit-only-wait-false";
}

console.log(JSON.stringify(result));
