/**
 * Typed API error contracts and models.
 *
 * Provides discriminated error types for standardized error handling across
 * API calls and hooks, enabling actionable error reporting and diagnostics.
 */

/**
 * Discriminated error type representing an HTTP API error response.
 *
 * Thrown when the server returns a non-2xx HTTP status code.
 * Use the `type` discriminator to handle different error categories.
 *
 * The correlation_id enables tracing this error through request logs
 * on both frontend and backend systems for debugging distributed issues.
 */
export interface ApiErrorPayload {
  type: "api_error";
  /** HTTP status code returned by the server. */
  status: number;
  /** Raw response body text as returned by the server. */
  body: string;
  /** User-friendly error message derived from status and body. */
  message: string;
  /** Machine-readable error code for client-side branching (e.g., "jail_not_found"). */
  code?: string;
  /** Human-readable error description from the server. */
  detail?: string;
  /** Optional structured context for the error (e.g., field names, constraint violations). */
  metadata?: Record<string, string | number | boolean | null>;
  /** Unique ID for correlating this error with request logs on both frontend and backend. */
  correlationId?: string;
}

/**
 * Discriminated error type representing a network error.
 *
 * Thrown when the request fails due to network issues (DNS lookup failure,
 * connection timeout, offline, CORS error, etc.) or when parsing JSON fails.
 */
export interface NetworkErrorPayload {
  type: "network_error";
  /** Underlying error message (network stack or JSON parse error). */
  message: string;
}

/**
 * Discriminated error type representing a request abort.
 *
 * Thrown when a fetch request is aborted (e.g., component unmounts, user cancels).
 * These are expected and should typically be silently ignored.
 */
export interface AbortErrorPayload {
  type: "abort_error";
  message: string;
}

/**
 * Union of all possible fetch error types.
 *
 * Use the `type` discriminator to narrow and handle each error case:
 *
 * ```ts
 * const error: FetchError = ...;
 * if (error.type === "api_error") {
 *   // Handle HTTP error — check status for 401/403/50x etc.
 *   if (error.status === 401) redirectToLogin();
 * } else if (error.type === "network_error") {
 *   // Handle network/connectivity issues
 *   showNetworkMessage();
 * } else if (error.type === "abort_error") {
 *   // Request was cancelled — typically silent
 *   return;
 * }
 * ```
 */
export type FetchError = ApiErrorPayload | NetworkErrorPayload | AbortErrorPayload;

/**
 * Type guard to check if an error is an authentication error (401/403).
 *
 * @param error - The error to check (typically a FetchError)
 * @returns `true` if the error is a 401 or 403 API error
 */
export function isAuthError(error: FetchError): error is ApiErrorPayload {
  return error.type === "api_error" && (error.status === 401 || error.status === 403);
}

/**
 * Type guard to check if an error is an abort error (request cancelled).
 *
 * @param error - The error to check (typically a FetchError)
 * @returns `true` if the error is an abort error
 */
export function isAbortError(error: FetchError): error is AbortErrorPayload {
  return error.type === "abort_error";
}

/**
 * Type guard to check if an error is a network error.
 *
 * @param error - The error to check (typically a FetchError)
 * @returns `true` if the error is a network error
 */
export function isNetworkError(error: FetchError): error is NetworkErrorPayload {
  return error.type === "network_error";
}

/**
 * Type guard to check if an error is an API error.
 *
 * @param error - The error to check (typically a FetchError)
 * @returns `true` if the error is an API error
 */
export function isApiError(error: FetchError): error is ApiErrorPayload {
  return error.type === "api_error";
}
