/**
 * Proxy Manager
 *
 * Manages proxy configuration and routing for HTTP requests.
 * Integrates with existing httpClient.ts proxy functionality and
 * provides platform-specific and account-specific proxy support.
 */

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

import { Config } from '@/config/simpleEnv'
import type { GlobalProxyConfig, AccountProxyConfig, ProxyConfigurationResult } from '@/types/httpClientCore'
import { logger } from '@/utils/logger'

export interface ProxyRoute {
  /** Proxy URL */
  url: string

  /** Proxy agent instance */
  agent: HttpsProxyAgent<string>

  /** Whether proxy is enabled */
  enabled: boolean

  /** Priority (higher number = higher priority) */
  priority: number

  /** Health status */
  healthy: boolean

  /** Last health check timestamp */
  lastHealthCheck?: Date
}

export interface ProxySelectionCriteria {
  /** Platform identifier */
  platform?: string

  /** Account identifier */
  accountId?: string

  /** Request URL */
  url?: string

  /** Geographic requirement */
  requiredGeo?: string

  /** Session identifier for sticky sessions */
  sessionId?: string
}

export class ProxyManager {
  private globalProxy: ProxyRoute | null = null
  private platformProxies: Map<string, ProxyRoute> = new Map()
  private accountProxies: Map<string, ProxyRoute> = new Map()
  private sessionStickyMap: Map<string, string> = new Map() // sessionId -> proxyUrl

  constructor() {
    this.initializeFromConfig()
  }

  /**
   * Initialize proxy configuration from existing Config
   */
  private initializeFromConfig(): void {
    try {
      // Initialize global proxy
      if (Config.proxy.isConfigured()) {
        const globalUrl = Config.proxy.getUrl()
        if (globalUrl) {
          this.globalProxy = {
            url: globalUrl,
            agent: new HttpsProxyAgent<string>(globalUrl),
            enabled: true,
            priority: 1,
            healthy: true,
            lastHealthCheck: new Date(),
          }
          logger.info(`ProxyManager: Global proxy initialized: ${globalUrl}`)
        }
      }

      // Initialize platform-specific proxies
      if (Config.proxy.aster.isConfigured()) {
        const asterUrl = Config.proxy.aster.getUrl()
        if (asterUrl) {
          this.platformProxies.set('aster', {
            url: asterUrl,
            agent: new HttpsProxyAgent<string>(asterUrl),
            enabled: true,
            priority: 2,
            healthy: true,
            lastHealthCheck: new Date(),
          })
          logger.info(`ProxyManager: Aster proxy initialized: ${asterUrl}`)
        }
      }

      // Check for other platform-specific proxies from Config
      if ((Config.proxy as any).pacifica?.isConfigured?.()) {
        const pacificaUrl = (Config.proxy as any).pacifica.getUrl()
        if (pacificaUrl) {
          this.platformProxies.set('pacifica', {
            url: pacificaUrl,
            agent: new HttpsProxyAgent<string>(pacificaUrl),
            enabled: true,
            priority: 2,
            healthy: true,
            lastHealthCheck: new Date(),
          })
          logger.info(`ProxyManager: Pacifica proxy initialized: ${pacificaUrl}`)
        }
      }

      if ((Config.proxy as any).binance?.isConfigured?.()) {
        const binanceUrl = (Config.proxy as any).binance.getUrl()
        if (binanceUrl) {
          this.platformProxies.set('binance', {
            url: binanceUrl,
            agent: new HttpsProxyAgent<string>(binanceUrl),
            enabled: true,
            priority: 2,
            healthy: true,
            lastHealthCheck: new Date(),
          })
          logger.info(`ProxyManager: Binance proxy initialized: ${binanceUrl}`)
        }
      }

      logger.info('ProxyManager: Initialization complete')
    } catch (error) {
      logger.error('ProxyManager: Failed to initialize from config', error)
    }
  }

