import { HttpsProxyAgent } from 'https-proxy-agent'

import { Config } from '@/config/simpleEnv'

import { logger } from './logger'

/**
 * HTTP 请求选项
 */
export interface HttpRequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
  headers?: Record<string, string>
  body?: any
  timeout?: number
  signal?: AbortSignal
  retries?: number
  retryDelay?: number
  useProxy?: boolean
  exchange?: 'aster' | 'pacifica' | 'binance' // 交易所标识，用于选择专用代理
  accountId?: string // 账户标识，支持同一交易所多账户
}

/**
 * HTTP 响应接口
 */
export interface HttpResponse<T = any> {
  status: number
  statusText: string
  ok: boolean
  data: T
  headers: Headers
}

/**
 * 统一的HTTP客户端，支持proxy和重试
 */
export class HttpClient {
  private static instance: HttpClient
  private proxyAgent: HttpsProxyAgent<string> | undefined
  private defaultTimeout = 30000
  private defaultRetries = 3
  private defaultRetryDelay = 1000

  constructor() {
    this.initializeProxy()
  }

  static getInstance(): HttpClient {
    if (!HttpClient.instance) {
      HttpClient.instance = new HttpClient()
    }
    return HttpClient.instance
  }

  private initializeProxy(): void {
    if (Config.proxy.isAnyConfigured()) {
      logger.info('HTTP代理系统已初始化')
      if (Config.proxy.isConfigured()) {
        logger.info(`全局代理: ${Config.proxy.protocol()}://${Config.proxy.host()}:${Config.proxy.port()}`)
      }
      if (Config.proxy.aster.isConfigured()) {
        logger.info(
          `Aster专用代理: ${Config.proxy.aster.protocol()}://${Config.proxy.aster.host()}:${Config.proxy.aster.port()}`,
        )
      }
    } else {
      logger.info('HTTP代理未配置，使用直连')
    }
  }

  /**
   * 重新初始化代理 (当环境变量发生变化时调用)
   */
  reinitializeProxy(): void {
    this.proxyAgent = undefined
    this.initializeProxy()
  }

  /**
   * 检查HTTP状态码是否应该重试
   */
  private shouldRetry(status: number): boolean {
    return status >= 500 || status === 429 || status === 408 || status === 0
  }

