/**
 * TransportChain Tests
 * Tests for the transport tracking blockchain implementation
 */

import {
  TransportChain,
  getTransportChain,
  setTransportChain,
} from '../../../lib/transport/tracker';
import {
  SeedAcquisitionEvent,
  PlantingEvent,
  GrowingTransportEvent,
  HarvestEvent,
  SeedSavingEvent,
  TransportLocation,
  CARBON_FACTORS,
} from '../../../lib/transport/types';

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

  beforeEach(() => {
    chain = new TransportChain(2); // Lower difficulty for faster tests
  });

  describe('Initialization', () => {
    it('should create genesis block on initialization', () => {
      expect(chain.chain.length).toBe(1);
      expect(chain.chain[0].index).toBe(0);
      expect(chain.chain[0].previousHash).toBe('0');
      expect(chain.chain[0].transportEvent.eventType).toBe('seed_acquisition');
    });

    it('should set default difficulty', () => {
      const defaultChain = new TransportChain();
      expect(defaultChain.difficulty).toBe(3);
    });

    it('should allow custom difficulty', () => {
      expect(chain.difficulty).toBe(2);
    });
  });

  describe('Recording Events', () => {
    it('should record seed acquisition event', () => {
      const event: SeedAcquisitionEvent = createSeedAcquisitionEvent();
      const block = chain.recordEvent(event);

      expect(block.index).toBe(1);
      expect(block.transportEvent.eventType).toBe('seed_acquisition');
      expect(chain.chain.length).toBe(2);
    });

    it('should record planting event', () => {
      const seedEvent = createSeedAcquisitionEvent();
      chain.recordEvent(seedEvent);

      const plantingEvent: PlantingEvent = createPlantingEvent();
      const block = chain.recordEvent(plantingEvent);

      expect(block.transportEvent.eventType).toBe('planting');
      expect((block.transportEvent as PlantingEvent).plantIds.length).toBe(2);
    });

    it('should record growing transport event', () => {
      const seedEvent = createSeedAcquisitionEvent();
      chain.recordEvent(seedEvent);

      const plantingEvent = createPlantingEvent();
      chain.recordEvent(plantingEvent);

      const growingEvent: GrowingTransportEvent = {
        id: 'growing-001',
        timestamp: new Date().toISOString(),
        eventType: 'growing_transport',
        fromLocation: createLocation('greenhouse'),
        toLocation: createLocation('farm'),
        distanceKm: 5,
        durationMinutes: 30,
        transportMethod: 'electric_vehicle',
        carbonFootprintKg: 0,
        senderId: 'grower-1',
        receiverId: 'grower-1',
        status: 'verified',
        plantIds: ['plant-001', 'plant-002'],
        reason: 'transplant',
        plantStage: 'seedling',
        handlingMethod: 'potted',
        rootDisturbance: 'minimal',
        acclimatizationRequired: true,
        acclimatizationDays: 3,
      };

      const block = chain.recordEvent(growingEvent);
      expect(block.transportEvent.eventType).toBe('growing_transport');
    });

    it('should record harvest event', () => {
      const harvestEvent: HarvestEvent = {
        id: 'harvest-001',
        timestamp: new Date().toISOString(),
        eventType: 'harvest',
        fromLocation: createLocation('farm'),
        toLocation: createLocation('warehouse'),
        distanceKm: 10,
        durationMinutes: 45,
        transportMethod: 'electric_truck',
        carbonFootprintKg: 0,
        senderId: 'grower-1',
        receiverId: 'distributor-1',
        status: 'verified',
        plantIds: ['plant-001', 'plant-002'],
        harvestBatchId: 'harvest-batch-001',
        harvestType: 'full',
        produceType: 'tomatoes',
        grossWeight: 10,
        netWeight: 9.5,
        weightUnit: 'kg',
        packagingType: 'crates',
        temperatureRequired: { min: 10, max: 15, optimal: 12, unit: 'celsius' },
        shelfLifeHours: 168,
        seedsSaved: false,
      };

      const block = chain.recordEvent(harvestEvent);
      expect(block.transportEvent.eventType).toBe('harvest');
      expect((block.transportEvent as HarvestEvent).netWeight).toBe(9.5);
    });

    it('should accumulate carbon footprint across blocks', () => {
      const event1 = createSeedAcquisitionEvent();
      event1.distanceKm = 10;
      event1.transportMethod = 'gasoline_vehicle';
      const block1 = chain.recordEvent(event1);

      const event2 = createSeedAcquisitionEvent();
      event2.id = 'seed-002';
      event2.distanceKm = 20;
      event2.transportMethod = 'diesel_truck';
      const block2 = chain.recordEvent(event2);

      expect(block2.cumulativeCarbonKg).toBeGreaterThan(block1.cumulativeCarbonKg);
    });

    it('should accumulate food miles across blocks', () => {
      const event1 = createSeedAcquisitionEvent();
      event1.distanceKm = 10;
      const block1 = chain.recordEvent(event1);

      const event2 = createSeedAcquisitionEvent();
      event2.id = 'seed-002';
      event2.distanceKm = 20;
      const block2 = chain.recordEvent(event2);

      expect(block2.cumulativeFoodMiles).toBe(
        block1.cumulativeFoodMiles + 20
      );
    });
  });

  describe('Chain Integrity', () => {
    it('should verify chain integrity', () => {
      chain.recordEvent(createSeedAcquisitionEvent());
      chain.recordEvent(createPlantingEvent());

      expect(chain.isChainValid()).toBe(true);
    });

    it('should detect tampered blocks', () => {
      chain.recordEvent(createSeedAcquisitionEvent());

      // Tamper with the chain
      chain.chain[1].transportEvent.distanceKm = 999;

      expect(chain.isChainValid()).toBe(false);
    });

    it('should detect broken chain links', () => {
      chain.recordEvent(createSeedAcquisitionEvent());
      chain.recordEvent(createPlantingEvent());

      // Break the chain link
      chain.chain[2].previousHash = 'fake-hash';

      expect(chain.isChainValid()).toBe(false);
    });

    it('should maintain proper hash linking', () => {
      chain.recordEvent(createSeedAcquisitionEvent());
      chain.recordEvent(createPlantingEvent());

      for (let i = 1; i < chain.chain.length; i++) {
        expect(chain.chain[i].previousHash).toBe(chain.chain[i - 1].hash);
      }
    });
  });

  describe('Plant Journey', () => {
    it('should track plant journey across events', () => {
      const plantId = 'plant-001';

      // Record planting
      const plantingEvent = createPlantingEvent();
      plantingEvent.plantIds = [plantId];
      chain.recordEvent(plantingEvent);

      // Record growing transport
      const growingEvent: GrowingTransportEvent = {
        id: 'growing-001',
        timestamp: new Date().toISOString(),
        eventType: 'growing_transport',
        fromLocation: createLocation('greenhouse'),
        toLocation: createLocation('farm'),
        distanceKm: 5,
        durationMinutes: 30,
        transportMethod: 'walking',
        carbonFootprintKg: 0,
        senderId: 'grower-1',
        receiverId: 'grower-1',
        status: 'verified',
        plantIds: [plantId],
        reason: 'transplant',
        plantStage: 'vegetative',
        handlingMethod: 'potted',
        rootDisturbance: 'minimal',
        acclimatizationRequired: false,
      };
      chain.recordEvent(growingEvent);

      const journey = chain.getPlantJourney(plantId);
      expect(journey).not.toBeNull();
      expect(journey?.plantId).toBe(plantId);
      expect(journey?.events.length).toBe(2);
      expect(journey?.totalFoodMiles).toBeGreaterThan(0);
    });

    it('should return null for unknown plant', () => {
      const journey = chain.getPlantJourney('unknown-plant');
      expect(journey).toBeNull();
    });

    it('should calculate correct journey metrics', () => {
      const plantId = 'plant-journey-test';

      const plantingEvent = createPlantingEvent();
      plantingEvent.plantIds = [plantId];
      plantingEvent.distanceKm = 10;
      plantingEvent.durationMinutes = 60;
      chain.recordEvent(plantingEvent);

      const journey = chain.getPlantJourney(plantId);
      expect(journey?.totalFoodMiles).toBe(10);
      expect(journey?.daysInTransit).toBe(0); // 60 min = ~0 days
    });

    it('should track current stage correctly', () => {
      const plantId = 'stage-test-plant';

      const plantingEvent = createPlantingEvent();
      plantingEvent.plantIds = [plantId];
      chain.recordEvent(plantingEvent);

      let journey = chain.getPlantJourney(plantId);
      expect(journey?.currentStage).toBe('seedling');

      const growingEvent: GrowingTransportEvent = {
        id: 'growing-stage',
        timestamp: new Date().toISOString(),
        eventType: 'growing_transport',
        fromLocation: createLocation('greenhouse'),
        toLocation: createLocation('farm'),
        distanceKm: 1,
        durationMinutes: 10,
        transportMethod: 'walking',
        carbonFootprintKg: 0,
        senderId: 'grower-1',
        receiverId: 'grower-1',
        status: 'verified',
        plantIds: [plantId],
        reason: 'relocation',
        plantStage: 'flowering',
        handlingMethod: 'potted',
        rootDisturbance: 'none',
        acclimatizationRequired: false,
      };
      chain.recordEvent(growingEvent);

      journey = chain.getPlantJourney(plantId);
      expect(journey?.currentStage).toBe('flowering');
    });
  });

  describe('Environmental Impact', () => {
    it('should calculate environmental impact for user', () => {
      const userId = 'user-impact-test';

      const event = createSeedAcquisitionEvent();
      event.senderId = userId;
      event.distanceKm = 50;
      event.transportMethod = 'diesel_truck';
      chain.recordEvent(event);

      const impact = chain.getEnvironmentalImpact(userId);
      expect(impact.totalFoodMiles).toBe(50);
      expect(impact.totalCarbonKg).toBeGreaterThan(0);
      expect(impact.breakdownByMethod['diesel_truck']).toBeDefined();
      expect(impact.breakdownByEventType['seed_acquisition']).toBeDefined();
    });

    it('should compare to conventional agriculture', () => {
      const userId = 'compare-test';

      const event = createSeedAcquisitionEvent();
      event.senderId = userId;
      event.distanceKm = 10; // Very short distance
      event.transportMethod = 'bicycle'; // Zero carbon
      chain.recordEvent(event);

      const impact = chain.getEnvironmentalImpact(userId);
      expect(impact.comparisonToConventional.milesSaved).toBeGreaterThan(0);
    });

    it('should return zero impact for user with no events', () => {
      const impact = chain.getEnvironmentalImpact('nonexistent-user');
      expect(impact.totalCarbonKg).toBe(0);
      expect(impact.totalFoodMiles).toBe(0);
    });
  });

  describe('QR Code Generation', () => {
    it('should generate valid QR data for plant', () => {
      const plantId = 'qr-test-plant';

      const plantingEvent = createPlantingEvent();
      plantingEvent.plantIds = [plantId];
      chain.recordEvent(plantingEvent);

      const qrData = chain.generateQRData(plantId, undefined);
      expect(qrData.plantId).toBe(plantId);
      expect(qrData.quickLookupUrl).toContain(plantId);
      expect(qrData.lineageHash).toBeDefined();
      expect(qrData.verificationCode).toMatch(/^[A-F0-9]{8}$/);
    });

    it('should generate QR data for batch', () => {
      const batchId = 'seed-batch-001';

      const event = createSeedAcquisitionEvent();
      event.seedBatchId = batchId;
      chain.recordEvent(event);

      const qrData = chain.generateQRData(undefined, batchId);
      expect(qrData.batchId).toBe(batchId);
      expect(qrData.lastEventType).toBe('seed_acquisition');
    });

    it('should include blockchain address', () => {
      const qrData = chain.generateQRData('any-id', undefined);
      expect(qrData.blockchainAddress).toHaveLength(42);
    });
  });

  describe('Serialization', () => {
    it('should export to JSON', () => {
      chain.recordEvent(createSeedAcquisitionEvent());

      const json = chain.toJSON();
      expect(json).toHaveProperty('difficulty');
      expect(json).toHaveProperty('chain');
      expect((json as any).chain.length).toBe(2);
    });

    it('should import from JSON', () => {
      chain.recordEvent(createSeedAcquisitionEvent());
      chain.recordEvent(createPlantingEvent());

      const json = chain.toJSON();
      const restored = TransportChain.fromJSON(json);

      expect(restored.chain.length).toBe(chain.chain.length);
      expect(restored.isChainValid()).toBe(true);
    });

    it('should rebuild indexes after import', () => {
      const plantId = 'import-test-plant';
      const plantingEvent = createPlantingEvent();
      plantingEvent.plantIds = [plantId];
      chain.recordEvent(plantingEvent);

      const json = chain.toJSON();
      const restored = TransportChain.fromJSON(json);

      const journey = restored.getPlantJourney(plantId);
      expect(journey).not.toBeNull();
    });
  });

  describe('Singleton', () => {
    it('should return same instance from getTransportChain', () => {
      const chain1 = getTransportChain();
      const chain2 = getTransportChain();
      expect(chain1).toBe(chain2);
    });

    it('should allow setting custom chain', () => {
      const customChain = new TransportChain(1);
      setTransportChain(customChain);
      expect(getTransportChain()).toBe(customChain);
    });
  });
});

