const crypto = require("crypto");

const DELAY_MS = 3000;
const EXPIRES_MS = 30000;
const MAX_PENDING = 20;
const DESTRUCTIVE_PATH = /(?:^|\/)(?:delete|remove|clear|reset|renew|uninstall|cleanup|archive|revoke|unlink|unset|revert)(?:\/|$)/i;

function isDestructivePath(value) {
  return DESTRUCTIVE_PATH.test(normalizeAction(value));
}

function issueConfirmation(req, action) {
  const normalizedAction = normalizeAction(action);
  if (!req.session?.user?.id) throw new Error("Authentication is required.");
  if (!isDestructivePath(normalizedAction)) throw new Error("This action is not registered as destructive.");

  const now = Date.now();
  prune(req.session, now);
  const token = crypto.randomBytes(32).toString("base64url");
  const entries = req.session.destructive_confirmations || {};
  entries[token] = {
    action: normalizedAction,
    user_id: req.session.user.id,
    not_before: now + DELAY_MS,
    expires_at: now + EXPIRES_MS
  };
  const keys = Object.keys(entries);
  for (const key of keys.slice(0, Math.max(0, keys.length - MAX_PENDING))) delete entries[key];
  req.session.destructive_confirmations = entries;
  return {
    token,
    not_before: now + DELAY_MS,
    expires_at: now + EXPIRES_MS,
    delay_seconds: DELAY_MS / 1000
  };
}

function consumeConfirmation(req, action, token = req.body?.confirmation_token || req.get?.("x-confirmation-token")) {
  const normalizedAction = normalizeAction(action);
  const entries = req.session?.destructive_confirmations || {};
  const entry = token ? entries[token] : null;
  if (!entry) return { valid: false, reason: "missing_or_invalid" };
  delete entries[token];
  req.session.destructive_confirmations = entries;
  const now = Date.now();
  if (entry.user_id !== req.session.user?.id || entry.action !== normalizedAction) {
    return { valid: false, reason: "action_mismatch" };
  }
  if (now < entry.not_before) return { valid: false, reason: "too_early" };
  if (now > entry.expires_at) return { valid: false, reason: "expired" };
  return { valid: true };
}

function normalizeAction(value) {
  const raw = String(value || "").trim();
  if (!raw.startsWith("/")) return "";
  try {
    return new URL(raw, "http://lumi.local").pathname;
  } catch {
    return "";
  }
}

function prune(session, now = Date.now()) {
  const entries = session.destructive_confirmations || {};
  for (const [token, entry] of Object.entries(entries)) {
    if (!entry || entry.expires_at < now) delete entries[token];
  }
  session.destructive_confirmations = entries;
}

module.exports = {
  DELAY_MS,
  EXPIRES_MS,
  isDestructivePath,
  issueConfirmation,
  consumeConfirmation,
  normalizeAction
};
