/**
 * Admin session — a signed, HttpOnly cookie carrying the logged-in tenant id.
 * Credentials themselves are checked in tenants.authenticate() (bcrypt).
 */
import crypto from "node:crypto";

const SECRET =
  process.env.SECRET_KEY ||
  crypto.createHash("sha256").update("chat-widget-saas::admin").digest("hex");
const TTL = parseInt(process.env.SESSION_TTL || "28800", 10) * 1000;
const COOKIE_SECURE = (process.env.COOKIE_SECURE || "false").toLowerCase() === "true";
const COOKIE = "cw_admin";

function sign(payload) {
  return crypto.createHmac("sha256", SECRET).update(payload).digest("hex");
}

function setCookie(res, payloadObj) {
  const payload = Buffer.from(JSON.stringify(payloadObj)).toString("base64url");
  res.cookie(COOKIE, payload + "." + sign(payload), {
    httpOnly: true, secure: COOKIE_SECURE, sameSite: "lax", maxAge: TTL,
  });
}

export function issueSession(res, tenantId) {
  setCookie(res, { t: tenantId, exp: Date.now() + TTL });
}

/** Platform-owner session (manages all tenants). */
export function issueSuperSession(res) {
  setCookie(res, { sa: 1, exp: Date.now() + TTL });
}

function readSession(req) {
  const token = req.cookies?.[COOKIE];
  if (!token || !token.includes(".")) return null;
  const [payload, mac] = token.split(".");
  const expected = sign(payload);
  if (!mac || mac.length !== expected.length) return null;
  if (!crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(expected))) return null;
  try {
    const d = JSON.parse(Buffer.from(payload, "base64url").toString());
    if (!d.exp || Date.now() > d.exp) return null;
    return d;
  } catch { return null; }
}

export function sessionIsSuper(req) {
  return readSession(req)?.sa === 1;
}

export function requireSuper(req, res, next) {
  if (!sessionIsSuper(req)) return res.status(401).json({ error: "Not authenticated" });
  next();
}

// ── Password-reset tokens (stateless, single-use) ──────────────────────────
// Embeds a prefix of the current password hash, so once the password changes
// the token stops working — no DB table needed.
export function issueResetToken(tenantId, passwordHash) {
  const payload = Buffer.from(JSON.stringify({ tid: tenantId, k: passwordHash.slice(0, 12), exp: Date.now() + 60 * 60 * 1000 })).toString("base64url");
  return payload + "." + sign(payload);
}

export function verifyResetToken(token) {
  if (typeof token !== "string" || !token.includes(".")) return null;
  const [payload, mac] = token.split(".");
  const expected = sign(payload);
  if (!mac || mac.length !== expected.length) return null;
  if (!crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(expected))) return null;
  try {
    const d = JSON.parse(Buffer.from(payload, "base64url").toString());
    if (!d.exp || Date.now() > d.exp) return null;
    return { tenantId: d.tid, hashPrefix: d.k };
  } catch { return null; }
}

export function clearSession(res) {
  res.clearCookie(COOKIE);
}

/** Returns the tenant id from a valid session cookie, or null. */
export function sessionTenantId(req) {
  return readSession(req)?.t || null;
}

/** Express middleware: 401 unless a valid session; sets req.tenantId. */
export function requireAuth(req, res, next) {
  const t = sessionTenantId(req);
  if (!t) return res.status(401).json({ error: "Not authenticated" });
  req.tenantId = t;
  next();
}
