import * as net from 'net'
import * as dns from 'dns'
import { promisify } from 'util'
import type { PingResult } from '../../shared/types'

const dnsLookup = promisify(dns.lookup)

export class PingService {
  private timeout: number

  constructor(timeout: number = 5000) {
    this.timeout = timeout
  }

  /**
   * Ping a host using TCP connection (works without admin privileges)
   */
  async ping(host: string, port: number = 80): Promise<PingResult> {
    const startTime = Date.now()
    
    try {
      // Resolve hostname first
      let ip = host
      if (!this.isIpAddress(host)) {
        try {
          const result = await dnsLookup(host)
          ip = result.address
        } catch (dnsError) {
          return {
            host,
            alive: false,
            error: `DNS lookup failed: ${(dnsError as Error).message}`
          }
        }
      }

      // TCP connection test
      const alive = await this.tcpPing(ip, port)
      const time = Date.now() - startTime

      return {
        host,
        alive,
        time: alive ? time : undefined,
        error: alive ? undefined : 'Connection failed'
      }
    } catch (error) {
      return {
        host,
        alive: false,
        error: (error as Error).message
      }
    }
  }

  private tcpPing(host: string, port: number): Promise<boolean> {
    return new Promise((resolve) => {
      const socket = new net.Socket()
      
      socket.setTimeout(this.timeout)

      socket.on('connect', () => {
        socket.destroy()
        resolve(true)
      })

      socket.on('timeout', () => {
        socket.destroy()
        resolve(false)
      })

      socket.on('error', () => {
        socket.destroy()
        resolve(false)
      })

      socket.connect(port, host)
    })
  }

  /**
   * Ping multiple hosts in parallel
   */
  async pingMultiple(hosts: string[], port: number = 80): Promise<PingResult[]> {
    const results = await Promise.all(
      hosts.map(host => this.ping(host, port))
    )
    return results
  }

  /**
   * Check if string is an IP address
   */
  private isIpAddress(str: string): boolean {
    const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/
    const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/
    return ipv4Regex.test(str) || ipv6Regex.test(str)
  }

  /**
   * Extract host from URL
   */
  static extractHost(url: string): string {
    try {
      const parsed = new URL(url)
      return parsed.hostname
    } catch {
      return url
    }
  }

  updateTimeout(timeout: number): void {
    this.timeout = timeout
  }
}

