/**
 * Crypto.com OTC 2.0 REST API Client
 * Based on: https://exchange-docs.crypto.com/exchange/v1/rest-ws/index_OTC2.html
 */

import axios, { AxiosInstance, AxiosError } from 'axios';
import { signRequest } from '../auth/signature';
import {
  CryptoComApiRequest,
  CryptoComApiResponse,
  CryptoComRestClientConfig,
  GetOtcInstrumentsResult,
  GetOpenQuoteRequestsParams,
  GetOpenQuoteRequestsResult,
  GetOpenQuotesParams,
  GetOpenQuotesResult,
  GetQuoteRequestHistoryParams,
  GetQuoteRequestHistoryResult,
  GetQuoteHistoryParams,
  GetQuoteHistoryResult,
  GetOpenDealsParams,
  GetOpenDealsResult,
  GetDealHistoryParams,
  GetDealHistoryResult,
  SettleLaterLimit,
  UnsettledAmounts,
} from '../types';
import { API_METHODS, ENVIRONMENTS, DEFAULT_CONFIG, REST_RATE_LIMITS } from '../config';
import { RateLimiter } from '../utils/rate-limiter';
import { withRetry } from '../utils/retry';
import { logger } from '@/infrastructure/monitoring/logger';

export class CryptoComRestClient {
  private client: AxiosInstance;
  private apiKey: string;
  private apiSecret: string;
  private requestIdCounter: number = 0;
  private rateLimiter: RateLimiter;
  private enableRetry: boolean;

  constructor(config: CryptoComRestClientConfig & { enableRetry?: boolean } = {}) {
    this.apiKey = config.apiKey;
    this.apiSecret = config.apiSecret;
    this.enableRetry = config.enableRetry ?? true;
    this.rateLimiter = new RateLimiter(REST_RATE_LIMITS['get-open-quote-requests'] || 1);

    const baseUrl = config.baseUrl || ENVIRONMENTS.production.restBaseUrl;

    this.client = axios.create({
      baseURL: baseUrl,
      timeout: config.timeout || DEFAULT_CONFIG.requestTimeout,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // Add response interceptor for error handling
    this.client.interceptors.response.use(
      (response) => response,
      (error: AxiosError) => {
        if (error.response) {
          logger.error('[CryptoComREST] API Error', {
            status: error.response.status,
            data: error.response.data,
          });
        } else if (error.request) {
          logger.error('[CryptoComREST] Network Error', { message: error.message });
        }
        throw error;
      }
    );
  }

  /**
   * Get the next request ID
   */
  private getNextId(): number {
    return ++this.requestIdCounter;
  }

  /**
   * Make a signed private API request (with rate limiting and optional retry)
   */
  private async privateRequest<T>(
    method: string,
    params?: Record<string, unknown>
  ): Promise<CryptoComApiResponse<T>> {
    const execute = async (): Promise<CryptoComApiResponse<T>> => {
      await this.rateLimiter.acquire();
      return this.executeRequest<T>(method, params);
    };
    return this.enableRetry ? withRetry(execute) : execute();
  }

  /**
   * Execute a single API request
   */
  private async executeRequest<T>(
    method: string,
    params?: Record<string, unknown>
  ): Promise<CryptoComApiResponse<T>> {
    const id = this.getNextId();
    const nonce = Date.now();

    const request: CryptoComApiRequest = {
      id,
      method,
      nonce,
    };

    if (params && Object.keys(params).length > 0) {
      request.params = params;
    }

    const { api_key, sig } = signRequest(
      { id, method, params: request.params, nonce },
      this.apiKey,
      this.apiSecret
    );

    const signedRequest = {
      ...request,
      api_key,
      sig,
    };

    try {
      const response = await this.client.post<CryptoComApiResponse<T>>(
        `/${method}`,
        signedRequest
      );
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error) && error.response?.data) {
        return error.response.data as CryptoComApiResponse<T>;
      }
      throw error;
    }
  }

