// src/lib/api/customer-portal-client.ts
import { customerPortalTokenStorage } from "@/src/lib/api/customer-portal-storage"

type ApiErrorShape = { message: string; status: number }

function joinUrl(base: string, path: string) {
  const b = (base || "").replace(/\/+$/, "")
  const p = (path || "").replace(/^\/+/, "")
  return `${b}/${p}`
}

async function safeJson(res: Response) {
  const ct = res.headers.get("content-type") || ""
  if (!ct.includes("application/json")) return null
  try { return await res.json() } catch { return null }
}

function parseError(payload: any, status: number): ApiErrorShape {
  const msg =
    payload?.detail ||
    payload?.message ||
    (typeof payload === "string" ? payload : null) ||
    `Request failed (${status}).`
  return { message: msg, status }
}

function redirectToClientLogin(reason: "expired" | "revoked") {
  if (typeof window === "undefined") return
  // Récupère la locale depuis l’URL courante
  const seg = window.location.pathname.split("/").filter(Boolean)[0]
  const locale = seg === "en" ? "en" : "fr"
  // Évite les boucles de redirection si on est déjà sur le login
  if (window.location.pathname.includes("/client/login")) return
  window.location.replace(`/${locale}/client/login?reason=${reason}`)
}

function isRevokedMessage(msg: string) {
  const m = (msg || "").toLowerCase()
  return m.includes("blacklist") || m.includes("revoked") || m.includes("revoqu") || m.includes("révoqu")
}

async function refreshCustomerPortalAccess(base: string): Promise<string | null> {
  const refresh = customerPortalTokenStorage.getRefresh()
  if (!refresh) return null

  const url = joinUrl(base, "/auth/token/refresh/")
  const res = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ refresh }),
  })

  const data = await safeJson(res)
  if (!res.ok) return null
  const access = data?.access
  if (typeof access !== "string" || !access) return null

  customerPortalTokenStorage.setTokens(access, refresh)
  return access
}

export const customerPortalApi = {
  async request<T = any>(path: string, init?: RequestInit): Promise<T> {
    const base = process.env.NEXT_PUBLIC_API_BASE_URL || ""
    const url = joinUrl(base, path)

    const headers = new Headers(init?.headers || {})
    const access = customerPortalTokenStorage.getAccess()
    if (access) headers.set("Authorization", `Bearer ${access}`)

    // Ne pas forcer Content-Type si FormData
    if (init?.body && !headers.has("Content-Type")) {
      if (!(init.body instanceof FormData)) headers.set("Content-Type", "application/json")
    }

    const doFetch = (h: Headers) => fetch(url, { ...init, headers: h })

    let res = await doFetch(headers)

    // 401 → essai de refresh
    if (res.status === 401) {
      const firstPayload = await safeJson(res)
      const firstMsg = firstPayload?.detail || firstPayload?.message || ""

      const newAccess = await refreshCustomerPortalAccess(base)
      if (newAccess) {
        const retryHeaders = new Headers(headers)
        retryHeaders.set("Authorization", `Bearer ${newAccess}`)
        res = await doFetch(retryHeaders)
      } else {
        // Refresh impossible → session expirée ou révoquée
        customerPortalTokenStorage.clear()
        redirectToClientLogin(isRevokedMessage(firstMsg) ? "revoked" : "expired")
        const err: ApiErrorShape = { message: "Session expirée. Veuillez vous reconnecter.", status: 401 }
        throw Object.assign(new Error(err.message), err)
      }
    }

    // Toujours 401 après retry → token révoqué
    if (res.status === 401) {
      const payload = await safeJson(res)
      const msg = payload?.detail || payload?.message || ""
      customerPortalTokenStorage.clear()
      redirectToClientLogin(isRevokedMessage(msg) ? "revoked" : "expired")
      const err: ApiErrorShape = { message: "Session expirée. Veuillez vous reconnecter.", status: 401 }
      throw Object.assign(new Error(err.message), err)
    }

    const payload = await safeJson(res)

    if (!res.ok) {
      const err = parseError(payload, res.status)
      throw Object.assign(new Error(err.message), err)
    }

    // 204 No Content
    if (res.status === 204) return undefined as T

    return payload as T
  },
}