  /**
   * Configure global proxy
   */
  async configureGlobalProxy(config: GlobalProxyConfig): Promise<ProxyConfigurationResult> {
    try {
      if (!config.enabled) {
        this.globalProxy = null
        return {
          success: true,
          proxyUrl: undefined,
          authConfigured: false,
          customHeaders: false,
        }
      }

      const proxyUrl = this.buildProxyUrl(config)
      const agent = new HttpsProxyAgent<string>(proxyUrl)

      this.globalProxy = {
        url: proxyUrl,
        agent,
        enabled: true,
        priority: 1,
        healthy: true,
        lastHealthCheck: new Date(),
      }

      logger.info(`ProxyManager: Global proxy configured: ${proxyUrl}`)

      return {
        success: true,
        proxyUrl,
        authConfigured: Boolean(config.auth),
        customHeaders: Boolean(config.headers),
      }
    } catch (error) {
      logger.error('ProxyManager: Failed to configure global proxy', error)
      return {
        success: false,
        error: error instanceof Error ? error.message : String(error),
      }
    }
  }

  /**
   * Configure account-specific proxy
   */
  async configureAccountProxy(accountId: string, config: AccountProxyConfig): Promise<ProxyConfigurationResult> {
    try {
      if (!config.enabled) {
        this.accountProxies.delete(accountId)
        return {
          success: true,
          accountId,
          proxyUrl: undefined,
          authConfigured: false,
          customHeaders: false,
        }
      }

      const proxyUrl = this.buildProxyUrl(config)
      const agent = new HttpsProxyAgent<string>(proxyUrl)

      this.accountProxies.set(accountId, {
        url: proxyUrl,
        agent,
        enabled: true,
        priority: config.priority || 3,
        healthy: true,
        lastHealthCheck: new Date(),
      })

      logger.info(`ProxyManager: Account proxy configured for ${accountId}: ${proxyUrl}`)

      return {
        success: true,
        accountId,
        proxyUrl,
        authConfigured: Boolean(config.auth),
        customHeaders: Boolean(config.headers),
      }
    } catch (error) {
      logger.error(`ProxyManager: Failed to configure account proxy for ${accountId}`, error)
      return {
        success: false,
        accountId,
        error: error instanceof Error ? error.message : String(error),
      }
    }
  }

  /**
   * Get best proxy for request criteria
   */
  getProxyForRequest(criteria: ProxySelectionCriteria): HttpsProxyAgent<string> | undefined {
    try {
      // Check for session stickiness
      if (criteria.sessionId && this.sessionStickyMap.has(criteria.sessionId)) {
        const stickyProxyUrl = this.sessionStickyMap.get(criteria.sessionId)!
        const stickyProxy = this.findProxyByUrl(stickyProxyUrl)
        if (stickyProxy && stickyProxy.enabled && stickyProxy.healthy) {
          return stickyProxy.agent
        }
      }

      // Priority order: Account > Platform > Global
      const candidateProxies: ProxyRoute[] = []

      // Account-specific proxy (highest priority)
      if (criteria.accountId && this.accountProxies.has(criteria.accountId)) {
        const accountProxy = this.accountProxies.get(criteria.accountId)!
        if (accountProxy.enabled && accountProxy.healthy) {
          candidateProxies.push(accountProxy)
        }
      }

      // Platform-specific proxy (medium priority)
      if (criteria.platform && this.platformProxies.has(criteria.platform)) {
        const platformProxy = this.platformProxies.get(criteria.platform)!
        if (platformProxy.enabled && platformProxy.healthy) {
          candidateProxies.push(platformProxy)
        }
      }

      // Global proxy (lowest priority)
      if (this.globalProxy && this.globalProxy.enabled && this.globalProxy.healthy) {
        candidateProxies.push(this.globalProxy)
      }

      // Select highest priority proxy
      if (candidateProxies.length > 0) {
        const selectedProxy = candidateProxies.sort((a, b) => b.priority - a.priority)[0]

        // Store session stickiness
        if (criteria.sessionId) {
          this.sessionStickyMap.set(criteria.sessionId, selectedProxy.url)
        }

        return selectedProxy.agent
      }

      return undefined
    } catch (error) {
      logger.error('ProxyManager: Failed to get proxy for request', error)
      return undefined
    }
  }