  /** Disable retry for this client */
  setRetryEnabled(enabled: boolean): void {
    this.enableRetry = enabled;
  }

  // ============================================================================
  // Reference Data API
  // ============================================================================

  /**
   * Get a list of OTC instruments which takers would be able to get a quote
   */
  async getOtcInstruments(): Promise<CryptoComApiResponse<GetOtcInstrumentsResult>> {
    return this.privateRequest<GetOtcInstrumentsResult>(
      API_METHODS.GET_OTC_INSTRUMENTS
    );
  }

  // ============================================================================
  // RFQ Quote Query API
  // ============================================================================

  /**
   * Get open quote requests (status = NEW or ACTIVE)
   */
  async getOpenQuoteRequests(
    params?: GetOpenQuoteRequestsParams
  ): Promise<CryptoComApiResponse<GetOpenQuoteRequestsResult>> {
    return this.privateRequest<GetOpenQuoteRequestsResult>(
      API_METHODS.GET_OPEN_QUOTE_REQUESTS,
      params as Record<string, unknown>
    );
  }

  /**
   * Get open quotes (status = ACTIVE)
   */
  async getOpenQuotes(
    params?: GetOpenQuotesParams
  ): Promise<CryptoComApiResponse<GetOpenQuotesResult>> {
    return this.privateRequest<GetOpenQuotesResult>(
      API_METHODS.GET_OPEN_QUOTES,
      params as Record<string, unknown>
    );
  }

  /**
   * Get quote request history (status = COMPLETED or REJECTED)
   */
  async getQuoteRequestHistory(
    params?: GetQuoteRequestHistoryParams
  ): Promise<CryptoComApiResponse<GetQuoteRequestHistoryResult>> {
    return this.privateRequest<GetQuoteRequestHistoryResult>(
      API_METHODS.GET_QUOTE_REQUEST_HISTORY,
      params as Record<string, unknown>
    );
  }

  /**
   * Get quote history
   */
  async getQuoteHistory(
    params?: GetQuoteHistoryParams
  ): Promise<CryptoComApiResponse<GetQuoteHistoryResult>> {
    return this.privateRequest<GetQuoteHistoryResult>(
      API_METHODS.GET_QUOTE_HISTORY,
      params as Record<string, unknown>
    );
  }

  // ============================================================================
  // Deal Query API
  // ============================================================================

  /**
   * Get open deals (not yet SETTLED)
   */
  async getOpenDeals(
    params: GetOpenDealsParams
  ): Promise<CryptoComApiResponse<GetOpenDealsResult>> {
    return this.privateRequest<GetOpenDealsResult>(
      API_METHODS.GET_OPEN_DEALS,
      params as Record<string, unknown>
    );
  }

  /**
   * Get closed deals history
   */
  async getDealHistory(
    params: GetDealHistoryParams
  ): Promise<CryptoComApiResponse<GetDealHistoryResult>> {
    return this.privateRequest<GetDealHistoryResult>(
      API_METHODS.GET_DEAL_HISTORY,
      params as Record<string, unknown>
    );
  }

  // ============================================================================
  // Settle Later Query API
  // ============================================================================

  /**
   * Get settle later limit for account
   */
  async getSettleLaterLimit(): Promise<CryptoComApiResponse<SettleLaterLimit>> {
    return this.privateRequest<SettleLaterLimit>(
      API_METHODS.GET_SETTLE_LATER_LIMIT
    );
  }

  /**
   * Get pending settlement amounts aggregated by date and instrument
   */
  async getUnsettledAmounts(): Promise<CryptoComApiResponse<UnsettledAmounts>> {
    return this.privateRequest<UnsettledAmounts>(
      API_METHODS.GET_UNSETTLED_AMOUNTS
    );
  }
}

/**
 * Factory function to create a REST client
 */
export function createRestClient(config: CryptoComRestClientConfig): CryptoComRestClient {
  return new CryptoComRestClient(config);
}
