const crypto = require("crypto");
const { getSetting } = require("./settings");

function encryptionKey() {
  const secret = getSetting("session_secret");
  if (!secret) {
    throw new Error("Lumi's session secret is not initialized.");
  }
  return crypto.createHash("sha256").update(`lumi-overlay:${secret}`).digest();
}

function generateToken() {
  return crypto.randomBytes(32).toString("base64url");
}

function tokenHash(token) {
  return crypto.createHash("sha256").update(String(token || "")).digest("hex");
}

function scopedSignature(scope, value) {
  return crypto.createHmac("sha256", encryptionKey())
    .update(`${String(scope || "")}:${String(value || "")}`)
    .digest("base64url");
}

function verifyScopedSignature(scope, value, signature) {
  const expected = Buffer.from(scopedSignature(scope, value));
  const received = Buffer.from(String(signature || ""));
  return expected.length === received.length && crypto.timingSafeEqual(expected, received);
}

function encryptSecret(value) {
  if (value === null || value === undefined || value === "") return null;
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv("aes-256-gcm", encryptionKey(), iv);
  const encrypted = Buffer.concat([cipher.update(String(value), "utf8"), cipher.final()]);
  const tag = cipher.getAuthTag();
  return `v1.${iv.toString("base64url")}.${tag.toString("base64url")}.${encrypted.toString("base64url")}`;
}

function decryptSecret(value) {
  if (!value) return "";
  const [version, ivValue, tagValue, encryptedValue] = String(value).split(".");
  if (version !== "v1" || !ivValue || !tagValue || !encryptedValue) {
    throw new Error("Stored secret has an unsupported format.");
  }
  const decipher = crypto.createDecipheriv(
    "aes-256-gcm",
    encryptionKey(),
    Buffer.from(ivValue, "base64url")
  );
  decipher.setAuthTag(Buffer.from(tagValue, "base64url"));
  return Buffer.concat([
    decipher.update(Buffer.from(encryptedValue, "base64url")),
    decipher.final()
  ]).toString("utf8");
}

function createStoredToken() {
  const token = generateToken();
  return {
    token,
    hash: tokenHash(token),
    encrypted: encryptSecret(token)
  };
}

module.exports = {
  createStoredToken,
  decryptSecret,
  encryptSecret,
  generateToken,
  scopedSignature,
  tokenHash,
  verifyScopedSignature
};
