// src/lib/api/client.ts
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
import type { ApiError } from '@/types/api';
import { notifyAccessTokenRefreshed } from '@/lib/auth/accessTokenEvents';
import { reportUnexpectedApiFailure } from '@/lib/error-tracking/reportUnexpectedApiFailure';

interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
  _retry?: boolean;
}

export const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  withCredentials: true, // ✅ REQUIRED FOR COOKIES
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 10000,
});

/** Invitation preview/accept must work with no cookies (public API, no JWT). */
function isPublicInvitationRequest(url: string | undefined): boolean {
  if (!url) return false;
  return (
    url.includes('/staff/invitations/preview') ||
    url.includes('/staff/invitations/accept') ||
    url.includes('/organizations/invitations/preview') ||
    url.includes('/organizations/invitations/accept')
  );
}

/** Session bootstrap / auth endpoints where 401 means "not logged in", not "retry refresh". */
function shouldSkipRefreshRetry(url: string | undefined): boolean {
  if (!url) return false;
  return (
    url.includes('/auth/refresh') ||
    url.includes('/auth/login') ||
    url.includes('/auth/register') ||
    url.includes('/auth/logout') ||
    url.includes('/auth/forgot-password') ||
    url.includes('/auth/profile/password')
  );
}

// ❌ REMOVE request interceptor completely (no Authorization header)

// ✅ Response interceptor
apiClient.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    reportUnexpectedApiFailure(error);
    const originalRequest = error.config as CustomAxiosRequestConfig;

    if (
      error.response?.status === 401 &&
      !originalRequest._retry &&
      !isPublicInvitationRequest(originalRequest.url) &&
      !shouldSkipRefreshRetry(originalRequest.url)
    ) {
      originalRequest._retry = true;

      try {
        // Refresh access token, then restore selected organization context on the new JWT.
        await axios.post(
          `${process.env.NEXT_PUBLIC_API_URL}/auth/refresh`,
          {},
          { withCredentials: true },
        );

        const orgId =
          typeof window !== 'undefined'
            ? localStorage.getItem('currentOrganizationId')
            : null;
        if (orgId) {
          try {
            await axios.post(
              `${process.env.NEXT_PUBLIC_API_URL}/auth/select-organization`,
              { organizationId: orgId },
              { withCredentials: true },
            );
          } catch {
            /* original retry may still succeed if refresh preserved organizationId */
          }
        }

        notifyAccessTokenRefreshed();
        return apiClient(originalRequest);
      } catch (refreshError) {
        if (typeof window !== 'undefined') {
          return Promise.reject(error); // ✅ just fail silently
        }
        return Promise.reject(refreshError);
      }
    }

    const responseData = error.response?.data as
      | { success?: false; error?: { code?: string; statusCode?: number; details?: ApiError['details'] } }
      | { message?: string | string[] }
      | undefined;

    if (responseData && 'error' in responseData && responseData.error?.code) {
      const apiError: ApiError = {
        statusCode: responseData.error.statusCode ?? error.response?.status ?? 500,
        code: responseData.error.code,
        details: responseData.error.details,
      };
      return Promise.reject(apiError);
    }

    const apiError: ApiError = {
      statusCode: error.response?.status || 500,
      code:
        error.response?.status === 401
          ? 'AUTH_UNAUTHORIZED'
          : error.response?.status === 403
            ? 'PERMISSION_DENIED'
            : error.response?.status === 404
              ? 'NOT_FOUND'
              : error.response?.status === 409
                ? 'CONFLICT'
                : (error.response?.status ?? 500) >= 500
                  ? 'INTERNAL_ERROR'
                  : 'BAD_REQUEST',
    };

    return Promise.reject(apiError);
  }
);