import { SamplingResult, SamplerOptions } from './types'

interface RegionalDistribution {
  region: string
  asnCount: number
  weight: number
}

/**
 * Stratified random sampling of ASNs weighted by regional distribution
 * Global AS number space: 0-4294967295 (2^32 - 1)
 * Private: 64512-65534, 4200000000-4294967294
 */
export class ASNSampler {
  private readonly regions: RegionalDistribution[] = [
    { region: 'APNIC', asnCount: 180000, weight: 0.35 },
    { region: 'RIPE', asnCount: 150000, weight: 0.28 },
    { region: 'ARIN', asnCount: 140000, weight: 0.26 },
    { region: 'LACNIC', asnCount: 45000, weight: 0.08 },
    { region: 'AFRINIC', asnCount: 20000, weight: 0.03 },
  ]

  private publicASNRanges = [
    { min: 1, max: 64511 },
    { min: 65535, max: 4199999999 },
    { min: 4294967295, max: 4294967295 },
  ]

  sample(targetCount: number, options: SamplerOptions = { stratifiedByRegion: true, minASNsPerRegion: 50 }): SamplingResult {
    const timestamp = new Date()

    if (options.stratifiedByRegion) {
      return this.stratifiedSample(targetCount, options)
    }

    return this.simpleRandomSample(targetCount, timestamp)
  }

  private stratifiedSample(targetCount: number, options: SamplerOptions): SamplingResult {
    const timestamp = new Date()
    const asns = new Set<number>()
    const weightedByRegion: Record<string, number> = {}

    for (const region of this.regions) {
      const regionCount = Math.max(
        options.minASNsPerRegion!,
        Math.floor(targetCount * region.weight)
      )
      weightedByRegion[region.region] = regionCount

      for (let i = 0; i < regionCount && asns.size < targetCount; i++) {
        const asn = this.generateRandomASN(options.seed)
        asns.add(asn)
      }
    }

    return {
      asns: Array.from(asns),
      totalSampled: asns.size,
      weightedByRegion,
      timestamp,
    }
  }

  private simpleRandomSample(targetCount: number, timestamp: Date): SamplingResult {
    const asns = new Set<number>()

    while (asns.size < targetCount) {
      const asn = this.generateRandomASN()
      asns.add(asn)
    }

    return {
      asns: Array.from(asns),
      totalSampled: asns.size,
      weightedByRegion: {},
      timestamp,
    }
  }

  private generateRandomASN(seed?: number): number {
    let random: number

    if (seed !== undefined) {
      // Deterministic pseudo-random with seed
      const x = Math.sin(seed) * 10000
      random = x - Math.floor(x)
    } else {
      random = Math.random()
    }

    const rangeIndex = Math.floor(random * this.publicASNRanges.length)
    const range = this.publicASNRanges[rangeIndex]
    const rangeSize = range.max - range.min + 1

    const asn = Math.floor(range.min + random * rangeSize)

    // Skip private ASN ranges
    if (asn >= 64512 && asn <= 65534) {
      return this.generateRandomASN()
    }
    if (asn >= 4200000000 && asn <= 4294967294) {
      return this.generateRandomASN()
    }

    return asn
  }

  getRegionalDistribution(): RegionalDistribution[] {
    return [...this.regions]
  }

  validateASN(asn: number): boolean {
    if (asn < 0 || asn > 4294967295) return false
    if (asn >= 64512 && asn <= 65534) return false
    if (asn >= 4200000000 && asn <= 4294967294) return false
    return true
  }
}
