"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.OxyServicesBase = void 0;
var _jwtDecode = require("jwt-decode");
var _errorUtils = require("../utils/errorUtils");
var _HttpService = require("./HttpService");
var _OxyServices = require("./OxyServices.errors");
/**
 * OxyServices Base Class
 * 
 * Contains core infrastructure, HTTP client, request management, and error handling
 */

/**
 * Base class for OxyServices with core infrastructure
 */
class OxyServicesBase {
  constructor(...args) {
    const config = args[0];
    if (!config || typeof config !== 'object') {
      throw new Error('OxyConfig is required');
    }
    this.config = config;
    this.cloudURL = config.cloudURL || 'https://cloud.oxy.so';

    // Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
    this.httpService = new _HttpService.HttpService(config);
  }

  // Test-only utility to reset global tokens between jest tests
  static __resetTokensForTests() {
    _HttpService.HttpService.__resetTokensForTests();
  }

  /**
   * Make a request with all performance optimizations
   * This is the main method for all API calls - ensures authentication and performance features
   */
  async makeRequest(method, url, data, options = {}) {
    return this.httpService.request({
      method,
      url,
      data: method !== 'GET' ? data : undefined,
      params: method === 'GET' ? data : undefined,
      ...options
    });
  }

  // ============================================================================
  // CORE METHODS (HTTP Client, Token Management, Error Handling)
  // ============================================================================

  /**
   * Get the configured Oxy API base URL
   */
  getBaseURL() {
    return this.httpService.getBaseURL();
  }

  /**
   * Get the HTTP service instance
   * Useful for advanced use cases where direct access to the HTTP service is needed
   */
  getClient() {
    return this.httpService;
  }

  /**
   * Get performance metrics
   */
  getMetrics() {
    return this.httpService.getMetrics();
  }

  /**
   * Clear request cache
   */
  clearCache() {
    this.httpService.clearCache();
  }

  /**
   * Clear specific cache entry
   */
  clearCacheEntry(key) {
    this.httpService.clearCacheEntry(key);
  }

  /**
   * Get cache statistics
   */
  getCacheStats() {
    return this.httpService.getCacheStats();
  }

  /**
   * Get the configured Oxy Cloud (file storage/CDN) URL
   */
  getCloudURL() {
    return this.cloudURL;
  }

  /**
   * Set authentication tokens
   */
  setTokens(accessToken, refreshToken = '') {
    this.httpService.setTokens(accessToken, refreshToken);
  }

  /**
   * Clear stored authentication tokens
   */
  clearTokens() {
    this.httpService.clearTokens();
  }

  /**
   * Get the current user ID from the access token
   */
  getCurrentUserId() {
    const accessToken = this.httpService.getAccessToken();
    if (!accessToken) {
      return null;
    }
    try {
      const decoded = (0, _jwtDecode.jwtDecode)(accessToken);
      return decoded.userId || decoded.id || null;
    } catch (error) {
      return null;
    }
  }

  /**
   * Check if the client has a valid access token (public method)
   */
  hasValidToken() {
    return this.httpService.hasAccessToken();
  }

  /**
   * Get the raw access token (for constructing anchor URLs when needed)
   */
  getAccessToken() {
    return this.httpService.getAccessToken();
  }

  /**
   * Wait for authentication to be ready
   * 
   * Optimized for high-scale usage with immediate synchronous check and adaptive polling.
   * Returns immediately if token is already available (0ms delay), otherwise uses
   * adaptive polling that starts fast (50ms) and gradually increases to reduce CPU usage.
   * 
   * @param timeoutMs Maximum time to wait in milliseconds (default: 5000ms)
   * @returns Promise that resolves to true if authentication is ready, false if timeout
   * 
   * @example
   * ```typescript
   * const isReady = await oxyServices.waitForAuth(3000);
   * if (isReady) {
   *   // Proceed with authenticated operations
   * }
   * ```
   */
  async waitForAuth(timeoutMs = 5000) {
    // Immediate synchronous check - no delay if token is ready
    if (this.httpService.hasAccessToken()) {
      return true;
    }
    const startTime = performance.now();
    const maxTime = startTime + timeoutMs;

    // Adaptive polling: start fast, then slow down to reduce CPU usage
    let pollInterval = 50; // Start with 50ms

    while (performance.now() < maxTime) {
      await new Promise(resolve => setTimeout(resolve, pollInterval));
      if (this.httpService.hasAccessToken()) {
        return true;
      }

      // Increase interval after first few checks (adaptive polling)
      // This reduces CPU usage for long waits while maintaining responsiveness
      if (pollInterval < 200) {
        pollInterval = Math.min(pollInterval * 1.5, 200);
      }
    }
    return false;
  }

  /**
   * Execute a function with automatic authentication retry logic
   * This handles the common case where API calls are made before authentication completes
   */
  async withAuthRetry(operation, operationName, options = {}) {
    const {
      maxRetries = 2,
      retryDelay = 1000,
      authTimeoutMs = 5000
    } = options;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        // First attempt: check if we have a token
        if (!this.httpService.hasAccessToken()) {
          if (attempt === 0) {
            // On first attempt, wait briefly for authentication to complete
            const authReady = await this.waitForAuth(authTimeoutMs);
            if (!authReady) {
              throw new _OxyServices.OxyAuthenticationTimeoutError(operationName, authTimeoutMs);
            }
          } else {
            // On retry attempts, fail immediately if no token
            throw new _OxyServices.OxyAuthenticationError(`Authentication required: ${operationName} requires a valid access token.`, 'AUTH_REQUIRED');
          }
        }

        // Execute the operation
        return await operation();
      } catch (error) {
        const isLastAttempt = attempt === maxRetries;
        const errorObj = error && typeof error === 'object' ? error : null;
        const isAuthError = errorObj?.response?.status === 401 || errorObj?.code === 'MISSING_TOKEN' || errorObj?.message?.includes('Authentication') || error instanceof _OxyServices.OxyAuthenticationError;
        if (isAuthError && !isLastAttempt && !(error instanceof _OxyServices.OxyAuthenticationTimeoutError)) {
          await new Promise(resolve => setTimeout(resolve, retryDelay));
          continue;
        }

        // If it's not an auth error, or it's the last attempt, throw the error
        if (error instanceof _OxyServices.OxyAuthenticationError) {
          throw error;
        }
        throw this.handleError(error);
      }
    }

    // This should never be reached, but TypeScript requires it
    throw new _OxyServices.OxyAuthenticationError(`${operationName} failed after ${maxRetries + 1} attempts`);
  }

  /**
   * Validate the current access token with the server
   */
  async validate() {
    if (!this.hasValidToken()) {
      return false;
    }
    try {
      const res = await this.makeRequest('GET', '/api/auth/validate', undefined, {
        cache: false,
        retry: false
      });
      return res.valid === true;
    } catch (error) {
      return false;
    }
  }

  /**
   * Centralized error handling
   */
  handleError(error) {
    const api = (0, _errorUtils.handleHttpError)(error);
    // Ensure we always have a non-empty message
    const message = api.message?.trim() || 'An unexpected error occurred';
    const err = new Error(message);
    err.code = api.code;
    err.status = api.status;
    err.details = api.details;
    return err;
  }

  /**
   * Health check endpoint
   */
  async healthCheck() {
    try {
      return await this.makeRequest('GET', '/health', undefined, {
        cache: false
      });
    } catch (error) {
      throw this.handleError(error);
    }
  }
}
exports.OxyServicesBase = OxyServicesBase;
//# sourceMappingURL=OxyServices.base.js.map