/**
 * Email verification for the chat gate + the signed chat token. Both are
 * tenant-scoped: OTPs remember which tenant they belong to, and the chat token
 * encodes the tenant, conversation, and email.
 */
import crypto from "node:crypto";

const SECRET =
  process.env.SECRET_KEY ||
  crypto.createHash("sha256").update("chat-widget-saas::verify").digest("hex");

const OTP_TTL = 10 * 60 * 1000;
const TOKEN_TTL = 7 * 24 * 60 * 60 * 1000;
const MAX_ATTEMPTS = 6;

const store = new Map(); // sessionId -> { tenantId, code, name, email, expires, attempts }

export function isValidEmail(email) {
  return typeof email === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email.trim());
}

export function createOtp(tenantId, name, email) {
  const sessionId = crypto.randomBytes(16).toString("hex");
  const code = String(crypto.randomInt(0, 1000000)).padStart(6, "0");
  store.set(sessionId, {
    tenantId,
    code,
    name: String(name).trim().slice(0, 120),
    email: String(email).trim().slice(0, 180),
    expires: Date.now() + OTP_TTL,
    attempts: 0,
  });
  pruneExpired();
  return { sessionId, code };
}

export function checkOtp(sessionId, code) {
  const e = store.get(sessionId);
  if (!e) return { ok: false, reason: "Code expired — please restart." };
  if (Date.now() > e.expires) { store.delete(sessionId); return { ok: false, reason: "Code expired — please restart." }; }
  if (++e.attempts > MAX_ATTEMPTS) { store.delete(sessionId); return { ok: false, reason: "Too many attempts — please restart." }; }
  if (e.code !== String(code).trim()) return { ok: false, reason: "Incorrect code. Try again." };
  store.delete(sessionId);
  return { ok: true, tenantId: e.tenantId, name: e.name, email: e.email };
}

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

export function issueChatToken({ tenantKey, conversationId, email }) {
  const payload = Buffer.from(
    JSON.stringify({ tk: tenantKey, c: conversationId, e: email, exp: Date.now() + TOKEN_TTL })
  ).toString("base64url");
  return payload + "." + sign(payload);
}

export function verifyChatToken(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 { tenantKey: d.tk, conversationId: d.c, email: d.e };
  } catch {
    return null;
  }
}

function pruneExpired() {
  const now = Date.now();
  for (const [k, v] of store) if (now > v.expires) store.delete(k);
}
