/**
 * DemandForecaster Tests
 * Tests for the demand forecasting system
 */

import {
  DemandForecaster,
  getDemandForecaster,
} from '../../../lib/demand/forecaster';
import {
  ConsumerPreference,
  SupplyCommitment,
  DemandSignal,
  PlantingRecommendation,
} from '../../../lib/demand/types';

describe('DemandForecaster', () => {
  let forecaster: DemandForecaster;

  beforeEach(() => {
    forecaster = new DemandForecaster();
  });

  describe('Initialization', () => {
    it('should create empty forecaster', () => {
      const json = forecaster.toJSON() as any;
      expect(json.preferences).toEqual([]);
      expect(json.supplyCommitments).toEqual([]);
      expect(json.demandSignals).toEqual([]);
    });
  });

  describe('Consumer Preferences', () => {
    it('should register consumer preferences', () => {
      const preference = createConsumerPreference('consumer-1');
      forecaster.registerPreference(preference);

      const json = forecaster.toJSON() as any;
      expect(json.preferences.length).toBe(1);
    });

    it('should update existing preference', () => {
      const preference1 = createConsumerPreference('consumer-1');
      preference1.householdSize = 2;
      forecaster.registerPreference(preference1);

      const preference2 = createConsumerPreference('consumer-1');
      preference2.householdSize = 4;
      forecaster.registerPreference(preference2);

      const json = forecaster.toJSON() as any;
      expect(json.preferences.length).toBe(1);
      expect(json.preferences[0][1].householdSize).toBe(4);
    });

    it('should register multiple consumers', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));
      forecaster.registerPreference(createConsumerPreference('consumer-2'));
      forecaster.registerPreference(createConsumerPreference('consumer-3'));

      const json = forecaster.toJSON() as any;
      expect(json.preferences.length).toBe(3);
    });
  });

  describe('Supply Commitments', () => {
    it('should register supply commitment', () => {
      const commitment = createSupplyCommitment('grower-1', 'lettuce', 50);
      forecaster.registerSupply(commitment);

      const json = forecaster.toJSON() as any;
      expect(json.supplyCommitments.length).toBe(1);
    });

    it('should track multiple supply commitments', () => {
      forecaster.registerSupply(createSupplyCommitment('grower-1', 'lettuce', 50));
      forecaster.registerSupply(createSupplyCommitment('grower-2', 'tomato', 100));

      const json = forecaster.toJSON() as any;
      expect(json.supplyCommitments.length).toBe(2);
    });
  });

  describe('Demand Signal Generation', () => {
    it('should generate demand signal for region', () => {
      // Add consumers
      forecaster.registerPreference(createConsumerPreference('consumer-1', 40.7, -74.0));
      forecaster.registerPreference(createConsumerPreference('consumer-2', 40.71, -74.01));

      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 50, 'New York Metro', 'summer'
      );

      expect(signal.id).toBeDefined();
      expect(signal.region.name).toBe('New York Metro');
      expect(signal.totalConsumers).toBe(2);
    });

    it('should filter consumers by region radius', () => {
      // Consumer inside radius
      forecaster.registerPreference(createConsumerPreference('inside', 40.7, -74.0));

      // Consumer outside radius (far away)
      forecaster.registerPreference(createConsumerPreference('outside', 35.0, -80.0));

      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 10, 'Small Region', 'summer'
      );

      expect(signal.totalConsumers).toBe(1);
    });

    it('should aggregate demand items', () => {
      const pref1 = createConsumerPreference('consumer-1');
      pref1.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
        { produceType: 'tomato', category: 'nightshades', priority: 'preferred', weeklyQuantity: 2, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref1);

      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 100, 'Test Region', 'summer'
      );

      expect(signal.demandItems.length).toBeGreaterThan(0);
    });

    it('should calculate supply status', () => {
      // Add consumer demand
      forecaster.registerPreference(createConsumerPreference('consumer-1'));

      // No supply - should show shortage
      let signal = forecaster.generateDemandSignal(
        40.7, -74.0, 100, 'Test', 'summer'
      );
      expect(['shortage', 'critical', 'balanced']).toContain(signal.supplyStatus);
    });

    it('should include seasonal period', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));

      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 100, 'Test', 'winter'
      );

      expect(signal.seasonalPeriod).toBe('winter');
    });

    it('should calculate weekly demand correctly', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.householdSize = 4;
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 2, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref);

      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 100, 'Test', 'summer'
      );

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      // Weekly demand = weeklyQuantity * householdSize = 2 * 4 = 8
      expect(lettuceItem?.weeklyDemandKg).toBe(8);
    });
  });

  describe('Demand Forecasting', () => {
    it('should generate forecast for region', () => {
      // Setup some historical data
      forecaster.registerPreference(createConsumerPreference('consumer-1'));
      forecaster.generateDemandSignal(40.7, -74.0, 100, 'Test Region', 'summer');

      const forecast = forecaster.generateForecast('Test Region', 12);

      expect(forecast.id).toBeDefined();
      expect(forecast.region).toBe('Test Region');
      expect(forecast.forecasts).toBeDefined();
    });

    it('should include confidence intervals', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));
      forecaster.generateDemandSignal(40.7, -74.0, 100, 'Test', 'summer');

      const forecast = forecaster.generateForecast('Test', 12);

      forecast.forecasts.forEach(f => {
        expect(f.confidenceInterval.low).toBeLessThanOrEqual(f.predictedDemandKg);
        expect(f.confidenceInterval.high).toBeGreaterThanOrEqual(f.predictedDemandKg);
      });
    });

    it('should identify trends', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));
      forecaster.generateDemandSignal(40.7, -74.0, 100, 'Test', 'summer');

      const forecast = forecaster.generateForecast('Test', 12);

      forecast.forecasts.forEach(f => {
        expect(['increasing', 'stable', 'decreasing']).toContain(f.trend);
      });
    });
  });

  describe('Singleton', () => {
    it('should return same instance from getDemandForecaster', () => {
      const forecaster1 = getDemandForecaster();
      const forecaster2 = getDemandForecaster();
      expect(forecaster1).toBe(forecaster2);
    });
  });

  describe('Serialization', () => {
    it('should export to JSON', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));

      const json = forecaster.toJSON();
      expect(json).toHaveProperty('preferences');
      expect(json).toHaveProperty('supplyCommitments');
      expect(json).toHaveProperty('demandSignals');
    });

    it('should import from JSON', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));
      forecaster.registerSupply(createSupplyCommitment('grower-1', 'lettuce', 50));

      const json = forecaster.toJSON();
      const restored = DemandForecaster.fromJSON(json);

      const restoredJson = restored.toJSON() as any;
      expect(restoredJson.preferences.length).toBe(1);
      expect(restoredJson.supplyCommitments.length).toBe(1);
    });
  });
});

