import { ASPAObject, AdoptionSnapshot, CollectorOptions } from './types'

export class ASPACollector {
  private readonly ripeStatUrl = 'https://stat.ripe.net/api/v1'
  private readonly requestQueue: Array<() => Promise<void>> = []
  private isProcessing = false

  collect(asns: number[], options: CollectorOptions = { maxRetries: 3, timeoutMs: 5000, rateLimit: 100 }): Promise<AdoptionSnapshot[]> {
    return this.collectWithRateLimit(asns, options)
  }

  private async collectWithRateLimit(
    asns: number[],
    options: CollectorOptions
  ): Promise<AdoptionSnapshot[]> {
    const snapshots: AdoptionSnapshot[] = []
    const delayMs = Math.ceil(1000 / options.rateLimit)

    for (const asn of asns) {
      const snapshot = await this.fetchASPAForASN(asn, options)
      snapshots.push(snapshot)
      await this.delay(delayMs)
    }

    return snapshots
  }

  private async fetchASPAForASN(asn: number, options: CollectorOptions): Promise<AdoptionSnapshot> {
    let lastError: Error | null = null

    for (let attempt = 0; attempt <= options.maxRetries; attempt++) {
      try {
        const providers = await this.queryASPAProviders(asn, options.timeoutMs)
        const hasASPA = providers.length > 0

        return {
          asn,
          hasASPA,
          providers,
          region: this.inferRegion(asn),
        }
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error))

        if (attempt < options.maxRetries) {
          const delayMs = Math.pow(2, attempt) * 1000
          await this.delay(delayMs)
        }
      }
    }

    return {
      asn,
      hasASPA: false,
      providers: [],
      region: this.inferRegion(asn),
    }
  }

  private async queryASPAProviders(asn: number, timeoutMs: number): Promise<number[]> {
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs)

    try {
      const url = `${this.ripeStatUrl}/as/${asn}/aspa`
      const response = await fetch(url, {
        signal: controller.signal,
        headers: {
          'Accept': 'application/json',
        },
      })

      if (!response.ok) {
        if (response.status === 404) {
          return []
        }
        throw new Error(`RIPE Stat API error: ${response.status}`)
      }

      const data = (await response.json()) as {
        data?: {
          aspa?: Array<{ provider_asn: number }>
        }
      }
      const aspaData = data.data?.aspa || []

      return Array.from(new Set(aspaData.map((obj) => obj.provider_asn)))
    } finally {
      clearTimeout(timeoutId)
    }
  }

  private inferRegion(asn: number): string {
    if (asn <= 1000) return 'RESERVED'
    if (asn <= 9999) return 'ARIN'
    if (asn <= 39999) return 'RIPE'
    if (asn <= 65534) return 'RIPENCC'
    if (asn <= 4199999999) return 'ARIN'
    if (asn <= 4294967294) return 'RESERVED'
    return 'UNKNOWN'
  }

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

  getQueueSize(): number {
    return this.requestQueue.length
  }
}
