/**
 * Signed VPS directory list (ML-DSA-87, same release key as release_manifest).
 * Clients bootstrap from hardcoded seeds, then fetch / verify / cache this list.
 */
const fs = require("fs");
const path = require("path");
const releaseStore = require("./release_store");

const KIND = "vps-directory";
const MAX_URLS = 32;

function directoryFilePath() {
  const candidates = [
    process.env.SARMAD_DATA_DIR
      ? path.join(String(process.env.SARMAD_DATA_DIR), "vps_directory.json")
      : null,
    path.join(__dirname, "..", "vps_scheduler", "vps_directory.json"),
    path.join(__dirname, "vps_directory.json"),
  ].filter(Boolean);
  for (const p of candidates) {
    if (fs.existsSync(p)) return p;
  }
  return candidates[0];
}

const DIRECTORY_FILE = directoryFilePath();

/** @type {object|null} */
let cached = null;

function normalizeUrl(raw) {
  const u = String(raw || "")
    .trim()
    .replace(/\/$/, "");
  if (!u) return "";
  if (!/^https?:\/\//i.test(u)) return "";
  try {
    const parsed = new URL(u);
    if (parsed.username || parsed.password) return "";
    if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return "";
    return u;
  } catch {
    return "";
  }
}

function sanitizeUrls(list) {
  const out = [];
  const seen = new Set();
  for (const item of list || []) {
    const u = normalizeUrl(item);
    if (!u || seen.has(u)) continue;
    seen.add(u);
    out.push(u);
    if (out.length >= MAX_URLS) break;
  }
  return out;
}

function readFile() {
  try {
    const file = directoryFilePath();
    if (!fs.existsSync(file)) return null;
    return JSON.parse(fs.readFileSync(file, "utf8"));
  } catch {
    return null;
  }
}

function writeFile(doc) {
  const file = directoryFilePath();
  fs.mkdirSync(path.dirname(file), { recursive: true });
  fs.writeFileSync(file, JSON.stringify(doc, null, 2));
  // Keep repo/tools copy in sync when developing from source tree.
  const repoCopy = path.join(__dirname, "..", "vps_scheduler", "vps_directory.json");
  if (path.resolve(file) !== path.resolve(repoCopy)) {
    try {
      fs.mkdirSync(path.dirname(repoCopy), { recursive: true });
      fs.writeFileSync(repoCopy, JSON.stringify(doc, null, 2));
    } catch (_) {}
  }
  cached = doc;
}

async function verifyDirectory(doc, pubHexOverride) {
  if (!doc || typeof doc !== "object") return { ok: false, error: "missing directory" };
  if (String(doc.kind || "") !== KIND) return { ok: false, error: "bad directory kind" };
  const urls = sanitizeUrls(doc.urls);
  if (!urls.length) return { ok: false, error: "directory urls empty" };
  const sig = doc.signature;
  const body = { ...doc };
  delete body.signature;
  const check = await releaseStore.verifyPayload(body, sig, pubHexOverride);
  if (!check.ok) return check;
  return { ok: true, urls, publishedAt: Number(doc.publishedAt || 0), doc: body };
}

async function buildAndSignDirectory(urls) {
  const clean = sanitizeUrls(urls);
  if (!clean.length) throw new Error("at least one directory url required");
  const body = {
    kind: KIND,
    publishedAt: Date.now(),
    urls: clean,
  };
  const signature = await releaseStore.signPayload(body);
  const doc = { ...body, signature };
  writeFile(doc);
  return doc;
}

async function getDirectory() {
  if (cached) return cached;
  cached = readFile();
  return cached;
}

/**
 * Ensure a signed directory exists. Re-signs when the URL set changes or file missing.
 * Requires release private key on the VPS.
 */
async function ensureSigned(urls) {
  const clean = sanitizeUrls(urls);
  if (!clean.length) return null;
  const existing = await getDirectory();
  if (existing) {
    const verified = await verifyDirectory(existing);
    if (verified.ok) {
      const same =
        verified.urls.length === clean.length &&
        verified.urls.every((u, i) => u === clean[i]);
      if (same) return existing;
    }
  }
  try {
    return await buildAndSignDirectory(clean);
  } catch (e) {
    console.warn(
      "[directory-store] could not sign VPS directory:",
      e && e.message ? e.message : e
    );
    return existing;
  }
}

function clearCache() {
  cached = null;
}

module.exports = {
  KIND,
  MAX_URLS,
  get DIRECTORY_FILE() {
    return directoryFilePath();
  },
  sanitizeUrls,
  normalizeUrl,
  verifyDirectory,
  buildAndSignDirectory,
  getDirectory,
  ensureSigned,
  clearCache,
};
