import { createHmac, timingSafeEqual } from "crypto";

const base64Url = (input: Buffer | string): string =>
  Buffer.from(input)
    .toString("base64")
    .replace(/=/g, "")
    .replace(/\+/g, "-")
    .replace(/\//g, "_");

const fromBase64Url = (input: string): Buffer =>
  Buffer.from(input.replace(/-/g, "+").replace(/_/g, "/"), "base64");

export type JwtPayload = {
  sub: string;
  email: string;
  iat: number;
  exp: number;
};

const secret = (): string => {
  const value = process.env.AUTH_JWT_SECRET;
  if (!value) throw new Error("Missing AUTH_JWT_SECRET.");
  return value;
};

/** "Remember me" sessions: long enough that a rider rarely signs in again. */
export const SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;

/** Sessions on a shared or borrowed phone: survives a day, then expires. */
export const SHORT_SESSION_TTL_SECONDS = 12 * 60 * 60;

export const signJwt = (
  payload: { sub: string; email: string },
  expiresInSeconds = SESSION_TTL_SECONDS,
): string => {
  const iat = Math.floor(Date.now() / 1000);
  const body: JwtPayload = { ...payload, iat, exp: iat + expiresInSeconds };

  const header = base64Url(JSON.stringify({ alg: "HS256", typ: "JWT" }));
  const claims = base64Url(JSON.stringify(body));
  const signature = base64Url(
    createHmac("sha256", secret()).update(`${header}.${claims}`).digest(),
  );

  return `${header}.${claims}.${signature}`;
};

export const verifyJwt = (token: string): JwtPayload | null => {
  const parts = token.split(".");
  if (parts.length !== 3) return null;

  const [header, claims, signature] = parts;

  const expected = createHmac("sha256", secret())
    .update(`${header}.${claims}`)
    .digest();

  const received = fromBase64Url(signature);

  if (
    expected.length !== received.length ||
    !timingSafeEqual(expected, received)
  ) {
    return null;
  }

  try {
    const payload = JSON.parse(fromBase64Url(claims).toString()) as JwtPayload;

    if (payload.exp * 1000 < Date.now()) return null;

    return payload;
  } catch {
    return null;
  }
};

// Returns the authenticated principal or a ready-to-return error Response.
export const requireAuth = (
  req: Request,
): { userId: string; email: string } | { error: Response } => {
  const header = req.headers.get("authorization") ?? "";
  const token = header.startsWith("Bearer ") ? header.slice(7) : "";

  const payload = token ? verifyJwt(token) : null;

  if (!payload) {
    return {
      error: Response.json({ error: "Unauthorized." }, { status: 401 }),
    };
  }

  return { userId: payload.sub, email: payload.email };
};

export const decodeJwtExp = (token: string): number | null => {
  try {
    const claims = JSON.parse(
      fromBase64Url(token.split(".")[1]).toString(),
    ) as JwtPayload;

    return claims.exp ?? null;
  } catch {
    return null;
  }
};
