import axios, { AxiosRequestConfig } from "axios";
import { ENDPOINTS } from "@/constants/endpoints";

// Configure axios defaults
axios.defaults.baseURL = ENDPOINTS.BASE_URL;
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.withCredentials = true;

const mutatingInFlight = new Map<string, Promise<unknown>>();

function isAuthenticationRequest(url?: string): boolean {
  return Boolean(url?.replace(/^\//, "").startsWith("auth/"));
}

function newIdempotencyKey(): string {
  if (typeof globalThis.crypto?.randomUUID === "function") {
    return (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2));
  }
  return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
}

function mutate(method: "post" | "put" | "patch", url: string, data?: unknown, config: AxiosRequestConfig = {}) {
  const fingerprint = `${method}:${url}:${JSON.stringify(data ?? null)}`;
  const isBrowser = typeof window !== "undefined";
  const existing = isBrowser ? mutatingInFlight.get(fingerprint) : undefined;
  if (existing) return existing;

  const hasIdempotencyKey = Boolean(
    config.headers && Object.keys(config.headers).some((key) => key.toLowerCase() === "idempotency-key"),
  );
  const headers = !isAuthenticationRequest(url) && !hasIdempotencyKey
    ? { ...config.headers, "Idempotency-Key": newIdempotencyKey() }
    : config.headers;

  const request = axios.request({ ...config, method, url, data, headers }).then((res) => res.data);
  if (isBrowser) {
    mutatingInFlight.set(fingerprint, request);
    request.then(
      () => window.setTimeout(() => mutatingInFlight.delete(fingerprint), 750),
      () => mutatingInFlight.delete(fingerprint),
    );
  }
  return request;
}

// Add request interceptor to include language parameter and auth token
axios.interceptors.request.use((config) => {
  if (typeof window !== "undefined") {
    const language = localStorage.getItem("language") || "ar";
    const token = localStorage.getItem("auth_token");
    
    // Add Accept-Language and X-Language headers to all requests
    config.headers["Accept-Language"] = language;
    config.headers["X-Language"] = language;

    // Add Authorization header if token exists
    if (token) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    
    // Add language query param to GET requests
    if (config.method === "get") {
      config.params = { ...config.params, language };
    }
  }
  return config;
});

import { toast } from "sonner";
import i18n from "@/lib/i18n";

function fieldErrorsToMessage(fieldErrors: unknown): string {
  if (!fieldErrors || typeof fieldErrors !== "object") return "";
  const messages = Object.entries(fieldErrors as Record<string, unknown>)
    .flatMap(([field, errors]) => {
      const values = Array.isArray(errors) ? errors : [errors];
      return values
        .filter((value): value is string => typeof value === "string" && value.trim().length > 0)
        .map((value) => `${field}: ${value}`);
    })
    .slice(0, 4);
  return messages.join("\n");
}

function detailToMessage(detail: unknown): string {
  if (typeof detail === "string") return detail;
  if (!Array.isArray(detail)) return "";
  return detail
    .map((entry) => {
      if (!entry || typeof entry !== "object") return "";
      const item = entry as { loc?: unknown[]; msg?: unknown };
      const field = Array.isArray(item.loc) ? item.loc.filter((part) => part !== "body").join(".") : "";
      const msg = typeof item.msg === "string" ? item.msg : "";
      return field && msg ? `${field}: ${msg}` : msg;
    })
    .filter(Boolean)
    .slice(0, 4)
    .join("\n");
}

function apiErrorMessage(data: unknown, fallback: string): string {
  if (!data || typeof data !== "object") return fallback;
  const payload = data as { message?: unknown; detail?: unknown; field_errors?: unknown; trace_id?: unknown };
  const fieldMessage = fieldErrorsToMessage(payload.field_errors);
  const traceSuffix = typeof payload.trace_id === "string" && payload.trace_id.trim()
    ? `\ntrace_id: ${payload.trace_id}`
    : "";
  if (fieldMessage) return `${fieldMessage}${traceSuffix}`;
  if (typeof payload.message === "string" && payload.message.trim()) return `${payload.message}${traceSuffix}`;
  const detailMessage = detailToMessage(payload.detail);
  return `${detailMessage || fallback}${traceSuffix}`;
}

// Add response interceptor to handle errors globally
axios.interceptors.response.use(
  (response) => {
    if (response.data && response.data.success === false && typeof window !== "undefined") {
      toast.error(response.data.message || i18n.t('lib.api.text_1'));
    }
    return response;
  },
  (error) => {
    if (error.response) {
      const authRequest = isAuthenticationRequest(error.config?.url);
      if (error.response.status === 401) {
        if (typeof window !== "undefined") {
          if (!window.location.pathname.startsWith("/login") && !window.location.pathname.startsWith("/auth")) {
            window.dispatchEvent(new CustomEvent("session-expired"));
          }
        }
      } else if (!authRequest) {
        if (typeof window !== "undefined") {
          let errorMessage = apiErrorMessage(error.response.data, error.message);
          if (typeof errorMessage === "string" && errorMessage.includes("zero subtotal")) {
            errorMessage = i18n.t("app.admin.warehouse.receiving.requests.warnings.zero_subtotal");
          }
          toast.error(errorMessage);
        }
      }
    } else {
      if (typeof window !== "undefined") {
        toast.error(error.message);
      }
    }
    return Promise.reject(error);
  }
);

/**
 * API utility for HTTP requests using Axios
 */
export const api = {
  get: (url: string, config?: AxiosRequestConfig) => axios.get(url, config).then(res => res.data),
  post: (url: string, data?: unknown, config?: AxiosRequestConfig) => mutate("post", url, data, config),
  put: (url: string, data?: unknown, config?: AxiosRequestConfig) => mutate("put", url, data, config),
  patch: (url: string, data?: unknown, config?: AxiosRequestConfig) => mutate("patch", url, data, config),
  del: (url: string, config?: AxiosRequestConfig) => axios.delete(url, config).then(res => res.data),
};
