import { tokenStorage } from "./storage"
import { refreshAccessToken } from "./auth-refresh"
import { getLoginRoute } from "@/src/lib/auth/login-route"

const BASE = process.env.NEXT_PUBLIC_API_BASE_URL

type ApiError = { message: string; status?: number; code?: string; [key: string]: unknown }

/** BASE sans slash final + path sans slash initial */
function joinUrl(base: string, path: string) {
  const b = base.replace(/\/+$/, "")
  const p = path.replace(/^\/+/, "")
  return `${b}/${p}`
}

async function parseError(res: Response): Promise<ApiError> {
  let msg = `HTTP ${res.status}`
  let extra: Record<string, unknown> = {}
  try {
    const data = await res.json()
    msg = data?.detail || data?.message || JSON.stringify(data)
    // Conserver les champs supplémentaires (code, cases_count, etc.)
    const { detail: _d, message: _m, ...rest } = data ?? {}
    extra = rest
  } catch {}
  return { message: msg, status: res.status, ...extra }
}

/** Essaie de déduire /fr ou /en depuis l'URL courante */
function guessLocaleFromPathname() {
  if (typeof window === "undefined") return "fr"
  const seg = window.location.pathname.split("/").filter(Boolean)[0]
  if (seg === "fr" || seg === "en") return seg
  return "fr"
}

/** Détecte un token révoqué/blacklisté via message backend */
function isRevokedMessage(msg: string) {
  const m = (msg || "").toLowerCase()
  return m.includes("blacklist") || m.includes("revoked") || m.includes("revoqu") || m.includes("révoqu")
}

/** Redirect vers la bonne page login selon le domaine (admin ou tenant) */
function redirectToLogin(reason: "revoked" | "expired") {
  if (typeof window === "undefined") return
  const locale = guessLocaleFromPathname()
  window.location.replace(getLoginRoute(locale, reason))
}

/**
 * Fetch "no-auth": PAS de Bearer, PAS de refresh retry.
 * À utiliser pour refresh/logout pour éviter les boucles.
 */
export async function rawFetchNoAuth(path: string, init: RequestInit = {}) {
  if (!BASE) throw new Error("NEXT_PUBLIC_API_BASE_URL is not set")

  const headers = new Headers(init.headers)
  // si tu envoies du JSON, on force le content-type
  if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json")

  return fetch(joinUrl(BASE, path), { ...init, headers })
}

async function rawFetch(path: string, init: RequestInit = {}) {
  if (!BASE) throw new Error("NEXT_PUBLIC_API_BASE_URL s is not set")

  const headers = new Headers(init.headers)
  // Ne pas forcer Content-Type si le body est FormData (le navigateur gère le boundary multipart)
  if (!headers.has("Content-Type") && !(init.body instanceof FormData)) {
    headers.set("Content-Type", "application/json")
  }

  const access = tokenStorage.getAccess()
  if (access) headers.set("Authorization", `Bearer ${access}`)

  return fetch(joinUrl(BASE, path), { ...init, headers })
}

async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
  // 1) tentative normale
  let res = await rawFetch(path, init)

  // 2) si 401 -> refresh -> retry 1 fois
  if (res.status === 401) {
    // lire le message 401 initial (utile pour savoir si c'est blacklist)
    const firstErr = await parseError(res)

    const newAccess = await refreshAccessToken()
    if (!newAccess) {
      // session expirée OU refresh révoqué -> on redirige
      tokenStorage.clear()
      redirectToLogin(isRevokedMessage(firstErr.message) ? "revoked" : "expired")
      throw { message: "Session expirée. Veuillez vous reconnecter.", status: 401 } as ApiError
    }

    // retry avec le nouvel access
    res = await rawFetch(path, init)
  }

  // 3) si toujours pas OK
  if (!res.ok) {
    const err = await parseError(res)

    // si backend renvoie encore 401 => on nettoie + redirige
    if (res.status === 401) {
      tokenStorage.clear()
      redirectToLogin(isRevokedMessage(err.message) ? "revoked" : "expired")
    }

    // 402 = abonnement expiré → broadcast pour la gate UI
    if (res.status === 402 && typeof window !== "undefined") {
      window.dispatchEvent(new CustomEvent("subscription:expired", { detail: err }))
    }

    throw err
  }

  // 4) 204 no content
  if (res.status === 204) return undefined as T

  return (await res.json()) as T
}

/** Fetch authentifié retournant un Blob (téléchargement de fichiers) */
export async function fetchBlob(path: string): Promise<Blob> {
  const res = await rawFetch(path, { method: "GET" })
  if (res.status === 401) {
    const newAccess = await refreshAccessToken()
    if (!newAccess) { tokenStorage.clear(); redirectToLogin("expired"); throw new Error("Session expirée") }
    const retry = await rawFetch(path, { method: "GET" })
    if (!retry.ok) throw new Error(`HTTP ${retry.status}`)
    return retry.blob()
  }
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.blob()
}

export const api = { request }