// Helper functions
function createConsumerPreference(
  consumerId: string,
  lat: number = 40.7128,
  lon: number = -74.006
): ConsumerPreference {
  return {
    consumerId,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    location: {
      latitude: lat,
      longitude: lon,
      maxDeliveryRadiusKm: 25,
      city: 'New York',
    },
    dietaryType: ['omnivore'],
    allergies: [],
    dislikes: [],
    preferredCategories: ['leafy_greens', 'nightshades'],
    preferredItems: [
      { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
      { produceType: 'tomato', category: 'nightshades', priority: 'preferred', weeklyQuantity: 2, seasonalOnly: false },
    ],
    certificationPreferences: ['organic', 'local'],
    freshnessImportance: 5,
    priceImportance: 3,
    sustainabilityImportance: 4,
    deliveryPreferences: {
      method: ['home_delivery'],
      frequency: 'weekly',
      preferredDays: ['saturday'],
    },
    householdSize: 3,
    weeklyBudget: 100,
    currency: 'USD',
  };
}

function createSupplyCommitment(
  growerId: string,
  produceType: string,
  quantity: number
): SupplyCommitment {
  return {
    id: `supply-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    growerId,
    timestamp: new Date().toISOString(),
    produceType,
    committedQuantityKg: quantity,
    availableFrom: new Date().toISOString(),
    availableUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
    pricePerKg: 5,
    currency: 'USD',
    minimumOrderKg: 1,
    certifications: ['organic'],
    freshnessGuaranteeHours: 48,
    deliveryRadiusKm: 50,
    deliveryMethods: ['grower_delivery', 'customer_pickup'],
    status: 'available',
    remainingKg: quantity,
  };
}
