import { describe, it, expect } from 'vitest';
import * as forecastingService from './revenueForecastingService.ts';

describe('Revenue Forecasting Service', () => {
  describe('generateRevenueForecast', () => {
    it('should generate forecasts for specified days', async () => {
      const forecasts = await forecastingService.generateRevenueForecast('ml', 7);

      expect(Array.isArray(forecasts)).toBe(true);
      expect(forecasts.length).toBe(7);

      forecasts.forEach((forecast) => {
        expect(forecast).toHaveProperty('forecastDate');
        expect(forecast).toHaveProperty('forecastedRevenue');
        expect(forecast).toHaveProperty('confidenceHigh');
        expect(forecast).toHaveProperty('confidenceLow');
        expect(forecast).toHaveProperty('confidenceLevel');
        expect(forecast).toHaveProperty('modelType');
        expect(forecast).toHaveProperty('accuracy');
        expect(forecast).toHaveProperty('trend');

        // Verify confidence intervals
        expect(forecast.confidenceHigh).toBeGreaterThanOrEqual(forecast.forecastedRevenue);
        expect(forecast.confidenceLow).toBeLessThanOrEqual(forecast.forecastedRevenue);
        expect(forecast.confidenceLow).toBeGreaterThanOrEqual(0);

        // Verify trend
        expect(['up', 'down', 'stable']).toContain(forecast.trend);
      });
    });

    it('should support different model types', async () => {
      const mlForecasts = await forecastingService.generateRevenueForecast('ml', 3);
      const arimaForecasts = await forecastingService.generateRevenueForecast('arima', 3);
      const expForecasts = await forecastingService.generateRevenueForecast('exponential', 3);

      expect(mlForecasts[0].modelType).toBe('ml');
      expect(arimaForecasts[0].modelType).toBe('arima');
      expect(expForecasts[0].modelType).toBe('exponential');
    });

    it('should have different accuracy for different models', async () => {
      const mlForecasts = await forecastingService.generateRevenueForecast('ml', 1);
      const arimaForecasts = await forecastingService.generateRevenueForecast('arima', 1);

      // ML should generally have higher accuracy
      expect(mlForecasts[0].accuracy).toBeGreaterThanOrEqual(arimaForecasts[0].accuracy);
    });
  });

  describe('getRevenueBySource', () => {
    it('should return revenue breakdown by source', async () => {
      const sources = await forecastingService.getRevenueBySource();

      expect(Array.isArray(sources)).toBe(true);
      expect(sources.length).toBeGreaterThan(0);

      sources.forEach((source) => {
        expect(source).toHaveProperty('source');
        expect(source).toHaveProperty('revenue');
        expect(source).toHaveProperty('percentage');
        expect(source).toHaveProperty('forecast');

        expect(source.revenue).toBeGreaterThan(0);
        expect(source.percentage).toBeGreaterThan(0);
        expect(source.percentage).toBeLessThanOrEqual(100);
        expect(source.forecast).toBeGreaterThan(0);
      });
    });

    it('should have percentages that add up to 100', async () => {
      const sources = await forecastingService.getRevenueBySource();
      const totalPercentage = sources.reduce((sum, s) => sum + s.percentage, 0);

      expect(totalPercentage).toBe(100);
    });
  });

  describe('generateForecastInsights', () => {
    it('should generate actionable insights', async () => {
      const insights = await forecastingService.generateForecastInsights();

      expect(Array.isArray(insights)).toBe(true);
      expect(insights.length).toBeGreaterThan(0);

      insights.forEach((insight) => {
        expect(insight).toHaveProperty('insightType');
        expect(insight).toHaveProperty('title');
        expect(insight).toHaveProperty('description');
        expect(insight).toHaveProperty('recommendation');
        expect(insight).toHaveProperty('priority');

        expect(insight.title).toBeTruthy();
        expect(insight.description).toBeTruthy();
        expect(insight.recommendation).toBeTruthy();
        expect(['low', 'medium', 'high', 'critical']).toContain(insight.priority);
      });
    });
  });

  describe('retrainForecastModel', () => {
    it('should retrain model successfully', async () => {
      const result = await forecastingService.retrainForecastModel('ml');

      expect(result).toHaveProperty('success');
      expect(result).toHaveProperty('message');
      expect(result).toHaveProperty('accuracy');

      expect(result.success).toBe(true);
      expect(result.accuracy).toBeGreaterThan(0);
      expect(result.accuracy).toBeLessThanOrEqual(95);
    });

    it('should work for all model types', async () => {
      const mlResult = await forecastingService.retrainForecastModel('ml');
      const arimaResult = await forecastingService.retrainForecastModel('arima');
      const expResult = await forecastingService.retrainForecastModel('exponential');

      expect(mlResult.success).toBe(true);
      expect(arimaResult.success).toBe(true);
      expect(expResult.success).toBe(true);
    });
  });

  describe('getForecastAccuracyMetrics', () => {
    it('should return accuracy metrics', async () => {
      const metrics = await forecastingService.getForecastAccuracyMetrics();

      expect(metrics).toHaveProperty('modelType');
      expect(metrics).toHaveProperty('accuracy');
      expect(metrics).toHaveProperty('mape');
      expect(metrics).toHaveProperty('rmse');
      expect(metrics).toHaveProperty('lastUpdated');

      expect(metrics.accuracy).toBeGreaterThan(0);
      expect(metrics.accuracy).toBeLessThanOrEqual(100);
      expect(metrics.mape).toBeGreaterThan(0);
      expect(metrics.rmse).toBeGreaterThan(0);
    });
  });

  describe('compareModels', () => {
    it('should compare all model types', async () => {
      const comparison = await forecastingService.compareModels();

      expect(Array.isArray(comparison)).toBe(true);
      expect(comparison.length).toBe(3); // ml, arima, exponential

      comparison.forEach((model) => {
        expect(model).toHaveProperty('modelType');
        expect(model).toHaveProperty('accuracy');
        expect(model).toHaveProperty('rmse');
        expect(model).toHaveProperty('trainingTime');
        expect(model).toHaveProperty('recommendedFor');

        expect(model.accuracy).toBeGreaterThan(0);
        expect(model.rmse).toBeGreaterThan(0);
        expect(model.trainingTime).toBeGreaterThan(0);
      });
    });

    it('should rank models by accuracy', async () => {
      const comparison = await forecastingService.compareModels();
      const accuracies = comparison.map((m) => m.accuracy);

      // ML should have highest accuracy
      expect(accuracies[0]).toBeGreaterThanOrEqual(accuracies[1]);
      expect(accuracies[1]).toBeGreaterThanOrEqual(accuracies[2]);
    });
  });
});
