/**
 * Keycloak Admin API client for fetching login events, sessions, and credentials.
 * Uses service account token obtained via client_credentials grant.
 */

const KEYCLOAK_ISSUER = process.env.AUTH_KEYCLOAK_ISSUER || "";
const KEYCLOAK_CLIENT_ID = process.env.AUTH_KEYCLOAK_ID || "";
const KEYCLOAK_CLIENT_SECRET = process.env.AUTH_KEYCLOAK_SECRET || "";

// Derive admin base URL from issuer (e.g., https://auth.gosec.cloud/realms/gosecCloud → https://auth.gosec.cloud)
function getAdminBaseUrl(): string {
  const url = new URL(KEYCLOAK_ISSUER);
  return `${url.protocol}//${url.host}`;
}

function getRealmName(): string {
  // issuer format: https://host/realms/{realm}
  const parts = KEYCLOAK_ISSUER.split("/realms/");
  return parts[1] || "gosecCloud";
}

let cachedToken: { token: string; expiresAt: number } | null = null;

async function getServiceAccountToken(): Promise<string> {
  if (cachedToken && Date.now() < cachedToken.expiresAt) {
    return cachedToken.token;
  }

  const res = await fetch(`${KEYCLOAK_ISSUER}/protocol/openid-connect/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: KEYCLOAK_CLIENT_ID,
      client_secret: KEYCLOAK_CLIENT_SECRET,
    }),
  });

  if (!res.ok) {
    throw new Error(`Keycloak token request failed: ${res.status}`);
  }

  const data = await res.json();
  cachedToken = {
    token: data.access_token,
    expiresAt: Date.now() + (data.expires_in - 30) * 1000,
  };
  return cachedToken.token;
}

async function adminFetch(path: string): Promise<unknown> {
  const token = await getServiceAccountToken();
  const baseUrl = getAdminBaseUrl();
  const realm = getRealmName();
  const url = `${baseUrl}/admin/realms/${realm}${path}`;

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` },
    next: { revalidate: 0 },
  });

  if (!res.ok) {
    throw new Error(`Keycloak admin API error: ${res.status} ${res.statusText}`);
  }

  return res.json();
}

// ─── Public API ─────────────────────────────────────────────

export interface KeycloakLoginEvent {
  time: number;
  type: string;
  clientId: string;
  userId: string;
  ipAddress: string;
  details?: Record<string, string>;
  error?: string;
}

export interface KeycloakSession {
  id: string;
  userId: string;
  ipAddress: string;
  start: number;
  lastAccess: number;
  clients: Record<string, string>;
}

/**
 * Get login events for a user (both successful and failed).
 */
export async function getLoginEvents(keycloakId: string, maxResults = 50): Promise<KeycloakLoginEvent[]> {
  try {
    const events = (await adminFetch(
      `/events?userId=${keycloakId}&type=LOGIN&type=LOGIN_ERROR&max=${maxResults}&first=0`
    )) as KeycloakLoginEvent[];
    return events;
  } catch (err) {
    console.warn("[keycloak] Failed to fetch login events:", err instanceof Error ? err.message : err);
    return [];
  }
}

/**
 * Get active sessions for a user.
 */
export async function getUserSessions(keycloakId: string): Promise<KeycloakSession[]> {
  try {
    const sessions = (await adminFetch(`/users/${keycloakId}/sessions`)) as KeycloakSession[];
    return sessions;
  } catch (err) {
    console.warn("[keycloak] Failed to fetch user sessions:", err instanceof Error ? err.message : err);
    return [];
  }
}

/**
 * Check if user has TOTP (2FA) configured.
 */
export async function getUserHas2FA(keycloakId: string): Promise<boolean> {
  try {
    const credentials = (await adminFetch(`/users/${keycloakId}/credentials`)) as Array<{
      type: string;
      id: string;
    }>;
    return credentials.some((c) => c.type === "otp");
  } catch (err) {
    console.warn("[keycloak] Failed to fetch user credentials:", err instanceof Error ? err.message : err);
    return false;
  }
}

/**
 * Get admin events (password changes, 2FA config, etc.) for a user.
 */
export async function getAdminEvents(keycloakId: string, maxResults = 20): Promise<KeycloakLoginEvent[]> {
  try {
    const events = (await adminFetch(
      `/admin-events?userId=${keycloakId}&max=${maxResults}&first=0`
    )) as KeycloakLoginEvent[];
    return events;
  } catch (err) {
    console.warn("[keycloak] Failed to fetch admin events:", err instanceof Error ? err.message : err);
    return [];
  }
}
