// Persistent per-install identifier. Generated once on first boot and
// stashed at `<DATA_DIR>/install-id` (typically /data/install-id on
// StartOS). Survives container restarts and Recap upgrades; lost on a
// full uninstall + reinstall.
//
// What it's for: the upcoming relay backend will use this ID as the
// owner of comped/paid relay credits. Without a stable client identity
// the relay can't tell whether a request belongs to a credited install
// or a fresh one. Direct install-ID auth is the v1 choice (see the
// project roadmap discussion) — simple, sufficient for low-count free
// credits, can be hardened later with license-server-minted JWTs.
//
// What it is NOT: a license key. The license system (./license.js) is
// completely separate — license keys are user-facing strings that
// authorize Pro features, while install-IDs are opaque per-install
// UUIDs the relay backend uses for credit accounting.

import fs from "fs/promises";
import path from "path";
import { randomUUID } from "crypto";

let cachedId = null;

// Initialize on boot. Reads the existing ID off disk; if there's no
// file, generates a fresh UUIDv4 and writes it. Subsequent calls to
// getInstallId() return the cached value without touching disk.
//
// `dataDir` must be writable — on StartOS that's /data (the persistent
// volume), on local dev it's the project root.
export async function initInstallId({ dataDir }) {
  if (!dataDir) throw new Error("initInstallId: dataDir is required");
  const filePath = path.join(dataDir, "install-id");
  try {
    const raw = await fs.readFile(filePath, "utf8");
    const trimmed = raw.trim();
    if (isValidInstallId(trimmed)) {
      cachedId = trimmed;
      console.log(`[install-id] loaded ${redact(cachedId)} from ${filePath}`);
      return cachedId;
    }
    console.warn(
      `[install-id] file at ${filePath} contained an invalid value — regenerating`
    );
  } catch (err) {
    if (err.code !== "ENOENT") {
      console.warn(`[install-id] read failed (${err.code}): ${err.message}`);
    }
  }
  // No valid file — mint a new one. UUIDv4 is plenty: 122 bits of
  // randomness, no collision risk across realistic install counts, and
  // it's opaque enough to share over the wire without leaking system
  // info (unlike e.g. a machine-id).
  const fresh = randomUUID();
  await fs.writeFile(filePath, fresh + "\n", { mode: 0o600 });
  cachedId = fresh;
  console.log(`[install-id] generated ${redact(cachedId)} → ${filePath}`);
  return cachedId;
}

export function getInstallId() {
  return cachedId;
}

// Loose UUID shape check — accepts any reasonable UUID-ish string.
// Avoids requiring v4 specifically in case operators want to seed
// non-standard IDs.
function isValidInstallId(s) {
  return typeof s === "string" && /^[0-9a-f-]{32,40}$/i.test(s);
}

// Log-safe display: first 8 + last 4 chars only.
function redact(id) {
  if (!id || id.length < 12) return "(short)";
  return `${id.slice(0, 8)}…${id.slice(-4)}`;
}
