import { describe, it, expect } from 'vitest'
import { AdoptionForecaster } from '../forecaster'

describe('AdoptionForecaster', () => {
  const forecaster = new AdoptionForecaster()

  describe('forecast', () => {
    it('should return forecast data with required fields', () => {
      const data = [
        { date: new Date('2024-01-01'), coverage: 20 },
        { date: new Date('2024-01-02'), coverage: 21 },
        { date: new Date('2024-01-03'), coverage: 22 },
      ]

      const forecast = forecaster.forecast(data)

      expect(forecast).toHaveProperty('predictedCoverage6m')
      expect(forecast).toHaveProperty('confidence')
      expect(forecast).toHaveProperty('trend')
      expect(forecast).toHaveProperty('trendStrength')
    })

    it('should return upward trend for increasing data', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 20 + i * 0.5,
      }))

      const forecast = forecaster.forecast(data)

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

    it('should return downward trend for decreasing data', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 50 - i * 0.5,
      }))

      const forecast = forecaster.forecast(data)

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

    it('should return stable trend for flat data', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 35,
      }))

      const forecast = forecaster.forecast(data)

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

    it('should predict coverage between 0 and 100', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 40 + Math.sin(i / 10) * 10,
      }))

      const forecast = forecaster.forecast(data)

      expect(forecast.predictedCoverage6m).toBeGreaterThanOrEqual(0)
      expect(forecast.predictedCoverage6m).toBeLessThanOrEqual(100)
    })

    it('should have confidence between 0 and 1', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 30 + i * 0.3,
      }))

      const forecast = forecaster.forecast(data)

      expect(forecast.confidence).toBeGreaterThan(0)
      expect(forecast.confidence).toBeLessThanOrEqual(1)
    })

    it('should handle empty data gracefully', () => {
      const forecast = forecaster.forecast([])

      expect(forecast.predictedCoverage6m).toBe(0)
      expect(forecast.confidence).toBe(0)
      expect(forecast.trend).toBe('stable')
    })

    it('should handle single data point', () => {
      const data = [{ date: new Date(), coverage: 50 }]

      const forecast = forecaster.forecast(data)

      expect(forecast.predictedCoverage6m).toBeGreaterThanOrEqual(0)
      expect(forecast.confidence).toBe(0)
    })
  })

  describe('calculateMovingAverage', () => {
    it('should smooth coverage values', () => {
      const data = [
        { date: new Date('2024-01-01'), coverage: 10 },
        { date: new Date('2024-01-02'), coverage: 50 },
        { date: new Date('2024-01-03'), coverage: 20 },
        { date: new Date('2024-01-04'), coverage: 40 },
        { date: new Date('2024-01-05'), coverage: 30 },
      ]

      const smoothed = forecaster.calculateMovingAverage(data, 2)

      expect(smoothed.length).toBeGreaterThan(0)
      expect(smoothed[0].coverage).toBeLessThanOrEqual(30)
      expect(smoothed[0].coverage).toBeGreaterThanOrEqual(10)
    })

    it('should have same date ordering as input', () => {
      const data = Array.from({ length: 10 }, (_, i) => ({
        date: new Date(Date.now() - (10 - i) * 86400000),
        coverage: 40 + i,
      }))

      const smoothed = forecaster.calculateMovingAverage(data, 3)

      for (let i = 0; i < smoothed.length - 1; i++) {
        expect(smoothed[i].date <= smoothed[i + 1].date).toBe(true)
      }
    })
  })

  describe('detectAnomalies', () => {
    it('should identify outliers', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: i < 29 ? 40 : 80,
      }))

      const anomalies = forecaster.detectAnomalies(data, 2)

      expect(anomalies.length).toBeGreaterThan(0)
      expect(anomalies[0].coverage).toBe(80)
    })

    it('should not detect anomalies in normal data', () => {
      const data = Array.from({ length: 30 }, (_, i) => ({
        date: new Date(Date.now() - (30 - i) * 86400000),
        coverage: 40 + Math.sin(i / 5) * 2,
      }))

      const anomalies = forecaster.detectAnomalies(data, 2)

      expect(anomalies.length).toBe(0)
    })
  })
})
