import { createHmac, randomBytes, scryptSync, timingSafeEqual } from "crypto";

import { getAuthSecretOrThrow } from "@/lib/runtime-env";

export const AUTH_COOKIE_NAME = "abelbirdnest_session";
const SESSION_TTL_SECONDS = 60 * 60 * 12;

export type SessionUser = {
  id: string;
  name: string;
  role: string;
  email: string | null;
  username: string | null;
};

type SessionPayload = SessionUser & {
  exp: number;
};

function getAuthSecret() {
  return getAuthSecretOrThrow();
}

function base64UrlEncode(input: Buffer | string) {
  return Buffer.from(input)
    .toString("base64")
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/g, "");
}

function base64UrlDecode(input: string) {
  const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
  const padding = (4 - (normalized.length % 4)) % 4;
  return Buffer.from(`${normalized}${"=".repeat(padding)}`, "base64");
}

export function hashPassword(password: string) {
  const salt = randomBytes(16).toString("hex");
  const derived = scryptSync(password, salt, 64).toString("hex");
  return `${salt}:${derived}`;
}

export function verifyPassword(password: string, passwordHash: string) {
  const [salt, expected] = passwordHash.split(":");
  if (!salt || !expected) return false;

  const derived = scryptSync(password, salt, 64).toString("hex");
  return timingSafeEqual(Buffer.from(derived, "hex"), Buffer.from(expected, "hex"));
}

export function createSessionToken(user: SessionUser, ttlSeconds = SESSION_TTL_SECONDS) {
  const payload: SessionPayload = {
    ...user,
    exp: Math.floor(Date.now() / 1000) + ttlSeconds
  };
  const encodedPayload = base64UrlEncode(JSON.stringify(payload));
  const signature = createHmac("sha256", getAuthSecret())
    .update(encodedPayload)
    .digest();

  return `${encodedPayload}.${base64UrlEncode(signature)}`;
}

export function getSessionTtlSeconds() {
  return SESSION_TTL_SECONDS;
}

export function verifySessionToken(token: string): SessionUser | null {
  const [encodedPayload, encodedSignature] = token.split(".");
  if (!encodedPayload || !encodedSignature) return null;

  const expectedSignature = createHmac("sha256", getAuthSecret())
    .update(encodedPayload)
    .digest();
  const givenSignature = base64UrlDecode(encodedSignature);

  if (expectedSignature.length !== givenSignature.length) return null;
  if (!timingSafeEqual(expectedSignature, givenSignature)) return null;

  try {
    const payload = JSON.parse(base64UrlDecode(encodedPayload).toString("utf8")) as SessionPayload;
    if (payload.exp < Math.floor(Date.now() / 1000)) return null;

    return {
      id: payload.id,
      name: payload.name,
      role: payload.role,
      email: payload.email,
      username: payload.username
    };
  } catch {
    return null;
  }
}

export function getSessionCookieOptions(ttlSeconds = SESSION_TTL_SECONDS) {
  return {
    httpOnly: true,
    sameSite: "lax" as const,
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: ttlSeconds
  };
}
