/**
 * Demand Aggregation Tests
 * Tests for demand aggregation logic
 */

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

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

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

  describe('Consumer Aggregation', () => {
    it('should aggregate demand from multiple consumers', () => {
      // Add 5 consumers all wanting lettuce
      for (let i = 0; i < 5; i++) {
        const pref = createConsumerPreference(`consumer-${i}`);
        pref.preferredItems = [
          { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
        ];
        pref.householdSize = 2;
        forecaster.registerPreference(pref);
      }

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      expect(lettuceItem?.consumerCount).toBe(5);
      // Total demand = 5 consumers * 1kg/week * 2 household = 10 kg
      expect(lettuceItem?.weeklyDemandKg).toBe(10);
    });

    it('should aggregate different produce types separately', () => {
      const pref1 = createConsumerPreference('consumer-1');
      pref1.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref1);

      const pref2 = createConsumerPreference('consumer-2');
      pref2.preferredItems = [
        { produceType: 'tomato', category: 'nightshades', priority: 'must_have', weeklyQuantity: 2, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref2);

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      const tomatoItem = signal.demandItems.find(i => i.produceType === 'tomato');

      expect(lettuceItem).toBeDefined();
      expect(tomatoItem).toBeDefined();
      expect(lettuceItem?.consumerCount).toBe(1);
      expect(tomatoItem?.consumerCount).toBe(1);
    });

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

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      expect(lettuceItem?.monthlyDemandKg).toBe(lettuceItem!.weeklyDemandKg * 4);
    });
  });

  describe('Priority Aggregation', () => {
    it('should calculate aggregate priority from consumer priorities', () => {
      const pref1 = createConsumerPreference('consumer-1');
      pref1.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref1);

      const pref2 = createConsumerPreference('consumer-2');
      pref2.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'occasional', weeklyQuantity: 1, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref2);

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      // must_have=10, occasional=2, average = 6
      expect(lettuceItem?.aggregatePriority).toBe(6);
    });

    it('should determine urgency from aggregate priority', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, 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');
      expect(lettuceItem?.urgency).toBe('immediate');
    });

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

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

      // Tomato (must_have) should come before lettuce (occasional)
      expect(signal.demandItems[0].produceType).toBe('tomato');
    });
  });

  describe('Certification Aggregation', () => {
    it('should aggregate certification preferences', () => {
      const pref1 = createConsumerPreference('consumer-1');
      pref1.certificationPreferences = ['organic'];
      forecaster.registerPreference(pref1);

      const pref2 = createConsumerPreference('consumer-2');
      pref2.certificationPreferences = ['organic', 'local'];
      forecaster.registerPreference(pref2);

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

      // Check that certification preferences are aggregated
      signal.demandItems.forEach(item => {
        expect(item.preferredCertifications).toContain('organic');
      });
    });
  });

  describe('Price Aggregation', () => {
    it('should calculate average willing price', () => {
      const pref1 = createConsumerPreference('consumer-1');
      pref1.weeklyBudget = 100;
      pref1.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 2, seasonalOnly: false },
      ];
      pref1.householdSize = 1;
      forecaster.registerPreference(pref1);

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      expect(lettuceItem?.averageWillingPrice).toBeGreaterThan(0);
      expect(lettuceItem?.priceUnit).toBe('per_kg');
    });
  });

  describe('Supply Gap Calculation', () => {
    it('should calculate supply gap when no supply', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 10, seasonalOnly: false },
      ];
      pref.householdSize = 1;
      forecaster.registerPreference(pref);

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      expect(lettuceItem?.gapKg).toBe(10);
      expect(lettuceItem?.matchedSupply).toBe(0);
    });

    it('should reduce gap when supply is available', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 10, seasonalOnly: false },
      ];
      pref.householdSize = 1;
      forecaster.registerPreference(pref);

      // Add partial supply
      forecaster.registerSupply({
        id: 'supply-1',
        growerId: 'grower-1',
        timestamp: new Date().toISOString(),
        produceType: 'lettuce',
        committedQuantityKg: 5,
        availableFrom: new Date().toISOString(),
        availableUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
        pricePerKg: 5,
        currency: 'USD',
        minimumOrderKg: 1,
        certifications: [],
        freshnessGuaranteeHours: 48,
        deliveryRadiusKm: 50,
        deliveryMethods: ['grower_delivery'],
        status: 'available',
        remainingKg: 5,
      });

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

      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');
      expect(lettuceItem?.matchedSupply).toBe(5);
      expect(lettuceItem?.gapKg).toBe(5);
    });

    it('should show surplus when supply exceeds demand', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 5, seasonalOnly: false },
      ];
      pref.householdSize = 1;
      forecaster.registerPreference(pref);

      // Add excess supply
      forecaster.registerSupply({
        id: 'supply-1',
        growerId: 'grower-1',
        timestamp: new Date().toISOString(),
        produceType: 'lettuce',
        committedQuantityKg: 20,
        availableFrom: new Date().toISOString(),
        availableUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
        pricePerKg: 5,
        currency: 'USD',
        minimumOrderKg: 1,
        certifications: [],
        freshnessGuaranteeHours: 48,
        deliveryRadiusKm: 50,
        deliveryMethods: ['grower_delivery'],
        status: 'available',
        remainingKg: 20,
      });

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

      expect(signal.supplyStatus).toBe('surplus');
    });
  });

  describe('Seasonal Availability', () => {
    it('should mark items as in-season correctly', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
        { produceType: 'tomato', category: 'nightshades', priority: 'must_have', weeklyQuantity: 1, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref);

      // Summer - tomato in season, lettuce depends
      const summerSignal = forecaster.generateDemandSignal(
        40.7, -74.0, 100, 'Test', 'summer'
      );

      const tomatoItem = summerSignal.demandItems.find(i => i.produceType === 'tomato');
      expect(tomatoItem?.inSeason).toBe(true);
    });

    it('should include seasonal availability info', () => {
      const pref = createConsumerPreference('consumer-1');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 1, 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');
      expect(lettuceItem?.seasonalAvailability).toBeDefined();
      expect(lettuceItem?.seasonalAvailability.spring).toBeDefined();
      expect(lettuceItem?.seasonalAvailability.summer).toBeDefined();
      expect(lettuceItem?.seasonalAvailability.fall).toBeDefined();
      expect(lettuceItem?.seasonalAvailability.winter).toBeDefined();
    });
  });

  describe('Confidence Level', () => {
    it('should increase confidence with more consumers', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-1'));

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

      forecaster.registerPreference(createConsumerPreference('consumer-2'));
      forecaster.registerPreference(createConsumerPreference('consumer-3'));

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

      expect(signal2.confidenceLevel).toBeGreaterThan(signal1.confidenceLevel);
    });

    it('should cap confidence at 100', () => {
      // Add many consumers
      for (let i = 0; i < 100; i++) {
        forecaster.registerPreference(createConsumerPreference(`consumer-${i}`));
      }

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

      expect(signal.confidenceLevel).toBeLessThanOrEqual(100);
    });
  });
});

// Helper function
function createConsumerPreference(consumerId: string): ConsumerPreference {
  return {
    consumerId,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    location: {
      latitude: 40.7128,
      longitude: -74.006,
      maxDeliveryRadiusKm: 25,
    },
    dietaryType: ['omnivore'],
    allergies: [],
    dislikes: [],
    preferredCategories: ['leafy_greens'],
    preferredItems: [],
    certificationPreferences: ['organic'],
    freshnessImportance: 4,
    priceImportance: 3,
    sustainabilityImportance: 4,
    deliveryPreferences: {
      method: ['home_delivery'],
      frequency: 'weekly',
      preferredDays: ['saturday'],
    },
    householdSize: 2,
    weeklyBudget: 100,
    currency: 'USD',
  };
}
