/**
 * Binance HTTP Adapter
 *
 * Platform adapter for Binance exchange with credential manager integration.
 * Implements HMAC-SHA256 signature authentication and platform-specific request handling.
 */

import type {
  IPlatformAdapter,
  PlatformRequest,
  PlatformResponse,
  PreparedRequest,
  ProxyConfig,
  ProxyStatus,
  PlatformHealthStatus,
  PlatformConfig,
  RawResponse
} from '@/types/platformAdapter'
import { AuthenticationManager, type CredentialManagerInterface } from '@/core/http-client/AuthenticationManager'

export class BinanceHttpAdapter implements IPlatformAdapter {
  public readonly platform = 'binance'
  public readonly baseUrl: string

  private config: PlatformConfig
  private authManager: AuthenticationManager
  private proxyConfig: ProxyConfig | null = null
  private lastHealthCheck: Date = new Date()

  constructor(config: PlatformConfig, credentialManager?: CredentialManagerInterface) {
    this.config = config
    this.baseUrl = config.baseUrl
    this.authManager = new AuthenticationManager(credentialManager)

    if (!this.validateConfig()) {
      throw new Error('Invalid Binance configuration')
    }
  }

  /**
   * Execute HTTP request through Binance adapter
   */
  async request<T = any>(request: PlatformRequest): Promise<PlatformResponse<T>> {
    const startTime = Date.now()

    try {
      // Prepare request with authentication
      const prepared = await this.prepareRequest(request)

      // Execute HTTP request
      const response = await this.executeRequest<T>(prepared)

      // Process and return response
      return this.processResponse<T>(response)
    } catch (error) {
      const duration = Date.now() - startTime

      return {
        status: 500,
        data: null as T,
        headers: {},
        success: false,
        error: {
          type: 'network_error',
          message: error instanceof Error ? error.message : String(error),
          code: 'REQUEST_FAILED'
        },
        metadata: {
          platform: this.platform,
          requestId: this.generateRequestId(),
          duration,
          timestamp: new Date(),
          statusCode: 500,
          responseSize: 0
        }
      }
    }
  }

  /**
   * Prepare request with authentication and Binance-specific formatting
   */
  async prepareRequest(request: PlatformRequest): Promise<PreparedRequest> {
    // Create authentication context
    const authContext = this.authManager.createAuthContext(
      request.accountId,
      this.platform,
      request.options?.requiresAuth ?? true
    )

    // Add Binance-specific parameters
    if (request.options?.requiresAuth) {
      request.params = {
        ...request.params,
        timestamp: Date.now(),
        recvWindow: 5000
      }
    }

    // Prepare request with authentication
    const prepared = await this.authManager.prepareRequest(request, authContext)

    // Add Binance-specific headers
    prepared.headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
      'User-Agent': 'binance-http-adapter/1.0.0',
      'X-Platform': 'binance',
      ...prepared.headers
    }

    // Format path for Binance API
    prepared.path = this.formatApiPath(prepared.path)

