import axios from "axios";

import { defaultLocale, type Locale } from "@/lib/locales";

const apiErrorCopy = {
  en: {
    fallback: "Something went wrong. Please try again.",
    server: "Something went wrong on our server. Please try again later or contact support.",
    network: "Unable to connect to the server. Please check your internet connection or try again later.",
    timeout: "Request timeout. Please try again.",
  },
  hi: {
    fallback: "कुछ गलत हो गया। कृपया फिर से कोशिश करें।",
    server: "हमारे सर्वर पर कुछ गलत हो गया। कृपया बाद में फिर से कोशिश करें या सपोर्ट से संपर्क करें।",
    network: "सर्वर से कनेक्ट नहीं हो पा रहा है। कृपया अपना इंटरनेट कनेक्शन जांचें या बाद में फिर से कोशिश करें।",
    timeout: "रिक्वेस्ट टाइमआउट हो गई। कृपया फिर से कोशिश करें।",
  },
  gu: {
    fallback: "કંઈક ખોટું થયું. કૃપા કરીને ફરી પ્રયાસ કરો.",
    server: "અમારા સર્વર પર કંઈક ખોટું થયું. કૃપા કરીને પછીથી ફરી પ્રયાસ કરો અથવા સપોર્ટનો સંપર્ક કરો.",
    network: "સર્વર સાથે કનેક્ટ થઈ શકતું નથી. કૃપા કરીને તમારું ઇન્ટરનેટ કનેક્શન તપાસો અથવા પછીથી ફરી પ્રયાસ કરો.",
    timeout: "રિક્વેસ્ટ ટાઈમઆઉટ થઈ. કૃપા કરીને ફરી પ્રયાસ કરો.",
  },
} satisfies Record<Locale, Record<string, string>>;

export class ApiError extends Error {
  status?: number;

  constructor(message: string, status?: number) {
    super(message);
    this.name = "ApiError";
    this.status = status;
  }
}

export function getApiErrorMessage(error: unknown, locale: Locale = defaultLocale): string {
  const copy = apiErrorCopy[locale];

  if (error instanceof ApiError) return getSafeMessage(error.message, error.status, locale);

  if (axios.isAxiosError(error)) {
    const status = error.response?.status;

    if (error.code === "ERR_NETWORK") {
      return copy.network;
    }

    if (error.code === "ECONNABORTED") {
      return copy.timeout;
    }

    const extracted = extractMessage(error.response?.data);

    return getSafeMessage(extracted ?? error.message ?? copy.fallback, status, locale);
  }

  if (error instanceof Error) {
    return getSafeMessage(error.message || copy.fallback, undefined, locale);
  }

  return getSafeMessage(extractMessage(error) ?? copy.fallback, undefined, locale);
}

export function normalizeApiError(error: unknown, locale: Locale = defaultLocale): ApiError {
  const copy = apiErrorCopy[locale];

  if (error instanceof ApiError) {
    return new ApiError(getSafeMessage(error.message, error.status, locale), error.status);
  }

  if (axios.isAxiosError(error)) {
    const status = error.response?.status;

    if (error.code === "ERR_NETWORK") {
      return new ApiError(copy.network, status);
    }

    const extracted = extractMessage(error.response?.data);

    return new ApiError(
      getSafeMessage(extracted ?? error.message ?? copy.fallback, status, locale),
      status,
    );
  }

  return new ApiError(getApiErrorMessage(error, locale));
}

function extractMessage(value: unknown): string | undefined {
  if (typeof value === "string" && value.trim()) {
    return value.trim();
  }

  if (Array.isArray(value)) {
    for (const item of value) {
      const message = extractMessage(item);
      if (message) return message;
    }
  }

  if (isRecord(value)) {
    for (const key of ["message", "errors", "error", "detail", "non_field_errors"]) {
      const message = extractMessage(value[key]);
      if (message) return message;
    }

    for (const entry of Object.values(value)) {
      const message = extractMessage(entry);
      if (message) return message;
    }
  }

  return undefined;
}

function getSafeMessage(message: string, status?: number, locale: Locale = defaultLocale): string {
  const copy = apiErrorCopy[locale];
  const cleanMessage = stripHtml(message).trim();

  if (!cleanMessage) return copy.fallback;

  // Hide all backend/Django/Python technical errors from users
  if (isTechnicalBackendError(cleanMessage)) {
    return copy.server;
  }

  // Hide very long messages, because they are usually debug pages / tracebacks
  if (cleanMessage.length > 220) {
    return status && status >= 500 ? copy.server : copy.fallback;
  }

  if (status && status >= 500) {
    return copy.server;
  }

  return cleanMessage;
}

function isTechnicalBackendError(message: string): boolean {
  const lower = message.toLowerCase();

  return [
    "traceback",
    "syntaxerror",
    "typeerror",
    "valueerror",
    "attributeerror",
    "keyerror",
    "integrityerror",
    "programmingerror",
    "operationalerror",
    "multipleobjectsreturned",
    "django",
    "debug = true",
    "exception type",
    "exception value",
    "request method",
    "request url",
    "python executable",
    "python version",
    "installed applications",
    "installed middleware",
    "settings:",
    "invalid syntax",
    "server error",
    "<!doctype html",
    "<html",
    "</html>",
  ].some((keyword) => lower.includes(keyword));
}

function stripHtml(value: string): string {
  return value
    .replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "")
    .replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, "")
    .replace(/<[^>]+>/g, " ")
    .replace(/\s+/g, " ");
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}
