/**
 * Carbon Calculation Tests
 * Tests for carbon footprint calculation logic
 */

import { TransportChain } from '../../../lib/transport/tracker';
import {
  CARBON_FACTORS,
  TransportMethod,
  TransportLocation,
} from '../../../lib/transport/types';

describe('Carbon Footprint Calculation', () => {
  let chain: TransportChain;

  beforeEach(() => {
    chain = new TransportChain(1);
  });

  describe('CARBON_FACTORS', () => {
    it('should have zero emissions for walking', () => {
      expect(CARBON_FACTORS.walking).toBe(0);
    });

    it('should have zero emissions for bicycle', () => {
      expect(CARBON_FACTORS.bicycle).toBe(0);
    });

    it('should have low emissions for electric vehicles', () => {
      expect(CARBON_FACTORS.electric_vehicle).toBeLessThan(CARBON_FACTORS.gasoline_vehicle);
      expect(CARBON_FACTORS.electric_truck).toBeLessThan(CARBON_FACTORS.diesel_truck);
    });

    it('should have high emissions for air transport', () => {
      expect(CARBON_FACTORS.air).toBeGreaterThan(CARBON_FACTORS.diesel_truck);
      expect(CARBON_FACTORS.air).toBeGreaterThan(CARBON_FACTORS.ship);
    });

    it('should have moderate emissions for refrigerated trucks', () => {
      expect(CARBON_FACTORS.refrigerated_truck).toBeGreaterThan(CARBON_FACTORS.diesel_truck);
    });

    it('should have lowest emissions for rail and ship', () => {
      expect(CARBON_FACTORS.rail).toBeLessThan(CARBON_FACTORS.diesel_truck);
      expect(CARBON_FACTORS.ship).toBeLessThan(CARBON_FACTORS.diesel_truck);
    });

    it('should define all transport methods', () => {
      const methods: TransportMethod[] = [
        'walking', 'bicycle', 'electric_vehicle', 'hybrid_vehicle',
        'gasoline_vehicle', 'diesel_truck', 'electric_truck',
        'refrigerated_truck', 'rail', 'ship', 'air', 'drone',
        'local_delivery', 'customer_pickup'
      ];

      methods.forEach(method => {
        expect(CARBON_FACTORS[method]).toBeDefined();
        expect(typeof CARBON_FACTORS[method]).toBe('number');
      });
    });
  });

  describe('calculateCarbon', () => {
    it('should calculate zero carbon for zero distance', () => {
      const carbon = chain.calculateCarbon('diesel_truck', 0, 10);
      expect(carbon).toBe(0);
    });

    it('should calculate zero carbon for zero weight', () => {
      const carbon = chain.calculateCarbon('diesel_truck', 100, 0);
      expect(carbon).toBe(0);
    });

    it('should calculate zero carbon for walking', () => {
      const carbon = chain.calculateCarbon('walking', 10, 5);
      expect(carbon).toBe(0);
    });

    it('should calculate correct carbon for known values', () => {
      // diesel_truck = 0.15 kg CO2 per km per kg cargo
      const carbon = chain.calculateCarbon('diesel_truck', 100, 10);
      expect(carbon).toBe(0.15 * 100 * 10);
    });

    it('should increase linearly with distance', () => {
      const carbon1 = chain.calculateCarbon('diesel_truck', 50, 10);
      const carbon2 = chain.calculateCarbon('diesel_truck', 100, 10);
      expect(carbon2).toBe(carbon1 * 2);
    });

    it('should increase linearly with weight', () => {
      const carbon1 = chain.calculateCarbon('diesel_truck', 100, 5);
      const carbon2 = chain.calculateCarbon('diesel_truck', 100, 10);
      expect(carbon2).toBe(carbon1 * 2);
    });

    it('should compare transport methods correctly', () => {
      const distance = 100;
      const weight = 10;

      const walking = chain.calculateCarbon('walking', distance, weight);
      const electric = chain.calculateCarbon('electric_vehicle', distance, weight);
      const gasoline = chain.calculateCarbon('gasoline_vehicle', distance, weight);
      const diesel = chain.calculateCarbon('diesel_truck', distance, weight);
      const air = chain.calculateCarbon('air', distance, weight);

      expect(walking).toBe(0);
      expect(electric).toBeLessThan(gasoline);
      expect(gasoline).toBeLessThan(diesel);
      expect(diesel).toBeLessThan(air);
    });
  });

  describe('Distance Calculation', () => {
    it('should calculate zero distance for same location', () => {
      const location: TransportLocation = {
        latitude: 40.7128,
        longitude: -74.006,
        locationType: 'farm',
      };

      const distance = TransportChain.calculateDistance(location, location);
      expect(distance).toBe(0);
    });

    it('should calculate correct distance for known locations', () => {
      // New York to Los Angeles is approximately 3940 km
      const newYork: TransportLocation = {
        latitude: 40.7128,
        longitude: -74.006,
        locationType: 'warehouse',
      };

      const losAngeles: TransportLocation = {
        latitude: 34.0522,
        longitude: -118.2437,
        locationType: 'warehouse',
      };

      const distance = TransportChain.calculateDistance(newYork, losAngeles);
      expect(distance).toBeGreaterThan(3900);
      expect(distance).toBeLessThan(4000);
    });

    it('should calculate short distances accurately', () => {
      // Two points 1km apart (approximately)
      const point1: TransportLocation = {
        latitude: 40.7128,
        longitude: -74.006,
        locationType: 'farm',
      };

      const point2: TransportLocation = {
        latitude: 40.7218, // ~1km north
        longitude: -74.006,
        locationType: 'farm',
      };

      const distance = TransportChain.calculateDistance(point1, point2);
      expect(distance).toBeGreaterThan(0.9);
      expect(distance).toBeLessThan(1.1);
    });

    it('should be symmetric', () => {
      const pointA: TransportLocation = {
        latitude: 40.7128,
        longitude: -74.006,
        locationType: 'farm',
      };

      const pointB: TransportLocation = {
        latitude: 34.0522,
        longitude: -118.2437,
        locationType: 'warehouse',
      };

      const distanceAB = TransportChain.calculateDistance(pointA, pointB);
      const distanceBA = TransportChain.calculateDistance(pointB, pointA);

      expect(distanceAB).toBeCloseTo(distanceBA, 5);
    });

    it('should handle crossing the prime meridian', () => {
      const london: TransportLocation = {
        latitude: 51.5074,
        longitude: -0.1278,
        locationType: 'warehouse',
      };

      const paris: TransportLocation = {
        latitude: 48.8566,
        longitude: 2.3522,
        locationType: 'warehouse',
      };

      const distance = TransportChain.calculateDistance(london, paris);
      // London to Paris is approximately 344 km
      expect(distance).toBeGreaterThan(330);
      expect(distance).toBeLessThan(360);
    });

    it('should handle crossing the equator', () => {
      const north: TransportLocation = {
        latitude: 10,
        longitude: 0,
        locationType: 'farm',
      };

      const south: TransportLocation = {
        latitude: -10,
        longitude: 0,
        locationType: 'farm',
      };

      const distance = TransportChain.calculateDistance(north, south);
      // 20 degrees of latitude is approximately 2222 km
      expect(distance).toBeGreaterThan(2200);
      expect(distance).toBeLessThan(2250);
    });
  });

  describe('Carbon Tracking in Events', () => {
    it('should auto-calculate carbon if not provided', () => {
      const event = {
        id: 'test-carbon-auto',
        timestamp: new Date().toISOString(),
        eventType: 'seed_acquisition' as const,
        fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const },
        toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const },
        distanceKm: 100,
        durationMinutes: 120,
        transportMethod: 'diesel_truck' as const,
        carbonFootprintKg: 0, // Will be auto-calculated
        senderId: 'sender',
        receiverId: 'receiver',
        status: 'verified' as const,
        seedBatchId: 'batch-001',
        sourceType: 'seed_bank' as const,
        species: 'Test',
        quantity: 100,
        quantityUnit: 'seeds' as const,
        generation: 1,
      };

      const block = chain.recordEvent(event);
      expect(block.transportEvent.carbonFootprintKg).toBeGreaterThan(0);
    });

    it('should accumulate carbon across chain', () => {
      const event1 = {
        id: 'carbon-chain-1',
        timestamp: new Date().toISOString(),
        eventType: 'seed_acquisition' as const,
        fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const },
        toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const },
        distanceKm: 100,
        durationMinutes: 120,
        transportMethod: 'diesel_truck' as const,
        carbonFootprintKg: 0,
        senderId: 'sender',
        receiverId: 'receiver',
        status: 'verified' as const,
        seedBatchId: 'batch-001',
        sourceType: 'seed_bank' as const,
        species: 'Test',
        quantity: 100,
        quantityUnit: 'seeds' as const,
        generation: 1,
      };

      const event2 = { ...event1, id: 'carbon-chain-2', distanceKm: 200 };

      const block1 = chain.recordEvent(event1);
      const block2 = chain.recordEvent(event2);

      expect(block2.cumulativeCarbonKg).toBeGreaterThan(block1.cumulativeCarbonKg);
      expect(block2.cumulativeCarbonKg).toBeCloseTo(
        block1.cumulativeCarbonKg + block2.transportEvent.carbonFootprintKg,
        5
      );
    });
  });

  describe('Environmental Impact Calculations', () => {
    it('should calculate carbon savings vs conventional', () => {
      const userId = 'eco-user';

      const event = {
        id: 'eco-event',
        timestamp: new Date().toISOString(),
        eventType: 'seed_acquisition' as const,
        fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const },
        toLocation: { latitude: 40.1, longitude: -74, locationType: 'farm' as const },
        distanceKm: 10, // Very short local distance
        durationMinutes: 20,
        transportMethod: 'bicycle' as const, // Zero carbon
        carbonFootprintKg: 0,
        senderId: userId,
        receiverId: userId,
        status: 'verified' as const,
        seedBatchId: 'batch-eco',
        sourceType: 'seed_bank' as const,
        species: 'Test',
        quantity: 1000,
        quantityUnit: 'grams' as const,
        generation: 1,
      };

      chain.recordEvent(event);
      const impact = chain.getEnvironmentalImpact(userId);

      // Local, zero-carbon transport should save significantly vs conventional
      expect(impact.comparisonToConventional.carbonSaved).toBeGreaterThan(0);
      expect(impact.comparisonToConventional.milesSaved).toBeGreaterThan(0);
      expect(impact.comparisonToConventional.percentageReduction).toBeGreaterThan(0);
    });

    it('should break down impact by transport method', () => {
      const userId = 'breakdown-user';

      // Record multiple events with different methods
      const methods: TransportMethod[] = ['bicycle', 'electric_vehicle', 'diesel_truck'];

      methods.forEach((method, i) => {
        chain.recordEvent({
          id: `breakdown-${i}`,
          timestamp: new Date().toISOString(),
          eventType: 'seed_acquisition' as const,
          fromLocation: { latitude: 40, longitude: -74, locationType: 'seed_bank' as const },
          toLocation: { latitude: 41, longitude: -74, locationType: 'farm' as const },
          distanceKm: 50,
          durationMinutes: 60,
          transportMethod: method,
          carbonFootprintKg: 0,
          senderId: userId,
          receiverId: userId,
          status: 'verified' as const,
          seedBatchId: `batch-${i}`,
          sourceType: 'seed_bank' as const,
          species: 'Test',
          quantity: 100,
          quantityUnit: 'seeds' as const,
          generation: 1,
        });
      });

      const impact = chain.getEnvironmentalImpact(userId);

      expect(impact.breakdownByMethod['bicycle']).toBeDefined();
      expect(impact.breakdownByMethod['electric_vehicle']).toBeDefined();
      expect(impact.breakdownByMethod['diesel_truck']).toBeDefined();

      // Bicycle should have zero carbon
      expect(impact.breakdownByMethod['bicycle'].carbon).toBe(0);

      // Diesel should have highest carbon
      expect(impact.breakdownByMethod['diesel_truck'].carbon)
        .toBeGreaterThan(impact.breakdownByMethod['electric_vehicle'].carbon);
    });
  });
});