    return prepared
  }

  /**
   * Set proxy configuration
   */
  async setProxyConfig(proxyConfig: ProxyConfig | null): Promise<void> {
    this.proxyConfig = proxyConfig
  }

  /**
   * Get current proxy status
   */
  async getProxyStatus(): Promise<ProxyStatus> {
    if (!this.proxyConfig || !this.proxyConfig.enabled) {
      return { enabled: false }
    }

    return {
      enabled: true,
      host: this.proxyConfig.host,
      port: this.proxyConfig.port
    }
  }

  /**
   * Process raw response from HTTP client
   */
  async processResponse<T>(response: RawResponse): Promise<PlatformResponse<T>> {
    const processed: PlatformResponse<T> = {
      status: response.status,
      data: this.parseResponseData<T>(response.body),
      headers: response.headers,
      success: response.ok && response.status >= 200 && response.status < 300,
      metadata: {
        platform: this.platform,
        requestId: this.generateRequestId(),
        duration: 0, // Will be set by caller
        timestamp: new Date(),
        statusCode: response.status,
        responseSize: this.calculateResponseSize(response.body),
        resourcesCleanedUp: true
      }
    }

    // Handle Binance-specific error responses
    if (!processed.success) {
      processed.error = this.parseError(response)
    }

    return processed
  }

  /**
   * Check platform health status
   */
  async checkHealth(): Promise<PlatformHealthStatus> {
    const startTime = Date.now()

    try {
      // Make a lightweight health check request
      const healthRequest: PlatformRequest = {
        accountId: 'health-check',
        method: 'GET',
        path: '/api/v3/ping',
        options: { requiresAuth: false, timeout: 5000 }
      }

      const response = await this.request(healthRequest)
      const responseTime = Date.now() - startTime

      this.lastHealthCheck = new Date()

      return {
        platform: this.platform,
        status: response.success ? 'healthy' : 'unhealthy',
        responseTime,
        lastCheck: this.lastHealthCheck
      }
    } catch (error) {
      const responseTime = Date.now() - startTime

      return {
        platform: this.platform,
        status: 'unhealthy',
        responseTime,
        lastCheck: new Date()
      }
    }
  }

  /**
   * Get platform configuration
   */
  getConfig(): PlatformConfig {
    return { ...this.config }
  }

  /**
   * Validate platform configuration
   */
  validateConfig(): boolean {
    if (!this.config) {
      return false
    }

    if (this.config.platform !== 'binance') {
      return false
    }

    if (!this.config.baseUrl || !this.isValidUrl(this.config.baseUrl)) {
      return false
    }

    if (!this.config.authConfig || this.config.authConfig.type !== 'signature') {
      return false
    }

    if (this.config.authConfig.algorithm !== 'HMAC-SHA256') {
      return false
    }

    return true
  }

  /**
   * Set credential manager
   */
  setCredentialManager(credentialManager: CredentialManagerInterface): void {
    this.authManager.setCredentialManager(credentialManager)
  }

  /**
   * Update configuration
   */
  updateConfig(config: Partial<PlatformConfig>): void {
    this.config = { ...this.config, ...config }

    if (config.baseUrl) {
      this.config.baseUrl = config.baseUrl
    }
  }

  private async executeRequest<T>(request: PreparedRequest): Promise<RawResponse> {
    const url = new URL(request.path, this.baseUrl)

    // For Binance, parameters are added differently based on method
    let body: string | undefined
    let urlWithParams = url.toString()

    if (request.method === 'GET' && request.params) {
      // Add query parameters for GET requests
      Object.entries(request.params).forEach(([key, value]) => {
        url.searchParams.append(key, String(value))
      })
      urlWithParams = url.toString()
    } else if (['POST', 'PUT', 'DELETE'].includes(request.method) && request.params) {
      // Add parameters as form data for non-GET requests
      const formData = new URLSearchParams()
      Object.entries(request.params).forEach(([key, value]) => {
        formData.append(key, String(value))
      })
      body = formData.toString()
    }

    const fetchOptions: RequestInit = {
      method: request.method,
      headers: request.headers,
      ...(body && { body })
    }

    // Add timeout
    const controller = new AbortController()
    const timeout = request.options?.timeout || this.config.timeouts?.read || 30000

    const timeoutId = setTimeout(() => controller.abort(), timeout)

    try {
      const response = await fetch(urlWithParams, {
        ...fetchOptions,
        signal: controller.signal
      })

      clearTimeout(timeoutId)

      const responseBody = await response.text()

      return {
        status: response.status,
        headers: this.headersToObject(response.headers),
        body: responseBody,
        ok: response.ok
      }
    } catch (error) {
      clearTimeout(timeoutId)
      throw error
    }
  }

  private formatApiPath(path: string): string {
    // Ensure path starts with appropriate Binance API version
    if (!path.startsWith('/api/')) {
      // Default to v3 for spot trading
      return `/api/v3${path.startsWith('/') ? path : `/${path}`}`
    }
    return path
  }

  private parseResponseData<T>(body: string | any): T {
    if (typeof body === 'string') {
      try {
        return JSON.parse(body)
      } catch {
        return body as T
      }
    }
    return body
  }

  private parseError(response: RawResponse): { type: string; message: string; code: string } {
    let errorMessage = 'Unknown error'
    let errorCode = 'UNKNOWN_ERROR'

    try {
      const errorData = this.parseResponseData(response.body)

      if (errorData && typeof errorData === 'object') {
        errorMessage = errorData.msg || errorData.message || errorMessage
        errorCode = errorData.code || String(errorData.code) || errorCode
      }
    } catch {
      errorMessage = `HTTP ${response.status}: ${response.body}`
    }

    let errorType: 'client_error' | 'server_error' | 'network_error' | 'auth_error' = 'server_error'

    if (response.status >= 400 && response.status < 500) {
      errorType = response.status === 401 || response.status === 403 ? 'auth_error' : 'client_error'
    }

    // Handle specific Binance error codes
    if (errorCode.includes('API-key') || errorCode.includes('signature') || errorCode.includes('auth')) {
      errorType = 'auth_error'
    }

    return {
      type: errorType,
      message: errorMessage,
      code: errorCode
    }
  }

  private calculateResponseSize(body: string | any): number {
    if (typeof body === 'string') {
      return Buffer.byteLength(body, 'utf8')
    }
    return Buffer.byteLength(JSON.stringify(body), 'utf8')
  }

  private generateRequestId(): string {
    return `binance_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`
  }

  private headersToObject(headers: Headers): Record<string, string> {
    const obj: Record<string, string> = {}
    headers.forEach((value, key) => {
      obj[key] = value
    })
    return obj
  }

  private isValidUrl(url: string): boolean {
    try {
      new URL(url)
      return true
    } catch {
      return false
    }
  }
}

export default BinanceHttpAdapter