import type { AuthUser } from "@pi-chat/shared";
import type { FastifyRequest } from "fastify";
import { createRemoteJWKSet, jwtVerify } from "jose";
import { envString } from "../env.js";

type KeycloakClaims = {
  sub?: string;
  preferred_username?: string;
  email?: string;
  realm_access?: { roles?: string[] };
  resource_access?: Record<string, { roles?: string[] }>;
};

const authMode = () => envString(process.env.AUTH_MODE, "local");
const oidcIssuer = () => envString(process.env.OIDC_ISSUER, "https://auth.rikrdo.com/realms/homelab");
const oidcAudience = () => envString(process.env.OIDC_AUDIENCE, "pi-chat");

let jwks: ReturnType<typeof createRemoteJWKSet> | undefined;

const getLocalUser = (): AuthUser => ({
  id: "local-user",
  username: "local-user",
  roles: ["admin", "webhook-runner"],
});

const bearerTokenFrom = (request: FastifyRequest) => {
  const header = request.headers.authorization;
  if (!header?.startsWith("Bearer ")) {
    throw new Error("auth_missing_bearer_token");
  }

  return header.slice("Bearer ".length).trim();
};

const rolesFromClaims = (claims: KeycloakClaims) => {
  const audience = oidcAudience();
  const realmRoles = claims.realm_access?.roles ?? [];
  const clientRoles = claims.resource_access?.[audience]?.roles ?? [];

  return [...new Set([...realmRoles, ...clientRoles])];
};

const getKeycloakUser = async (request: FastifyRequest): Promise<AuthUser> => {
  const issuer = oidcIssuer();
  const audience = oidcAudience();
  jwks ??= createRemoteJWKSet(new URL(`${issuer}/protocol/openid-connect/certs`));

  const { payload } = await jwtVerify(bearerTokenFrom(request), jwks, {
    issuer,
    audience,
  });
  const claims = payload as KeycloakClaims;

  if (!claims.sub) {
    throw new Error("auth_missing_subject");
  }

  return {
    id: claims.sub,
    username: claims.preferred_username,
    email: claims.email,
    roles: rolesFromClaims(claims),
  };
};

export const getAuthUser = async (request: FastifyRequest): Promise<AuthUser> => {
  if (authMode() === "local") {
    return getLocalUser();
  }

  if (authMode() === "keycloak") {
    return getKeycloakUser(request);
  }

  throw new Error(`auth_mode_not_supported:${authMode()}`);
};
