import { describe, it, expect, beforeEach } from 'vitest'
import { ASNSampler } from '../sampler'
import { ASPACollector } from '../collector'
import { AdoptionAggregator } from '../aggregator'
import { AdoptionForecaster } from '../forecaster'

describe('ASPA Adoption Tracker - Integration', () => {
  let sampler: ASNSampler
  let collector: ASPACollector
  let aggregator: AdoptionAggregator
  let forecaster: AdoptionForecaster

  beforeEach(() => {
    sampler = new ASNSampler()
    collector = new ASPACollector()
    aggregator = new AdoptionAggregator()
    forecaster = new AdoptionForecaster()
  })

  describe('Daily ASPA Adoption Workflow', () => {
    it('should complete full workflow: sample -> collect -> aggregate -> forecast -> store', async () => {
      // Step 1: Sample ASNs (stratified sampling with minASNsPerRegion may return fewer total)
      const samplingResult = sampler.sample(500, {
        stratifiedByRegion: true,
        minASNsPerRegion: 50,
      })

      expect(samplingResult.asns.length).toBeGreaterThan(0)
      expect(samplingResult.asns.length).toBeLessThanOrEqual(500)

      // Verify stratification by checking regional distribution
      const regionCounts = Object.values(samplingResult.weightedByRegion)
      const allAboveMinimum = regionCounts.every((count) => count >= 10)
      expect(allAboveMinimum).toBe(true)

      // Step 2: Collect ASPA data for sampled ASNs (use mock snapshots to avoid network calls)
      const snapshots = samplingResult.asns.map((asn, i) => ({
        asn,
        hasASPA: i % 2 === 0,
        providers: i % 2 === 0 ? [asn - 1, asn + 1] : [],
        region: ['APNIC', 'RIPE', 'ARIN', 'LACNIC'][i % 4],
      }))

      expect(snapshots).toHaveLength(samplingResult.asns.length)
      expect(snapshots[0]).toHaveProperty('asn')
      expect(snapshots[0]).toHaveProperty('hasASPA')
      expect(snapshots[0]).toHaveProperty('providers')
      expect(snapshots[0]).toHaveProperty('region')

      // Step 3: Aggregate by region and IXP
      const aggregationResult = aggregator.aggregate(snapshots, {
        includeRegions: true,
        includeIXPs: true,
      })

      expect(aggregationResult.overall).toBeDefined()
      expect(aggregationResult.overall.total).toBe(samplingResult.asns.length)
      expect(aggregationResult.overall.coverage).toBeGreaterThanOrEqual(0)
      expect(aggregationResult.overall.coverage).toBeLessThanOrEqual(100)

      // Verify regional breakdown
      expect(Array.isArray(aggregationResult.regions)).toBe(true)
      expect(aggregationResult.regions.length).toBeGreaterThan(0)
      for (const region of aggregationResult.regions) {
        expect(region.region).toBeDefined()
        expect(region.totalASNs).toBeGreaterThan(0)
        expect(region.coveragePercentage).toBeGreaterThanOrEqual(0)
        expect(region.coveragePercentage).toBeLessThanOrEqual(100)
      }

      // Step 4: Forecast adoption trend (mock with synthetic historical data)
      const now = new Date()
      const historicalData = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(now.getTime() - (30 - i) * 24 * 60 * 60 * 1000),
        coverage: 40 + Math.sin(i / 5) * 5 + Math.random() * 2,
      }))

      const forecast = forecaster.forecast(historicalData, {
        historyDays: 30,
        forecastMonths: 6,
        regressionAlpha: 0.05,
      })

      expect(forecast).toBeDefined()
      expect(forecast.trend).toMatch(/up|down|stable/)
      expect(forecast.confidence).toBeGreaterThan(0)
      expect(forecast.confidence).toBeLessThanOrEqual(1)
      expect(forecast.predictedCoverage6m).toBeGreaterThanOrEqual(0)
      expect(forecast.predictedCoverage6m).toBeLessThanOrEqual(100)
    })

    it('should handle edge case: no ASNs with ASPA', async () => {
      const snapshots = Array.from({ length: 50 }, (_, i) => ({
        asn: 10000 + i,
        hasASPA: false, // All without ASPA
        providers: [],
        region: ['APNIC', 'RIPE', 'ARIN', 'LACNIC'][i % 4],
      }))

      const result = aggregator.aggregate(snapshots, {
        includeRegions: true,
        includeIXPs: false,
      })

      expect(result.overall.coverage).toBe(0)
      expect(result.overall.withASPA).toBe(0)
    })

    it('should handle edge case: 100% adoption', async () => {
      const snapshots = Array.from({ length: 50 }, (_, i) => ({
        asn: 10000 + i,
        hasASPA: true, // All with ASPA
        providers: [100 + (i % 3)],
        region: ['APNIC', 'RIPE', 'ARIN', 'LACNIC'][i % 4],
      }))

      const result = aggregator.aggregate(snapshots, {
        includeRegions: true,
        includeIXPs: false,
      })

      expect(result.overall.coverage).toBe(100)
      expect(result.overall.withASPA).toBe(50)
    })

    it('should handle upward adoption trend', async () => {
      const now = new Date()
      const upwardTrend = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(now.getTime() - (30 - i) * 24 * 60 * 60 * 1000),
        coverage: 30 + i * 0.5, // Steady upward trend
      }))

      const forecast = forecaster.forecast(upwardTrend, {
        historyDays: 30,
        forecastMonths: 6,
        regressionAlpha: 0.05,
      })

      expect(forecast.trend).toBe('up')
      expect(forecast.trendStrength).toBeGreaterThan(0)
      expect(forecast.predictedCoverage6m).toBeGreaterThan(upwardTrend[upwardTrend.length - 1].coverage)
    })

    it('should handle downward adoption trend', async () => {
      const now = new Date()
      const downwardTrend = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(now.getTime() - (30 - i) * 24 * 60 * 60 * 1000),
        coverage: 70 - i * 0.3, // Steady downward trend
      }))

      const forecast = forecaster.forecast(downwardTrend, {
        historyDays: 30,
        forecastMonths: 6,
        regressionAlpha: 0.05,
      })

      expect(forecast.trend).toBe('down')
      expect(forecast.trendStrength).toBeGreaterThan(0)
    })

    it('should handle stable adoption trend', async () => {
      const now = new Date()
      const stableTrend = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(now.getTime() - (30 - i) * 24 * 60 * 60 * 1000),
        coverage: 45 + Math.sin(i / 10) * 1, // Flat with minimal variation
      }))

      const forecast = forecaster.forecast(stableTrend, {
        historyDays: 30,
        forecastMonths: 6,
        regressionAlpha: 0.05,
      })

      expect(forecast.trend).toBe('stable')
    })

    it('should calculate regional distribution correctly', async () => {
      const snapshots = [
        // APNIC: 35% (3-4 ASNs from 100)
        { asn: 1, hasASPA: true, providers: [100], region: 'APNIC' },
        { asn: 2, hasASPA: false, providers: [], region: 'APNIC' },
        { asn: 3, hasASPA: true, providers: [100], region: 'APNIC' },

        // RIPE: 28% (2-3 ASNs)
        { asn: 4, hasASPA: true, providers: [200], region: 'RIPE' },
        { asn: 5, hasASPA: true, providers: [200], region: 'RIPE' },

        // ARIN: 26% (2-3 ASNs)
        { asn: 6, hasASPA: false, providers: [], region: 'ARIN' },
        { asn: 7, hasASPA: true, providers: [300], region: 'ARIN' },

        // LACNIC: 8% (0-1 ASN)
        { asn: 8, hasASPA: true, providers: [400], region: 'LACNIC' },

        // AFRINIC: 3% (0 ASNs)
      ]

      const result = aggregator.aggregate(snapshots, {
        includeRegions: true,
        includeIXPs: false,
      })

      expect(result.regions).toBeDefined()
      expect(result.regions.length).toBeGreaterThan(0)

      // Verify each region has correct totals
      const apnic = result.regions.find((r) => r.region === 'APNIC')
      expect(apnic?.totalASNs).toBe(3)
      expect(apnic?.ASNsWithASPA).toBe(2)
      expect(apnic?.coveragePercentage).toBeCloseTo(66.67, 1)

      const ripe = result.regions.find((r) => r.region === 'RIPE')
      expect(ripe?.totalASNs).toBe(2)
      expect(ripe?.ASNsWithASPA).toBe(2)
      expect(ripe?.coveragePercentage).toBe(100)
    })

    it('should preserve data integrity through aggregation', () => {
      const testASNs = [13335, 15169, 8452]

      // Create deterministic snapshots
      const snapshots = testASNs.map((asn) => ({
        asn,
        hasASPA: asn % 2 === 0, // Alternating pattern
        providers: asn % 2 === 0 ? [asn - 1, asn + 1] : [],
        region: ['APNIC', 'RIPE', 'ARIN'][testASNs.indexOf(asn)],
      }))

      const aggregation = aggregator.aggregate(snapshots, {
        includeRegions: true,
        includeIXPs: false,
      })

      // Verify totals match input
      expect(aggregation.overall.total).toBe(snapshots.length)
      const withASPA = snapshots.filter((s) => s.hasASPA).length
      expect(aggregation.overall.withASPA).toBe(withASPA)

      // Verify regions are preserved
      expect(aggregation.regions.length).toBeGreaterThan(0)
    })
  })

  describe('Sampling Consistency', () => {
    it('should accept seed parameter without error', () => {
      const result1 = sampler.sample(100, {
        stratifiedByRegion: false,
        seed: 12345,
      })

      expect(result1.asns).toHaveLength(100)
      expect(result1.totalSampled).toBe(100)
    })

    it('should not include duplicate ASNs in sample', () => {
      const result = sampler.sample(500, {
        stratifiedByRegion: true,
        minASNsPerRegion: 50,
      })

      const unique = new Set(result.asns)
      expect(unique.size).toBe(result.asns.length)
    })

    it('should exclude private ASN ranges', () => {
      const result = sampler.sample(1000, {
        stratifiedByRegion: true,
      })

      for (const asn of result.asns) {
        // Private range 1: 64512-65534
        const inPrivateRange1 = asn >= 64512 && asn <= 65534
        // Private range 2: 4200000000-4294967294
        const inPrivateRange2 = asn >= 4200000000 && asn <= 4294967294

        expect(inPrivateRange1 || inPrivateRange2).toBe(false)
      }
    })
  })
})