  /**
   * 等待指定时间
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }

  /**
   * 发送HTTP请求
   */
  async request<T = any>(url: string, options: HttpRequestOptions = {}): Promise<HttpResponse<T>> {
    const {
      method = 'GET',
      headers = {},
      body,
      timeout = this.defaultTimeout,
      signal,
      retries = this.defaultRetries,
      retryDelay = this.defaultRetryDelay,
      useProxy = true,
      exchange,
    } = options

    const maxRetries = Math.max(0, retries)
    let lastError: Error

    // 请求配置
    const fetchOptions: RequestInit = {
      method,
      headers: {
        'User-Agent': 'Binance-Multi-Exchange-Client/1.0',
        ...headers,
      },
      signal,
    }

    // 添加请求体
    if (body && method !== 'GET') {
      if (typeof body === 'object') {
        fetchOptions.body = JSON.stringify(body)
        fetchOptions.headers = {
          'Content-Type': 'application/json',
          ...fetchOptions.headers,
        }
      } else {
        fetchOptions.body = body
      }
    }

    // 添加代理（仅在Node.js环境中）
    if (useProxy && typeof global !== 'undefined') {
      const proxyUrl = Config.proxy.getUrl(exchange as any)
      if (proxyUrl) {
        const proxyAgent = new HttpsProxyAgent<string>(proxyUrl)
        ;(fetchOptions as any).agent = proxyAgent

        if (Config.isDev()) {
          logger.debug(`使用代理发送HTTP请求`, {
            exchange: exchange || 'global',
            proxyHost: exchange === 'aster' ? Config.proxy.aster.host() : Config.proxy.host(),
            method,
            url: url.length > 100 ? url.substring(0, 100) + '...' : url,
          })
        }
      }
    }

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        // 创建超时控制
        const controller = new AbortController()
        const timeoutId = setTimeout(() => controller.abort(), timeout)
        const requestSignal = signal || controller.signal

        const startTime = Date.now()
        const response = await fetch(url, {
          ...fetchOptions,
          signal: requestSignal,
        })

        clearTimeout(timeoutId)
        const duration = Date.now() - startTime

        // 读取响应数据
        const contentType = response.headers.get('content-type') || ''
        let data: T

        if (contentType.includes('application/json')) {
          data = (await response.json()) as T
        } else {
          data = (await response.text()) as T
        }

        // 记录请求日志
        if (Config.isDev()) {
          logger.debug(`HTTP ${method} ${url}`, {
            status: response.status,
            duration: `${duration}ms`,
            attempt: attempt + 1,
            proxy: useProxy && this.proxyAgent ? 'enabled' : 'disabled',
          })
        }

        // 检查是否需要重试
        if (!response.ok && this.shouldRetry(response.status) && attempt < maxRetries) {
          const jitter = Math.random() * 500 // 0-500ms随机延迟
          const delay = retryDelay * Math.pow(2, attempt) + jitter

          logger.warn(`HTTP请求失败，将在${Math.round(delay)}ms后重试`, {
            url,
            status: response.status,
            attempt: attempt + 1,
            maxRetries: maxRetries + 1,
          })

          await this.sleep(delay)
          continue
        }

        return {
          status: response.status,
          statusText: response.statusText,
          ok: response.ok,
          data,
          headers: response.headers,
        }
      } catch (error: any) {
        lastError = error
        const isTimeout = error.name === 'AbortError'
        const isNetworkError = error.message?.includes('fetch')

        // 记录错误日志
        logger.error(`HTTP请求异常`, {
          url,
          method,
          attempt: attempt + 1,
          error: error.message,
          isTimeout,
          isNetworkError,
        })

        // 检查是否需要重试
        if ((isTimeout || isNetworkError) && attempt < maxRetries) {
          const jitter = Math.random() * 500
          const delay = retryDelay * Math.pow(2, attempt) + jitter

          logger.warn(`网络错误，将在${Math.round(delay)}ms后重试`, {
            url,
            attempt: attempt + 1,
            maxRetries: maxRetries + 1,
          })

          await this.sleep(delay)
          continue
        }

        // 不能重试，抛出错误
        throw error
      }
    }

    // 所有重试都失败了
    throw lastError || new Error(`HTTP请求失败，已重试${maxRetries}次`)
  }

  /**
   * GET 请求
   */
  async get<T = any>(url: string, options: Omit<HttpRequestOptions, 'method' | 'body'> = {}): Promise<HttpResponse<T>> {
    return this.request<T>(url, { ...options, method: 'GET' })
  }

  /**
   * POST 请求
   */
  async post<T = any>(
    url: string,
    body?: any,
    options: Omit<HttpRequestOptions, 'method' | 'body'> = {},
  ): Promise<HttpResponse<T>> {
    return this.request<T>(url, { ...options, method: 'POST', body })
  }

  /**
   * PUT 请求
   */
  async put<T = any>(
    url: string,
    body?: any,
    options: Omit<HttpRequestOptions, 'method' | 'body'> = {},
  ): Promise<HttpResponse<T>> {
    return this.request<T>(url, { ...options, method: 'PUT', body })
  }

  /**
   * DELETE 请求
   */
  async delete<T = any>(
    url: string,
    options: Omit<HttpRequestOptions, 'method' | 'body'> = {},
  ): Promise<HttpResponse<T>> {
    return this.request<T>(url, { ...options, method: 'DELETE' })
  }
}

/**
 * 全局HTTP客户端实例
 */
export const httpClient = HttpClient.getInstance()

/**
 * 便捷方法：使用代理的fetch
 */
export async function fetchWithProxy<T = any>(url: string, options: HttpRequestOptions = {}): Promise<HttpResponse<T>> {
  return httpClient.request<T>(url, options)
}

/**
 * 便捷方法：GET请求with proxy
 */
export async function getWithProxy<T = any>(
  url: string,
  options: Omit<HttpRequestOptions, 'method' | 'body'> = {},
): Promise<T> {
  const response = await httpClient.get<T>(url, options)
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`)
  }
  return response.data
}

/**
 * 便捷方法：POST请求with proxy
 */
export async function postWithProxy<T = any>(
  url: string,
  body?: any,
  options: Omit<HttpRequestOptions, 'method' | 'body'> = {},
): Promise<T> {
  const response = await httpClient.post<T>(url, body, options)
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`)
  }
  return response.data
}