// Helper functions
function createLocation(type: string): TransportLocation {
  return {
    latitude: 40.7128 + Math.random() * 0.1,
    longitude: -74.006 + Math.random() * 0.1,
    locationType: type as any,
    facilityName: `Test ${type}`,
  };
}

function createSeedAcquisitionEvent(): SeedAcquisitionEvent {
  return {
    id: `seed-${Date.now()}`,
    timestamp: new Date().toISOString(),
    eventType: 'seed_acquisition',
    fromLocation: createLocation('seed_bank'),
    toLocation: createLocation('greenhouse'),
    distanceKm: 25,
    durationMinutes: 45,
    transportMethod: 'electric_vehicle',
    carbonFootprintKg: 0,
    senderId: 'seed-bank-1',
    receiverId: 'grower-1',
    status: 'verified',
    seedBatchId: 'seed-batch-001',
    sourceType: 'seed_bank',
    species: 'Solanum lycopersicum',
    variety: 'Roma',
    quantity: 100,
    quantityUnit: 'seeds',
    generation: 1,
    germinationRate: 95,
    certifications: ['organic', 'heirloom'],
  };
}

function createPlantingEvent(): PlantingEvent {
  return {
    id: `planting-${Date.now()}`,
    timestamp: new Date().toISOString(),
    eventType: 'planting',
    fromLocation: createLocation('greenhouse'),
    toLocation: createLocation('greenhouse'),
    distanceKm: 0.01,
    durationMinutes: 5,
    transportMethod: 'walking',
    carbonFootprintKg: 0,
    senderId: 'grower-1',
    receiverId: 'grower-1',
    status: 'verified',
    seedBatchId: 'seed-batch-001',
    plantIds: ['plant-001', 'plant-002'],
    plantingMethod: 'indoor_start',
    quantityPlanted: 2,
    growingEnvironment: 'greenhouse',
  };
}
