// src/lib/session.ts
import * as jose from "jose";
function calculateEntropy(str) {
  const len = str.length;
  const frequencies = /* @__PURE__ */ new Map();
  for (const char of str) {
    frequencies.set(char, (frequencies.get(char) || 0) + 1);
  }
  let entropy = 0;
  for (const count of frequencies.values()) {
    const probability = count / len;
    entropy -= probability * Math.log2(probability);
  }
  return entropy;
}
async function getSessionSecret() {
  const secret = process.env.SPFN_AUTH_SESSION_SECRET || // New prefixed version (recommended)
  process.env.SESSION_SECRET;
  if (!secret) {
    throw new Error("SPFN_AUTH_SESSION_SECRET environment variable is not set");
  }
  if (secret.length < 32) {
    throw new Error("SPFN_AUTH_SESSION_SECRET must be at least 32 characters long");
  }
  const encoder = new TextEncoder();
  const data = encoder.encode(secret);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  return new Uint8Array(hashBuffer);
}
async function sealSession(data, ttl = 60 * 60 * 24 * 7) {
  const secret = await getSessionSecret();
  return await new jose.EncryptJWT({ data }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${ttl}s`).setIssuer("spfn-auth").setAudience("spfn-client").encrypt(secret);
}
async function unsealSession(jwt) {
  const secret = await getSessionSecret();
  try {
    const { payload } = await jose.jwtDecrypt(jwt, secret, {
      issuer: "spfn-auth",
      audience: "spfn-client"
    });
    return payload.data;
  } catch (err) {
    if (err instanceof jose.errors.JWTExpired) {
      throw new Error("Session expired");
    }
    if (err instanceof jose.errors.JWEDecryptionFailed) {
      throw new Error("Invalid session");
    }
    if (err instanceof jose.errors.JWTClaimValidationFailed) {
      throw new Error("Session validation failed");
    }
    throw new Error("Failed to unseal session");
  }
}
async function getSessionInfo(jwt) {
  const secret = await getSessionSecret();
  try {
    const { payload } = await jose.jwtDecrypt(jwt, secret);
    return {
      issuedAt: new Date(payload.iat * 1e3),
      expiresAt: new Date(payload.exp * 1e3),
      issuer: payload.iss || "",
      audience: Array.isArray(payload.aud) ? payload.aud[0] : payload.aud || ""
    };
  } catch (err) {
    if (process.env.NODE_ENV !== "production") {
      console.warn("[Session] Failed to get session info:", err instanceof Error ? err.message : "Unknown error");
    }
    return null;
  }
}
async function shouldRefreshSession(jwt, thresholdHours = 24) {
  const info = await getSessionInfo(jwt);
  if (!info) {
    return true;
  }
  const hoursRemaining = (info.expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60);
  return hoursRemaining < thresholdHours;
}
function validateSessionSecret() {
  try {
    const secret = process.env.SPFN_AUTH_SESSION_SECRET || // New prefixed version (recommended)
    process.env.SESSION_SECRET;
    if (!secret) {
      return { valid: false, error: "SPFN_AUTH_SESSION_SECRET is not set" };
    }
    const length = secret.length;
    const uniqueChars = new Set(secret).size;
    const entropy = calculateEntropy(secret);
    if (length < 32) {
      return {
        valid: false,
        error: `SPFN_AUTH_SESSION_SECRET too short (${length} chars, minimum 32)`,
        details: { length, uniqueChars, entropy }
      };
    }
    if (uniqueChars < 16) {
      return {
        valid: false,
        error: `SPFN_AUTH_SESSION_SECRET has low diversity (${uniqueChars} unique chars, minimum 16)`,
        details: { length, uniqueChars, entropy }
      };
    }
    if (entropy < 3.5) {
      return {
        valid: false,
        error: `SPFN_AUTH_SESSION_SECRET has low entropy (${entropy.toFixed(2)} bits/char, minimum 3.5). Use a more random secret.`,
        details: { length, uniqueChars, entropy }
      };
    }
    return {
      valid: true,
      details: { length, uniqueChars, entropy }
    };
  } catch (err) {
    return { valid: false, error: "Failed to validate SPFN_AUTH_SESSION_SECRET" };
  }
}
export {
  getSessionInfo,
  sealSession,
  shouldRefreshSession,
  unsealSession,
  validateSessionSecret
};
//# sourceMappingURL=session.js.map