/**
 * Demand API Tests
 * Tests for demand-related API endpoints
 */

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

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

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

  describe('POST /api/demand/preferences', () => {
    it('should register consumer preferences', () => {
      const preference = createConsumerPreference('api-consumer-001');

      forecaster.registerPreference(preference);

      const json = forecaster.toJSON() as any;
      expect(json.preferences.length).toBe(1);
      expect(json.preferences[0][0]).toBe('api-consumer-001');
    });

    it('should update existing preferences', () => {
      const pref1 = createConsumerPreference('api-consumer-001');
      pref1.householdSize = 2;
      forecaster.registerPreference(pref1);

      const pref2 = createConsumerPreference('api-consumer-001');
      pref2.householdSize = 5;
      forecaster.registerPreference(pref2);

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

    it('should validate required fields', () => {
      const preference = createConsumerPreference('api-consumer-001');

      // All required fields present
      forecaster.registerPreference(preference);

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

  describe('GET /api/demand/preferences', () => {
    it('should return preferences for consumer', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));
      forecaster.registerPreference(createConsumerPreference('consumer-002'));

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

  describe('POST /api/demand/signal', () => {
    it('should generate demand signal for region', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));

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

      expect(signal.id).toBeDefined();
      expect(signal.region.name).toBe('Test Region');
      expect(signal.demandItems).toBeDefined();
    });

    it('should include supply status', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));

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

      expect(['surplus', 'balanced', 'shortage', 'critical']).toContain(signal.supplyStatus);
    });

    it('should calculate confidence level', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));

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

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

  describe('GET /api/demand/recommendations', () => {
    it('should return planting recommendations', () => {
      const pref = createConsumerPreference('consumer-001');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 10, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref);

      forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test', 'spring');

      const recommendations = forecaster.generatePlantingRecommendations(
        'grower-001', 40.7, -74.0, 50, 100, 'spring'
      );

      expect(recommendations.length).toBeGreaterThanOrEqual(0);
    });

    it('should include risk assessment', () => {
      const pref = createConsumerPreference('consumer-001');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 10, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref);

      forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test', 'spring');

      const recommendations = forecaster.generatePlantingRecommendations(
        'grower-001', 40.7, -74.0, 50, 100, 'spring'
      );

      if (recommendations.length > 0) {
        expect(['low', 'medium', 'high']).toContain(recommendations[0].overallRisk);
      }
    });
  });

  describe('GET /api/demand/forecast', () => {
    it('should return demand forecast', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));
      forecaster.generateDemandSignal(40.7, -74.0, 50, '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 trend analysis', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));
      forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test Region', 'summer');

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

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

  describe('POST /api/demand/supply', () => {
    it('should register supply commitment', () => {
      const commitment = createSupplyCommitment('grower-001', 'lettuce', 50);

      forecaster.registerSupply(commitment);

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

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

      // Generate signal without supply
      const signal1 = forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test', 'spring');
      const gap1 = signal1.demandItems.find(i => i.produceType === 'lettuce')?.gapKg || 0;

      // Add supply
      forecaster.registerSupply(createSupplyCommitment('grower-001', 'lettuce', 5));

      // Generate signal with supply
      const signal2 = forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test', 'spring');
      const gap2 = signal2.demandItems.find(i => i.produceType === 'lettuce')?.gapKg || 0;

      expect(gap2).toBeLessThan(gap1);
    });
  });

  describe('POST /api/demand/match', () => {
    it('should create market match', () => {
      // This tests the matching logic between supply and demand
      const pref = createConsumerPreference('consumer-001');
      pref.preferredItems = [
        { produceType: 'lettuce', category: 'leafy_greens', priority: 'must_have', weeklyQuantity: 5, seasonalOnly: false },
      ];
      forecaster.registerPreference(pref);

      forecaster.registerSupply(createSupplyCommitment('grower-001', 'lettuce', 10));

      const signal = forecaster.generateDemandSignal(40.7, -74.0, 50, 'Test', 'spring');
      const lettuceItem = signal.demandItems.find(i => i.produceType === 'lettuce');

      expect(lettuceItem?.matchedSupply).toBeGreaterThan(0);
    });
  });

  describe('Error Handling', () => {
    it('should handle empty region gracefully', () => {
      // No consumers registered
      const signal = forecaster.generateDemandSignal(
        40.7, -74.0, 50, 'Empty Region', 'summer'
      );

      expect(signal.totalConsumers).toBe(0);
      expect(signal.demandItems.length).toBe(0);
    });

    it('should handle invalid coordinates gracefully', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));

      // Very distant coordinates
      const signal = forecaster.generateDemandSignal(
        -90, 0, 1, 'Antarctica', 'winter'
      );

      // Should still return valid signal structure
      expect(signal.id).toBeDefined();
    });
  });

  describe('Response Format', () => {
    it('should return consistent signal format', () => {
      forecaster.registerPreference(createConsumerPreference('consumer-001'));

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

      // Check all required fields
      expect(signal.id).toBeDefined();
      expect(signal.timestamp).toBeDefined();
      expect(signal.region).toBeDefined();
      expect(signal.periodStart).toBeDefined();
      expect(signal.periodEnd).toBeDefined();
      expect(signal.seasonalPeriod).toBeDefined();
      expect(signal.demandItems).toBeDefined();
      expect(signal.totalConsumers).toBeDefined();
      expect(signal.totalWeeklyDemandKg).toBeDefined();
      expect(signal.confidenceLevel).toBeDefined();
      expect(signal.currentSupplyKg).toBeDefined();
      expect(signal.supplyGapKg).toBeDefined();
      expect(signal.supplyStatus).toBeDefined();
    });
  });
});

// Helper functions
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: [
      { produceType: 'lettuce', category: 'leafy_greens', priority: 'preferred', weeklyQuantity: 1, seasonalOnly: false },
    ],
    certificationPreferences: ['organic'],
    freshnessImportance: 4,
    priceImportance: 3,
    sustainabilityImportance: 4,
    deliveryPreferences: {
      method: ['home_delivery'],
      frequency: 'weekly',
      preferredDays: ['saturday'],
    },
    householdSize: 2,
    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'],
    status: 'available',
    remainingKg: quantity,
  };
}
