export type WidgetErrorCode =
  | "access_denied"
  | "configuration_unavailable"
  | "invalid_response"
  | "rate_limited"
  | "service_unavailable"
  | "stream_error"
  | "stream_interrupted";

export class WidgetError extends Error {
  readonly code: WidgetErrorCode;
  readonly status?: number;

  constructor(code: WidgetErrorCode, options: { status?: number } = {}) {
    super(code);
    this.name = "WidgetError";
    this.code = code;
    if (options.status !== undefined) this.status = options.status;
  }
}

/**
 * Maps internal failures to bounded visitor-safe copy. Provider payloads and
 * raw exception messages never cross this presentation boundary.
 */
export function getWidgetErrorMessage(error: unknown): string {
  if (isAbortError(error)) return "Richiesta interrotta.";
  if (!(error instanceof WidgetError)) {
    return "La connessione si è interrotta. Riprova tra poco.";
  }

  switch (error.code) {
    case "access_denied":
      return "Akintu non è disponibile su questo sito. Verifica l’installazione.";
    case "rate_limited":
      return "Sono arrivate troppe richieste. Attendi un momento e riprova.";
    case "service_unavailable":
      return "Il collega digitale è temporaneamente non disponibile. Riprova tra poco.";
    case "stream_error":
    case "stream_interrupted":
      return "La risposta si è interrotta. Puoi inviare di nuovo la domanda.";
    case "configuration_unavailable":
    case "invalid_response":
      return "Akintu non è disponibile in questo momento. Riprova più tardi.";
  }
}

/** Normalizes only HTTP states that have distinct visitor recovery actions. */
export function mapHttpError(status: number): WidgetError {
  if (status === 401 || status === 403) {
    return new WidgetError("access_denied", { status });
  }
  if (status === 429) {
    return new WidgetError("rate_limited", { status });
  }
  if (status === 503) {
    return new WidgetError("service_unavailable", { status });
  }
  return new WidgetError("invalid_response", { status });
}

export function isAbortError(error: unknown): boolean {
  return (
    error instanceof DOMException &&
    (error.name === "AbortError" || error.code === DOMException.ABORT_ERR)
  );
}
