/**
 * Native fetch HTTP client
 * Replaces axios — zero dependencies, same DX.
 */

const API_URL =
  process.env.NEXT_PUBLIC_API_URL ?? 'https://devapi.tamm-kreiz.bzh';

// ── Error ────────────────────────────────────────────────────────────

export class HttpError extends Error {
  readonly status: number;
  readonly data: unknown;

  constructor(status: number, data: unknown) {
    super(`HTTP ${status}`);
    this.name = 'HttpError';
    this.status = status;
    this.data = data;
  }

  /** Compat with catch blocks that inspect error.response (ex-axios pattern) */
  get response() {
    return { status: this.status, data: this.data };
  }
}

// ── Internals ────────────────────────────────────────────────────────

function getAuthHeaders(): Record<string, string> {
  if (typeof window === 'undefined') return {};
  const token = localStorage.getItem('tk_jwt_token');
  return token ? { Authorization: `Bearer ${token}` } : {};
}

function handle401(data: unknown): void {
  if (typeof window === 'undefined') return;
  const msg =
    data && typeof data === 'object' && 'message' in data
      ? String((data as Record<string, unknown>).message)
      : '';
  if (/Expired|Invalid|not found/.test(msg)) {
    localStorage.removeItem('tk_jwt_token');
    localStorage.removeItem('tk_auth_session');
  }
}

async function request<T>(
  method: string,
  url: string,
  body?: unknown,
  options?: { headers?: Record<string, string>; signal?: AbortSignal },
): Promise<{ data: T; status: number }> {
  const separator = url.startsWith('/') || API_URL.endsWith('/') ? '' : '/';
  const fullUrl = url.startsWith('http') ? url : `${API_URL}${separator}${url}`;

  const headers: Record<string, string> = {
    Accept: 'application/ld+json',
    ...getAuthHeaders(),
    ...options?.headers,
  };

  // Let the browser set Content-Type + boundary for FormData
  const isFormData = typeof FormData !== 'undefined' && body instanceof FormData;
  if (body !== undefined && !isFormData) {
    headers['Content-Type'] = 'application/json';
  }

  const res = await fetch(fullUrl, {
    method,
    headers,
    signal: options?.signal,
    body: isFormData
      ? (body as FormData)
      : body !== undefined
        ? JSON.stringify(body)
        : undefined,
  });

  const ct = res.headers.get('content-type') ?? '';
  const data: T = ct.includes('json') ? await res.json() : ((await res.text()) as unknown as T);

  if (!res.ok) {
    if (res.status === 401) handle401(data);
    throw new HttpError(res.status, data);
  }

  return { data, status: res.status };
}

// ── Public API ───────────────────────────────────────────────────────

const http = {
  get<T = any>(url: string, options?: { headers?: Record<string, string>; signal?: AbortSignal; params?: Record<string, unknown> }) {
    if (options?.params) {
      const searchParams = new URLSearchParams();
      for (const [key, value] of Object.entries(options.params)) {
        if (value != null) searchParams.append(key, String(value));
      }
      const sep = url.includes('?') ? '&' : '?';
      url = `${url}${sep}${searchParams.toString()}`;
    }
    return request<T>('GET', url, undefined, options);
  },
  post<T = any>(url: string, body?: unknown, options?: { headers?: Record<string, string> }) {
    return request<T>('POST', url, body, options);
  },
  patch<T = any>(url: string, body?: unknown, options?: { headers?: Record<string, string> }) {
    return request<T>('PATCH', url, body, options);
  },
  put<T = any>(url: string, body?: unknown, options?: { headers?: Record<string, string> }) {
    return request<T>('PUT', url, body, options);
  },
  delete<T = any>(url: string, options?: { headers?: Record<string, string> }) {
    return request<T>('DELETE', url, undefined, options);
  },
};

export default http;
