import { AdoptionSnapshot, RegionalStats, IXPStats, AggregatorOptions } from './types'

export class AdoptionAggregator {
  aggregate(
    snapshots: AdoptionSnapshot[],
    options: AggregatorOptions = { includeRegions: true, includeIXPs: true }
  ): {
    overall: { total: number; withASPA: number; coverage: number }
    regions: RegionalStats[]
    ixps: IXPStats[]
  } {
    const overall = this.calculateOverall(snapshots)
    const regions = options.includeRegions ? this.aggregateByRegion(snapshots) : []
    const ixps = options.includeIXPs ? this.aggregateByIXP(snapshots) : []

    return { overall, regions, ixps }
  }

  private calculateOverall(snapshots: AdoptionSnapshot[]): { total: number; withASPA: number; coverage: number } {
    const total = snapshots.length
    const withASPA = snapshots.filter((s) => s.hasASPA).length
    const coverage = total > 0 ? Math.round((withASPA / total) * 10000) / 100 : 0

    return { total, withASPA, coverage }
  }

  private aggregateByRegion(snapshots: AdoptionSnapshot[]): RegionalStats[] {
    const regionMap = new Map<string, AdoptionSnapshot[]>()

    for (const snapshot of snapshots) {
      if (!regionMap.has(snapshot.region)) {
        regionMap.set(snapshot.region, [])
      }
      regionMap.get(snapshot.region)!.push(snapshot)
    }

    const results: RegionalStats[] = []

    for (const [region, snapshotsInRegion] of regionMap.entries()) {
      const total = snapshotsInRegion.length
      const withASPA = snapshotsInRegion.filter((s) => s.hasASPA).length
      const coverage = total > 0 ? Math.round((withASPA / total) * 10000) / 100 : 0

      const topNetworks = this.getTopNetworks(snapshotsInRegion, 5)

      results.push({
        region,
        totalASNs: total,
        ASNsWithASPA: withASPA,
        coveragePercentage: coverage,
        topNetworks,
      })
    }

    return results.sort((a, b) => b.coveragePercentage - a.coveragePercentage)
  }

  private aggregateByIXP(snapshots: AdoptionSnapshot[]): IXPStats[] {
    // IXP membership data would come from PeeringDB in production
    // For now, simulate IXP participation based on AS numbers
    const ixpMap = new Map<number, AdoptionSnapshot[]>()

    for (const snapshot of snapshots) {
      const ixpId = this.inferIXPMembership(snapshot.asn)
      if (ixpId > 0) {
        if (!ixpMap.has(ixpId)) {
          ixpMap.set(ixpId, [])
        }
        ixpMap.get(ixpId)!.push(snapshot)
      }
    }

    const results: IXPStats[] = []

    for (const [ixpId, snapshotsInIXP] of ixpMap.entries()) {
      const total = snapshotsInIXP.length
      const withASPA = snapshotsInIXP.filter((s) => s.hasASPA).length
      const coverage = total > 0 ? Math.round((withASPA / total) * 10000) / 100 : 0

      results.push({
        ixpId,
        ixpName: this.getIXPName(ixpId),
        participantCount: total,
        participantsWithASPA: withASPA,
        coveragePercentage: coverage,
      })
    }

    return results.sort((a, b) => b.coveragePercentage - a.coveragePercentage)
  }

  private getTopNetworks(
    snapshots: AdoptionSnapshot[],
    limit: number
  ): Array<{ asn: number; name: string; providers: number }> {
    const withASPA = snapshots
      .filter((s) => s.hasASPA)
      .sort((a, b) => b.providers.length - a.providers.length)
      .slice(0, limit)

    return withASPA.map((s) => ({
      asn: s.asn,
      name: this.getASNName(s.asn),
      providers: s.providers.length,
    }))
  }

  private inferIXPMembership(asn: number): number {
    // Simulate IXP membership based on AS number ranges
    // In production, query PeeringDB for actual IXP membership
    if (asn % 100 === 0) return asn / 100
    return 0
  }

  private getIXPName(ixpId: number): string {
    // Map IXP IDs to names - in production, query PeeringDB
    const ixpNames: Record<number, string> = {
      1: 'DE-CIX Frankfurt',
      2: 'AMS-IX Amsterdam',
      3: 'LINX London',
      4: 'JPNAP Tokyo',
      5: 'PCCW Hong Kong',
    }

    return ixpNames[ixpId] || `IXP-${ixpId}`
  }

  private getASNName(asn: number): string {
    // In production, query RIPE Stat or PeeringDB for actual AS names
    const asNames: Record<number, string> = {
      13335: 'Cloudflare',
      15169: 'Google',
      8452: 'Telenor',
      3352: 'Telefonica',
      1273: 'Vodafone',
    }

    return asNames[asn] || `AS${asn}`
  }
}
