import axios, { AxiosError } from 'axios'
import type { Channel, MonitoringResult, ChannelStatus } from '../../shared/types'

interface MonitoringConfig {
  pingInterval: number
  streamCheckInterval: number
  timeout: number
  retryCount: number
}

type MonitoringCallback = (result: MonitoringResult) => void

export class StreamChecker {
  private config: MonitoringConfig
  private channels: Channel[] = []
  private callback: MonitoringCallback
  private intervalId: NodeJS.Timeout | null = null
  private running = false

  constructor(config: MonitoringConfig, callback: MonitoringCallback) {
    this.config = config
    this.callback = callback
  }

  setChannels(channels: Channel[]): void {
    this.channels = channels
  }

  start(): void {
    if (this.running) return
    
    this.running = true
    this.checkAllChannels()
    
    this.intervalId = setInterval(() => {
      this.checkAllChannels()
    }, this.config.streamCheckInterval * 1000)
  }

  stop(): void {
    this.running = false
    if (this.intervalId) {
      clearInterval(this.intervalId)
      this.intervalId = null
    }
  }

  isRunning(): boolean {
    return this.running
  }

  updateConfig(config: MonitoringConfig): void {
    this.config = config
    
    // Restart if running with new interval
    if (this.running) {
      this.stop()
      this.start()
    }
  }

  private async checkAllChannels(): Promise<void> {
    for (const channel of this.channels) {
      if (!this.running) break
      
      const result = await this.checkChannel(channel)
      this.callback(result)
      
      // Small delay between checks to prevent overwhelming
      await this.delay(500)
    }
  }

  async checkChannel(channel: Channel): Promise<MonitoringResult> {
    const startTime = Date.now()
    
    // Notify checking status
    this.callback({
      channelId: channel.id,
      timestamp: startTime,
      status: 'checking'
    })

    let lastError: string | undefined
    let errorType: MonitoringResult['errorType']
    let httpStatus: number | undefined

    for (let attempt = 0; attempt < this.config.retryCount; attempt++) {
      try {
        const response = await axios.head(channel.url, {
          timeout: this.config.timeout,
          headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
          },
          maxRedirects: 5,
          validateStatus: () => true // Accept any status
        })

        httpStatus = response.status
        const latency = Date.now() - startTime

        // Check for successful response
        if (response.status >= 200 && response.status < 400) {
          return {
            channelId: channel.id,
            timestamp: Date.now(),
            status: 'online',
            latency,
            httpStatus
          }
        }

        // HTTP error status
        lastError = `HTTP ${response.status} ${response.statusText}`
        errorType = 'HTTP_ERROR'

        if (response.status === 404) {
          return {
            channelId: channel.id,
            timestamp: Date.now(),
            status: 'offline',
            httpStatus,
            error: lastError,
            errorType
          }
        }

      } catch (error) {
        const axiosError = error as AxiosError
        
        if (axiosError.code === 'ECONNRESET') {
          lastError = 'ERR_CONNECTION_RESET'
          errorType = 'CONNRESET'
        } else if (axiosError.code === 'ECONNREFUSED') {
          lastError = 'ERR_CONNECTION_REFUSED'
          errorType = 'CONNREFUSED'
        } else if (axiosError.code === 'ETIMEDOUT' || axiosError.code === 'ECONNABORTED') {
          lastError = 'Connection timeout'
          errorType = 'TIMEOUT'
        } else if (axiosError.code === 'ENOTFOUND') {
          lastError = 'DNS lookup failed'
          errorType = 'UNKNOWN'
        } else {
          lastError = axiosError.message
          errorType = 'UNKNOWN'
        }
      }

      // Wait before retry
      if (attempt < this.config.retryCount - 1) {
        await this.delay(1000 * (attempt + 1)) // Exponential backoff
      }
    }

    // All retries failed
    return {
      channelId: channel.id,
      timestamp: Date.now(),
      status: 'error',
      httpStatus,
      error: lastError,
      errorType
    }
  }

  /**
   * Check if M3U8 playlist is valid and can be parsed
   */
  async checkM3u8Content(url: string): Promise<{ valid: boolean; error?: string }> {
    try {
      const response = await axios.get(url, {
        timeout: this.config.timeout,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        },
        maxRedirects: 5
      })

      const content = response.data as string
      
      // Check for M3U8 header
      if (!content.includes('#EXTM3U')) {
        return { valid: false, error: 'Not a valid M3U8 file' }
      }

      // Check for segments or variants
      const hasContent = content.includes('#EXTINF') || content.includes('#EXT-X-STREAM-INF')
      
      if (!hasContent) {
        return { valid: false, error: 'M3U8 file has no content' }
      }

      return { valid: true }
    } catch (error) {
      const axiosError = error as AxiosError
      return { valid: false, error: axiosError.message }
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