  /**
   * Check proxy health status
   */
  async checkProxyHealth(proxyUrl?: string): Promise<boolean> {
    try {
      if (proxyUrl) {
        const proxy = this.findProxyByUrl(proxyUrl)
        if (!proxy) return false

        // Perform health check
        const isHealthy = await this.performHealthCheck(proxy)
        proxy.healthy = isHealthy
        proxy.lastHealthCheck = new Date()
        return isHealthy
      } else {
        // Check all proxies
        const allProxies = [
          ...(this.globalProxy ? [this.globalProxy] : []),
          ...Array.from(this.platformProxies.values()),
          ...Array.from(this.accountProxies.values()),
        ]

        const results = await Promise.all(
          allProxies.map(async proxy => {
            const isHealthy = await this.performHealthCheck(proxy)
            proxy.healthy = isHealthy
            proxy.lastHealthCheck = new Date()
            return isHealthy
          }),
        )

        return results.every(Boolean)
      }
    } catch (error) {
      logger.error('ProxyManager: Health check failed', error)
      return false
    }
  }

  /**
   * Get proxy statistics
   */
  getProxyStats(): {
    global?: ProxyRoute
    platforms: Record<string, ProxyRoute>
    accounts: Record<string, ProxyRoute>
    sessionStickiness: Record<string, string>
    totalProxies: number
    healthyProxies: number
  } {
    const platformProxies = Object.fromEntries(this.platformProxies)
    const accountProxies = Object.fromEntries(this.accountProxies)
    const sessionStickiness = Object.fromEntries(this.sessionStickyMap)

    const allProxies = [
      ...(this.globalProxy ? [this.globalProxy] : []),
      ...Array.from(this.platformProxies.values()),
      ...Array.from(this.accountProxies.values()),
    ]

    return {
      global: this.globalProxy || undefined,
      platforms: platformProxies,
      accounts: accountProxies,
      sessionStickiness,
      totalProxies: allProxies.length,
      healthyProxies: allProxies.filter(p => p.healthy).length,
    }
  }

  /**
   * Clear session stickiness
   */
  clearSessionStickiness(sessionId?: string): void {
    if (sessionId) {
      this.sessionStickyMap.delete(sessionId)
    } else {
      this.sessionStickyMap.clear()
    }
  }

  /**
   * Refresh configuration from Config
   */
  refreshFromConfig(): void {
    logger.info('ProxyManager: Refreshing configuration')
    this.globalProxy = null
    this.platformProxies.clear()
    this.accountProxies.clear()
    this.initializeFromConfig()
  }

  // Private helper methods

  private buildProxyUrl(config: GlobalProxyConfig | AccountProxyConfig): string {
    if (config.url) {
      return config.url
    }

    if (!config.host || !config.port) {
      throw new Error('Proxy configuration must include either url or host/port')
    }

    const protocol = config.protocol || 'http'
    const auth = config.auth ? `${config.auth.username}:${config.auth.password}@` : ''
    return `${protocol}://${auth}${config.host}:${config.port}`
  }

  private findProxyByUrl(url: string): ProxyRoute | undefined {
    if (this.globalProxy && this.globalProxy.url === url) {
      return this.globalProxy
    }

    for (const proxy of this.platformProxies.values()) {
      if (proxy.url === url) return proxy
    }

    for (const proxy of this.accountProxies.values()) {
      if (proxy.url === url) return proxy
    }

    return undefined
  }

  private async performHealthCheck(proxy: ProxyRoute): Promise<boolean> {
    try {
      // Simple health check - try to connect through proxy
      const testUrl = 'https://httpbin.org/ip'
      const controller = new AbortController()
      const timeoutId = setTimeout(() => controller.abort(), 5000)

      const response = await fetch(testUrl, {
        agent: proxy.agent,
        signal: controller.signal,
      })

      clearTimeout(timeoutId)
      return response.ok
    } catch {
      return false
    }
  }
}

export default ProxyManager
