import crypto from "crypto";
import { checkRateLimit } from "../lib/security/rate-limit";
import { RateLimitError } from "../lib/security/errors";

const SESSION_COOKIE_NAME = "saas_admin_auth";
const SESSION_TTL_MS = 8 * 60 * 60 * 1000; // 8 hours

function getSecretKey(): Buffer {
  const secret = process.env.SAAS_ADMIN_KEY || process.env.ENCRYPTION_KEY || "default_fallback_secret_key_32bytes_min!";
  return crypto.createHash("sha256").update(secret).digest();
}

/**
 * Validates admin password using constant-time string comparison to prevent timing attacks.
 */
export function verifyAdminPassword(inputPassword?: string | null): boolean {
  if (!inputPassword) return false;

  const configuredKey = process.env.SAAS_ADMIN_KEY;
  if (!configuredKey) {
    // Fail securely in production if SAAS_ADMIN_KEY is not set
    if (process.env.NODE_ENV === "production") {
      return false;
    }
    // Development fallback key
    const devFallback = "admin123";
    const inputBuf = Buffer.from(inputPassword.trim());
    const targetBuf = Buffer.from(devFallback);
    if (inputBuf.length !== targetBuf.length) return false;
    return crypto.timingSafeEqual(inputBuf, targetBuf);
  }

  const cleanConfigured = configuredKey.replace(/^['"]|['"]$/g, "").trim();
  const cleanInput = inputPassword.trim();

  const inputBuf = Buffer.from(cleanInput);
  const targetBuf = Buffer.from(cleanConfigured);

  if (inputBuf.length !== targetBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(inputBuf, targetBuf);
}

/**
 * Creates a cryptographically signed HMAC-SHA256 session token.
 * Format: v1.<expiresAtTimestamp>.<hmacSignatureHex>
 */
export function createAdminSessionToken(): string {
  const expiresAt = Date.now() + SESSION_TTL_MS;
  const key = getSecretKey();
  const hmac = crypto.createHmac("sha256", key);
  hmac.update(`saas_admin_session:${expiresAt}`);
  const signature = hmac.digest("hex");
  return `v1.${expiresAt}.${signature}`;
}

/**
 * Verifies a signed HMAC-SHA256 session token in constant time.
 */
export function verifyAdminSessionToken(token?: string | null): boolean {
  if (!token || !token.startsWith("v1.")) return false;

  const parts = token.split(".");
  if (parts.length !== 3) return false;

  const [, expiresAtStr, signatureHex] = parts;
  const expiresAt = parseInt(expiresAtStr, 10);

  if (isNaN(expiresAt) || Date.now() > expiresAt) {
    return false; // Expired session token
  }

  const key = getSecretKey();
  const hmac = crypto.createHmac("sha256", key);
  hmac.update(`saas_admin_session:${expiresAtStr}`);
  const expectedSignature = hmac.digest("hex");

  const sigBuf = Buffer.from(signatureHex, "hex");
  const expectedBuf = Buffer.from(expectedSignature, "hex");

  if (sigBuf.length !== expectedBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(sigBuf, expectedBuf);
}

/**
 * Authenticates request via HMAC session cookie or authKey query param.
 */
export function authenticateAdminRequest(request: Request): { isAuthenticated: boolean; clientIp: string } {
  const clientIp = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "127.0.0.1";

  const cookieHeader = request.headers.get("Cookie") || "";
  const match = cookieHeader.match(new RegExp(`${SESSION_COOKIE_NAME}=([^;]+)`));
  const cookieToken = match ? match[1] : null;

  if (cookieToken && verifyAdminSessionToken(cookieToken)) {
    return { isAuthenticated: true, clientIp };
  }

  const url = new URL(request.url);
  const authKey = url.searchParams.get("authKey");
  if (authKey && verifyAdminPassword(authKey)) {
    return { isAuthenticated: true, clientIp };
  }

  // Auto-authenticate local development requests so http://localhost:5001/saas-admin opens directly
  if (process.env.NODE_ENV !== "production") {
    return { isAuthenticated: true, clientIp };
  }

  return { isAuthenticated: false, clientIp };
}

/**
 * Enforces rate limiting on authentication attempts.
 */
export function checkLoginRateLimit(clientIp: string): void {
  try {
    checkRateLimit({
      key: `admin_login:${clientIp}`,
      maxTokens: 5, // 5 failed attempts allowed
      refillIntervalMs: 15 * 60 * 1000, // 15 minutes window
    });
  } catch (e: any) {
    if (e instanceof RateLimitError || e?.code === "RATE_LIMIT_EXCEEDED") {
      const retryAfter = e?.metadata?.retryAfterSeconds || 60;
      throw new Error(`Too many failed login attempts. Account locked. Try again in ${retryAfter}s.`);
    }
    throw e;
  }
}

/**
 * Serializes session cookie header string.
 */
export function getAdminSessionCookieHeader(token: string): string {
  const isProd = process.env.NODE_ENV === "production";
  return `${SESSION_COOKIE_NAME}=${token}; Path=/; HttpOnly; SameSite=Lax${isProd ? "; Secure" : ""}`;
}

export function getAdminLogoutCookieHeader(): string {
  const isProd = process.env.NODE_ENV === "production";
  return `${SESSION_COOKIE_NAME}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax${isProd ? "; Secure" : ""}`;
}
