import { getSession } from 'next-auth/react';
import {
  requestResponseHandler,
  errorHandler,
  triggerSignOut,
} from '@/app/actions/actionsHandlers';
import logger from '@/utils/logger';

// Déduplique les appels concurrents à getSession() :
// si plusieurs fetchWithAuth() partent en même temps (chargement de page),
// ils partagent la même promesse au lieu de déclencher N refreshs JWT en parallèle.
let _pendingSessionPromise = null;

const getSessionOnce = () => {
  if (!_pendingSessionPromise) {
    _pendingSessionPromise = getSession().finally(() => {
      _pendingSessionPromise = null;
    });
  }
  return _pendingSessionPromise;
};

/**
 * Récupère le token JWT Bearer depuis la session NextAuth.
 * @returns {Promise<string|null>}
 */
export const getAuthToken = async () => {
  const session = await getSessionOnce();
  if (!session) {
    logger.warn('getAuthToken: session nulle, aucun token envoyé');
    return null;
  }
  if (session?.error === 'RefreshTokenError') {
    logger.warn(
      'getAuthToken: RefreshTokenError détecté, déconnexion en cours'
    );
    await triggerSignOut();
    return null;
  }
  if (!session?.user?.token) {
    logger.warn('getAuthToken: session présente mais token absent', {
      session,
    });
    return null;
  }
  return session.user.token;
};

/**
 * Wrapper de fetch qui injecte automatiquement le header Authorization Bearer
 * depuis la session NextAuth, puis passe la réponse dans requestResponseHandler.
 *
 * - Ajoute Content-Type: application/json par défaut (sauf si le body est FormData)
 * - Ajoute credentials: 'include' par défaut
 * - Les options.headers passées en paramètre surchargent les défauts (ex: X-CSRFToken)
 *
 * @param {string} url
 * @param {RequestInit} options
 * @returns {Promise<any>} Corps de la réponse désérialisé
 */
export const fetchWithAuth = async (url, options = {}) => {
  const token = await getAuthToken();
  const isFormData = options.body instanceof FormData;

  const headers = {
    ...(isFormData ? {} : { 'Content-Type': 'application/json' }),
    ...options.headers,
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
  };

  return fetch(url, {
    credentials: 'include',
    ...options,
    headers,
  })
    .then(requestResponseHandler)
    .catch(errorHandler);
};

/**
 * Variante de fetchWithAuth qui retourne la Response brute sans passer
 * par requestResponseHandler. Utile quand l'appelant gère lui-même response.ok.
 *
 * @param {string} url
 * @param {RequestInit} options
 * @returns {Promise<Response>}
 */
export const fetchWithAuthRaw = async (url, options = {}) => {
  const token = await getAuthToken();

  const headers = {
    ...options.headers,
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
  };

  return fetch(url, {
    credentials: 'include',
    ...options,
    headers,
  });
};
