// File: src/services/API/ApiClient.js

/**
 * ApiClient
 *
 * Central HTTP client for making API requests to the MaplePress backend.
 * Handles authentication, error handling, and request/response transformation.
 */

// Auth manager getter function (will be set during service initialization)
// This is a function that returns the current authManager instance
let getAuthManager = null;

/**
 * Set the auth manager getter for API client to use
 * @param {Function} getter - Function that returns the current AuthManager instance
 */
export function setApiClientAuthManager(getter) {
  getAuthManager = getter;
  console.log("[ApiClient] AuthManager getter set");
}

/**
 * API Configuration
 */
const API_CONFIG = {
  baseURL: import.meta.env.VITE_API_BASE_URL || "http://localhost:8000",
  timeout: 30000,
};

/**
 * Make an authenticated API request
 * Automatically refreshes access token if needed before making the request
 */
async function makeRequest(method, endpoint, options = {}) {
  const { body, headers = {}, params, skipTokenRefresh = false } = options;

  // Automatically refresh token if needed (unless explicitly skipped)
  // Skip for refresh endpoint itself to avoid infinite loops
  if (!skipTokenRefresh && endpoint !== "/api/v1/refresh") {
    if (getAuthManager) {
      const authManager = getAuthManager();
      console.log("[ApiClient] 🔍 Retrieved AuthManager via getter:", {
        isInitialized: authManager?.getIsInitialized(),
        isAuthenticated: authManager?.isAuthenticated(),
      });

      // Wait for initialization if needed
      if (!authManager.getIsInitialized()) {
        console.log("[ApiClient] ⏳ Waiting for AuthManager initialization before token check...");
        // Wait up to 2 seconds for initialization
        for (let i = 0; i < 40; i++) {
          if (authManager.getIsInitialized()) break;
          await new Promise(resolve => setTimeout(resolve, 50));
        }
        console.log("[ApiClient] ✅ AuthManager initialized after waiting");
      }

      if (authManager.isAuthenticated()) {
        try {
          await authManager.ensureValidToken();
        } catch (error) {
          console.error("[ApiClient] Token refresh failed:", error);
          // Don't throw here - let the request proceed and handle 401 if it occurs
        }
      }
    }
  }

  // Build URL with query parameters
  let url = `${API_CONFIG.baseURL}${endpoint}`;
  if (params) {
    const queryString = new URLSearchParams(params).toString();
    url += `?${queryString}`;
  }

  // Build request headers
  const requestHeaders = {
    "Content-Type": "application/json",
    ...headers,
  };

  // Add authentication token if available
  // Use JWT prefix for access token (backend requirement)
  if (getAuthManager) {
    const authManager = getAuthManager();
    // Wait for initialization if needed
    if (!authManager.getIsInitialized()) {
      console.log("[ApiClient] Waiting for AuthManager initialization...");
      // Wait up to 2 seconds for initialization
      for (let i = 0; i < 40; i++) {
        if (authManager.getIsInitialized()) break;
        await new Promise(resolve => setTimeout(resolve, 50));
      }
    }

    if (authManager.isAuthenticated()) {
      const token = authManager.getAccessToken();
      requestHeaders["Authorization"] = `JWT ${token}`;
      console.log("[ApiClient] Added JWT token to request");
    } else {
      console.log("[ApiClient] Not authenticated, skipping token");
    }
  } else {
    console.log("[ApiClient] No authManager getter available");
  }

  // Build request options
  const requestOptions = {
    method,
    headers: requestHeaders,
  };

  if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
    requestOptions.body = JSON.stringify(body);
  }

  try {
    console.log(`[ApiClient] ${method} ${endpoint}`);

    const response = await fetch(url, requestOptions);

    // Handle non-OK responses
    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));

      // Handle 401 Unauthorized - token may have expired despite refresh attempt
      if (response.status === 401 && getAuthManager && !skipTokenRefresh && endpoint !== "/api/v1/refresh") {
        console.log("[ApiClient] ⚠️ Received 401, attempting token refresh and retry");

        const authManager = getAuthManager();

        // Check if authManager is initialized
        if (!authManager.getIsInitialized()) {
          console.error("[ApiClient] ❌ AuthManager not initialized, cannot refresh");
          throw new Error("Session expired. Please log in again.");
        }

        console.log("[ApiClient] 🔍 AuthManager state before refresh:", {
          isAuthenticated: authManager.isAuthenticated(),
          hasAccessToken: !!authManager.getAccessToken(),
        });

        try {
          // Force token refresh
          await authManager.refreshAccessToken();

          // Retry the request once with the new token
          console.log("[ApiClient] ✅ Retrying request with refreshed token");
          return makeRequest(method, endpoint, { ...options, skipTokenRefresh: true });
        } catch (refreshError) {
          console.error("[ApiClient] ❌ Token refresh on 401 failed:", refreshError);
          throw new Error("Session expired. Please log in again.");
        }
      }

      // Create error with RFC 9457 data if available
      const error = new Error(
        errorData.detail || errorData.message || `HTTP ${response.status}: ${response.statusText}`
      );

      // Attach RFC 9457 fields to error for parsing
      if (errorData.errors) {
        error.validationErrors = errorData.errors; // RFC 9457 validation errors
      }
      if (errorData.title) {
        error.title = errorData.title;
      }
      if (errorData.status) {
        error.status = errorData.status;
      }

      throw error;
    }

    // Parse response
    const contentType = response.headers.get("content-type");
    if (contentType && contentType.includes("application/json")) {
      return await response.json();
    }

    return await response.text();
  } catch (error) {
    console.error(`[ApiClient] Request failed:`, error);
    throw error;
  }
}

/**
 * API Client Methods
 */
const ApiClient = {
  /**
   * GET request
   */
  get(endpoint, options = {}) {
    return makeRequest("GET", endpoint, options);
  },

  /**
   * POST request
   */
  post(endpoint, body, options = {}) {
    return makeRequest("POST", endpoint, { ...options, body });
  },

  /**
   * PUT request
   */
  put(endpoint, body, options = {}) {
    return makeRequest("PUT", endpoint, { ...options, body });
  },

  /**
   * PATCH request
   */
  patch(endpoint, body, options = {}) {
    return makeRequest("PATCH", endpoint, { ...options, body });
  },

  /**
   * DELETE request
   */
  delete(endpoint, options = {}) {
    return makeRequest("DELETE", endpoint, options);
  },

  /**
   * Get API configuration
   */
  getConfig() {
    return API_CONFIG;
  },
};

export default ApiClient;
