import { getApiUrl } from "../env";

export interface ProblemDetails {
  type: string;
  title: string;
  status: number;
  detail?: string;
  instance?: string;
  errors?: { field?: string; message: string }[];
}

export class ApiError extends Error {
  readonly status: number;
  readonly title: string;
  readonly detail?: string;
  readonly errors?: { field?: string; message: string }[];

  constructor(problem: ProblemDetails) {
    super(problem.detail ?? problem.title);
    this.name = "ApiError";
    this.status = problem.status;
    this.title = problem.title;
    this.detail = problem.detail;
    this.errors = problem.errors;
  }
}

export interface AuthCredentials {
  email: string;
  password: string;
}

export interface RegisterPayload {
  email: string;
  password: string;
  username?: string;
  displayName?: string;
}

export interface LoginResponse {
  twoFactorRequired?: boolean;
  challengeId?: string;
}

export interface CurrentUser {
  id: string;
  email: string;
  username: string;
  displayName?: string | null;
  emailVerifiedAt?: string | null;
  roles?: string[];
  twoFactorEnabled?: boolean;
}

export interface TwoFactorSetupResponse {
  secret: string;
  qrCodeUrl: string;
  recoveryCodes?: string[];
}

export interface UserSession {
  id: string;
  ipAddress?: string | null;
  userAgent?: string | null;
  createdAt: string;
  expiresAt: string;
  current?: boolean;
}

async function parseProblemResponse(response: Response): Promise<ApiError> {
  const contentType = response.headers.get("content-type") ?? "";

  if (contentType.includes("application/problem+json")) {
    const problem = (await response.json()) as ProblemDetails;
    return new ApiError({
      ...problem,
      status: problem.status ?? response.status,
      title: problem.title ?? "Request failed",
    });
  }

  return new ApiError({
    type: "about:blank",
    title: "Request failed",
    status: response.status,
    detail: response.statusText || undefined,
  });
}

export async function apiFetch<T>(
  path: string,
  options: RequestInit = {},
): Promise<T> {
  const endpoint = `${getApiUrl()}${path}`;

  if (!endpoint.startsWith("http")) {
    throw new Error("Invalid API URL configuration");
  }

  const headers = new Headers(options.headers);

  if (options.body && !headers.has("Content-Type")) {
    headers.set("Content-Type", "application/json");
  }

  const response = await fetch(endpoint, {
    ...options,
    credentials: "include",
    headers,
  });

  if (!response.ok) {
    throw await parseProblemResponse(response);
  }

  if (response.status === 204) {
    return undefined as T;
  }

  const text = await response.text();
  if (!text) {
    return undefined as T;
  }

  return JSON.parse(text) as T;
}

export async function submitLogin(
  credentials: AuthCredentials,
): Promise<LoginResponse> {
  return apiFetch<LoginResponse>("/auth/login", {
    method: "POST",
    body: JSON.stringify(credentials),
  });
}

export async function submitRegister(
  payload: RegisterPayload,
): Promise<void> {
  await apiFetch<void>("/auth/register", {
    method: "POST",
    body: JSON.stringify(payload),
  });
}

export async function submitLogout(): Promise<void> {
  await apiFetch<void>("/auth/logout", {
    method: "POST",
  });
}

export async function getCurrentUser(): Promise<CurrentUser> {
  return apiFetch<CurrentUser>("/me");
}

export async function verifyEmail(token: string): Promise<void> {
  await apiFetch<void>("/auth/verify-email", {
    method: "POST",
    body: JSON.stringify({ token }),
  });
}

export async function forgotPassword(email: string): Promise<void> {
  await apiFetch<void>("/auth/forgot-password", {
    method: "POST",
    body: JSON.stringify({ email }),
  });
}

export async function resetPassword(
  token: string,
  newPassword: string,
): Promise<void> {
  await apiFetch<void>("/auth/reset-password", {
    method: "POST",
    body: JSON.stringify({ token, newPassword }),
  });
}

export async function setupTwoFactor(): Promise<TwoFactorSetupResponse> {
  return apiFetch<TwoFactorSetupResponse>("/auth/2fa/setup", {
    method: "POST",
  });
}

export async function confirmTwoFactor(code: string): Promise<void> {
  await apiFetch<void>("/auth/2fa/confirm", {
    method: "POST",
    body: JSON.stringify({ code }),
  });
}

export async function verifyTwoFactorLogin(
  challengeId: string,
  code: string,
): Promise<void> {
  await apiFetch<void>("/auth/2fa/verify", {
    method: "POST",
    body: JSON.stringify({ challengeId, code }),
  });
}

export async function getSessions(): Promise<UserSession[]> {
  return apiFetch<UserSession[]>("/me/sessions");
}

export async function revokeSession(sessionId: string): Promise<void> {
  await apiFetch<void>(`/me/sessions/${sessionId}`, {
    method: "DELETE",
  });
}
