/**
 * Vertical Farm Batch Lifecycle Integration Tests
 * Tests complete vertical farm batch from start to harvest
 */

import {
  VerticalFarmController,
} from '../../lib/vertical-farming/controller';
import { TransportChain, setTransportChain } from '../../lib/transport/tracker';
import {
  VerticalFarm,
  GrowingZone,
  ZoneEnvironmentReadings,
  CropBatch,
} from '../../lib/vertical-farming/types';
import {
  SeedAcquisitionEvent,
  PlantingEvent,
  HarvestEvent,
  TransportLocation,
} from '../../lib/transport/types';

describe('Vertical Farm Batch Lifecycle', () => {
  let controller: VerticalFarmController;
  let chain: TransportChain;

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

  describe('Complete Batch Lifecycle', () => {
    it('should complete full batch lifecycle from planting to harvest', () => {
      // Step 1: Register farm
      const farm = createVerticalFarm('vf-lifecycle-001');
      controller.registerFarm(farm);

      // Step 2: Select recipe and start batch
      const recipes = controller.getRecipes();
      const lettuceRecipe = recipes.find(r => r.cropType === 'lettuce')!;

      const batch = controller.startCropBatch(
        'vf-lifecycle-001',
        'zone-001',
        lettuceRecipe.id,
        'seed-batch-vf-001',
        100
      );

      expect(batch.status).toBe('germinating');
      expect(batch.plantCount).toBe(100);
      expect(batch.currentStage).toBe(lettuceRecipe.stages[0].name);

      // Step 3: Verify zone is updated
      const farmAfterPlanting = controller.getFarm('vf-lifecycle-001')!;
      const zone = farmAfterPlanting.zones.find(z => z.id === 'zone-001')!;

      expect(zone.status).toBe('planted');
      expect(zone.currentCrop).toBe('lettuce');
      expect(zone.plantIds.length).toBe(100);

      // Step 4: Record environment readings (simulate daily monitoring)
      for (let day = 0; day < 5; day++) {
        const readings = createGoodReadings();
        const alerts = controller.recordEnvironment('zone-001', readings);
        expect(alerts.length).toBe(0); // No alerts for good readings
      }

      // Step 5: Update batch progress
      const updatedBatch = controller.updateBatchProgress(batch.id);
      expect(updatedBatch.currentDay).toBeGreaterThanOrEqual(0);

      // Step 6: Complete harvest
      const completedBatch = controller.completeHarvest(batch.id, 18.0, 'A');

      expect(completedBatch.status).toBe('completed');
      expect(completedBatch.actualYieldKg).toBe(18.0);
      expect(completedBatch.qualityGrade).toBe('A');

      // Step 7: Verify zone is cleared
      const farmAfterHarvest = controller.getFarm('vf-lifecycle-001')!;
      const zoneAfterHarvest = farmAfterHarvest.zones.find(z => z.id === 'zone-001')!;

      expect(zoneAfterHarvest.status).toBe('cleaning');
      expect(zoneAfterHarvest.currentCrop).toBe('');
      expect(zoneAfterHarvest.plantIds.length).toBe(0);
    });
  });

  describe('Environment Monitoring During Growth', () => {
    it('should track health score throughout growth', () => {
      const farm = createVerticalFarm('vf-health-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'vf-health-001',
        'zone-001',
        recipes[0].id,
        'seed-batch-001',
        100
      );

      const initialHealth = batch.healthScore;
      expect(initialHealth).toBe(100);

      // Good readings - health should stay high
      for (let i = 0; i < 3; i++) {
        controller.recordEnvironment('zone-001', createGoodReadings());
      }
      expect(batch.healthScore).toBe(100);

      // Bad readings - health should decrease
      controller.recordEnvironment('zone-001', createBadReadings());
      expect(batch.healthScore).toBeLessThan(100);

      // Critical readings - health should decrease more
      controller.recordEnvironment('zone-001', createCriticalReadings());
      expect(batch.healthScore).toBeLessThan(95);
    });

    it('should log environment readings to batch', () => {
      const farm = createVerticalFarm('vf-log-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'vf-log-001',
        'zone-001',
        recipes[0].id,
        'seed-batch-001',
        100
      );

      // Record multiple readings
      for (let i = 0; i < 5; i++) {
        controller.recordEnvironment('zone-001', createGoodReadings());
      }

      expect(batch.environmentLog.length).toBe(5);
    });
  });

  describe('Multi-Zone Operations', () => {
    it('should manage multiple batches across zones', () => {
      const farm = createVerticalFarm('vf-multi-001');
      farm.zones = [
        createGrowingZone('zone-001', 'Zone A', 1),
        createGrowingZone('zone-002', 'Zone B', 1),
        createGrowingZone('zone-003', 'Zone C', 2),
      ];
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const lettuceRecipe = recipes.find(r => r.cropType === 'lettuce')!;
      const basilRecipe = recipes.find(r => r.cropType === 'basil')!;
      const microgreensRecipe = recipes.find(r => r.cropType === 'microgreens')!;

      // Start different batches in different zones
      const batch1 = controller.startCropBatch(
        'vf-multi-001', 'zone-001', lettuceRecipe.id, 'seed-001', 100
      );
      const batch2 = controller.startCropBatch(
        'vf-multi-001', 'zone-002', basilRecipe.id, 'seed-002', 50
      );
      const batch3 = controller.startCropBatch(
        'vf-multi-001', 'zone-003', microgreensRecipe.id, 'seed-003', 200
      );

      expect(batch1.cropType).toBe('lettuce');
      expect(batch2.cropType).toBe('basil');
      expect(batch3.cropType).toBe('microgreens');

      // Verify zones have correct crops
      const farmAfter = controller.getFarm('vf-multi-001')!;
      expect(farmAfter.zones.find(z => z.id === 'zone-001')!.currentCrop).toBe('lettuce');
      expect(farmAfter.zones.find(z => z.id === 'zone-002')!.currentCrop).toBe('basil');
      expect(farmAfter.zones.find(z => z.id === 'zone-003')!.currentCrop).toBe('microgreens');
    });
  });

  describe('Analytics Generation', () => {
    it('should generate analytics after batch completion', () => {
      const farm = createVerticalFarm('vf-analytics-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();

      // Complete multiple batches
      for (let i = 0; i < 3; i++) {
        const batch = controller.startCropBatch(
          'vf-analytics-001',
          'zone-001',
          recipes[0].id,
          `seed-batch-${i}`,
          100
        );
        controller.completeHarvest(batch.id, 15 + i, 'A');
      }

      const analytics = controller.generateAnalytics('vf-analytics-001', 30);

      expect(analytics.cropCyclesCompleted).toBe(3);
      expect(analytics.totalYieldKg).toBeGreaterThan(0);
      expect(analytics.gradeAPercent).toBe(100);
    });

    it('should calculate efficiency metrics', () => {
      const farm = createVerticalFarm('vf-efficiency-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'vf-efficiency-001',
        'zone-001',
        recipes[0].id,
        'seed-batch-001',
        100
      );

      controller.completeHarvest(batch.id, 18.0, 'A');

      const analytics = controller.generateAnalytics('vf-efficiency-001', 30);

      expect(analytics.yieldPerSqmPerYear).toBeGreaterThan(0);
      expect(analytics.averageQualityScore).toBeGreaterThan(0);
      expect(analytics.cropSuccessRate).toBe(100);
    });
  });

  describe('Integration with Transport Chain', () => {
    it('should track VF produce through transport chain', () => {
      const farm = createVerticalFarm('vf-transport-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'vf-transport-001',
        'zone-001',
        recipes[0].id,
        'seed-transport-001',
        100
      );

      // Record seed acquisition in transport chain
      const seedEvent: SeedAcquisitionEvent = {
        id: 'vf-transport-seed-001',
        timestamp: new Date().toISOString(),
        eventType: 'seed_acquisition',
        fromLocation: createLocation('seed_bank', 'Seed Supplier'),
        toLocation: createLocation('vertical_farm', 'VF Facility'),
        distanceKm: 20,
        durationMinutes: 30,
        transportMethod: 'electric_vehicle',
        carbonFootprintKg: 0,
        senderId: 'seed-supplier',
        receiverId: 'vf-operator',
        status: 'verified',
        seedBatchId: 'seed-transport-001',
        sourceType: 'purchase',
        species: recipes[0].cropType,
        quantity: 100,
        quantityUnit: 'seeds',
        generation: 1,
        certifications: ['organic'],
      };

      chain.recordEvent(seedEvent);

      // Complete VF harvest
      controller.completeHarvest(batch.id, 18.0, 'A');

      // Record harvest in transport chain
      const harvestEvent: HarvestEvent = {
        id: 'vf-transport-harvest-001',
        timestamp: new Date().toISOString(),
        eventType: 'harvest',
        fromLocation: createLocation('vertical_farm', 'VF Facility'),
        toLocation: createLocation('warehouse', 'Distribution Center'),
        distanceKm: 10,
        durationMinutes: 20,
        transportMethod: 'electric_truck',
        carbonFootprintKg: 0,
        senderId: 'vf-operator',
        receiverId: 'distributor',
        status: 'verified',
        plantIds: batch.plantIds.slice(0, 10),
        harvestBatchId: batch.id,
        harvestType: 'full',
        produceType: recipes[0].cropType,
        grossWeight: 20,
        netWeight: 18,
        weightUnit: 'kg',
        qualityGrade: 'A',
        packagingType: 'sustainable_packaging',
        temperatureRequired: { min: 2, max: 8, optimal: 4, unit: 'celsius' },
        shelfLifeHours: 168,
        seedsSaved: false,
      };

      chain.recordEvent(harvestEvent);

      // Verify chain integrity
      expect(chain.isChainValid()).toBe(true);

      // Calculate environmental impact
      const impact = chain.getEnvironmentalImpact('vf-operator');
      expect(impact.totalFoodMiles).toBeLessThan(50); // Very low for VF
    });
  });

  describe('Recipe Stage Transitions', () => {
    it('should update environment targets based on stage', () => {
      const farm = createVerticalFarm('vf-stage-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const recipe = recipes[0];

      const batch = controller.startCropBatch(
        'vf-stage-001',
        'zone-001',
        recipe.id,
        'seed-stage-001',
        100
      );

      // Initial stage should be first stage
      expect(batch.currentStage).toBe(recipe.stages[0].name);

      // Get zone and check targets match first stage
      const farmAfter = controller.getFarm('vf-stage-001')!;
      const zone = farmAfter.zones.find(z => z.id === 'zone-001')!;
      const firstStage = recipe.stages[0];

      expect(zone.environmentTargets.temperatureC.target).toBe(firstStage.temperature.day);
    });
  });

  describe('Batch Failure Handling', () => {
    it('should track issues that affect batch', () => {
      const farm = createVerticalFarm('vf-issue-001');
      controller.registerFarm(farm);

      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'vf-issue-001',
        'zone-001',
        recipes[0].id,
        'seed-issue-001',
        100
      );

      // Simulate multiple environment issues
      for (let i = 0; i < 20; i++) {
        controller.recordEnvironment('zone-001', createCriticalReadings());
      }

      // Health should be significantly reduced
      expect(batch.healthScore).toBeLessThan(50);
    });
  });
});

// Helper functions
function createVerticalFarm(id: string): VerticalFarm {
  return {
    id,
    name: 'Test Vertical Farm',
    ownerId: 'owner-001',
    location: {
      latitude: 40.7128,
      longitude: -74.006,
      address: '123 Farm Street',
      city: 'New York',
      country: 'USA',
      timezone: 'America/New_York',
    },
    specs: {
      totalAreaSqm: 500,
      growingAreaSqm: 400,
      numberOfLevels: 4,
      ceilingHeightM: 3,
      totalGrowingPositions: 4000,
      currentActivePlants: 0,
      powerCapacityKw: 100,
      waterStorageL: 5000,
      backupPowerHours: 24,
      certifications: ['organic'],
      buildingType: 'warehouse',
      insulation: 'high_efficiency',
    },
    zones: [createGrowingZone('zone-001', 'Zone A', 1)],
    environmentalControl: {
      hvacUnits: [],
      co2Injection: { type: 'tank', capacityKg: 50, currentLevelKg: 40, injectionRateKgPerHour: 2, status: 'maintaining' },
      humidification: { type: 'ultrasonic', capacityLPerHour: 10, status: 'running', currentOutput: 5 },
      airCirculation: { fans: [] },
      controlMode: 'adaptive',
    },
    irrigationSystem: {
      type: 'recirculating',
      freshWaterTankL: 2000,
      freshWaterLevelL: 1800,
      nutrientTankL: 500,
      nutrientLevelL: 450,
      wasteTankL: 200,
      wasteLevelL: 50,
      waterTreatment: { ro: true, uv: true, ozone: false, filtration: '10 micron' },
      pumps: [],
      irrigationSchedule: [],
    },
    lightingSystem: {
      type: 'LED',
      fixtures: [],
      lightSchedules: [],
      totalWattage: 5000,
      currentWattage: 3000,
      efficacyUmolJ: 2.5,
    },
    nutrientSystem: {
      mixingMethod: 'fully_auto',
      stockSolutions: [],
      dosingPumps: [],
      currentRecipe: {
        id: 'default',
        name: 'Default',
        cropType: 'general',
        growthStage: 'vegetative',
        targetEc: 1.5,
        targetPh: 6.0,
        ratios: { n: 200, p: 50, k: 200, ca: 200, mg: 50, s: 100, fe: 5, mn: 0.5, zn: 0.3, cu: 0.1, b: 0.5, mo: 0.05 },
        dosingRatiosMlPerL: [],
      },
      monitoring: {
        ec: 1.5,
        ph: 6.0,
        lastCalibration: new Date().toISOString(),
        calibrationDue: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
      },
    },
    automationLevel: 'semi_automated',
    automationSystems: [],
    status: 'operational',
    operationalSince: '2024-01-01',
    lastMaintenanceDate: new Date().toISOString(),
    currentCapacityUtilization: 75,
    averageYieldEfficiency: 85,
    energyEfficiencyScore: 80,
  };
}

function createGrowingZone(id: string, name: string, level: number): GrowingZone {
  return {
    id,
    name,
    level,
    areaSqm: 50,
    lengthM: 10,
    widthM: 5,
    growingMethod: 'NFT',
    plantPositions: 500,
    currentCrop: '',
    plantIds: [],
    plantingDate: '',
    expectedHarvestDate: '',
    environmentTargets: {
      temperatureC: { min: 18, max: 24, target: 21 },
      humidityPercent: { min: 60, max: 80, target: 70 },
      co2Ppm: { min: 800, max: 1200, target: 1000 },
      lightPpfd: { min: 200, max: 400, target: 300 },
      lightHours: 16,
      nutrientEc: { min: 1.2, max: 1.8, target: 1.5 },
      nutrientPh: { min: 5.8, max: 6.2, target: 6.0 },
      waterTempC: { min: 18, max: 22, target: 20 },
    },
    currentEnvironment: {
      timestamp: new Date().toISOString(),
      temperatureC: 21,
      humidityPercent: 70,
      co2Ppm: 1000,
      vpd: 1.0,
      ppfd: 300,
      dli: 17,
      waterTempC: 20,
      ec: 1.5,
      ph: 6.0,
      dissolvedOxygenPpm: 8,
      airflowMs: 0.5,
      alerts: [],
    },
    status: 'empty',
  };
}

function createLocation(type: string, name: string): TransportLocation {
  return {
    latitude: 40.7 + Math.random() * 0.1,
    longitude: -74.0 + Math.random() * 0.1,
    locationType: type as any,
    facilityName: name,
  };
}

function createGoodReadings(): ZoneEnvironmentReadings {
  return {
    timestamp: new Date().toISOString(),
    temperatureC: 21,
    humidityPercent: 70,
    co2Ppm: 1000,
    vpd: 1.0,
    ppfd: 300,
    dli: 17,
    waterTempC: 20,
    ec: 1.5,
    ph: 6.0,
    dissolvedOxygenPpm: 8,
    airflowMs: 0.5,
    alerts: [],
  };
}

function createBadReadings(): ZoneEnvironmentReadings {
  return {
    timestamp: new Date().toISOString(),
    temperatureC: 28, // Too high
    humidityPercent: 55, // Too low
    co2Ppm: 1000,
    vpd: 1.0,
    ppfd: 300,
    dli: 17,
    waterTempC: 20,
    ec: 1.5,
    ph: 6.0,
    dissolvedOxygenPpm: 8,
    airflowMs: 0.5,
    alerts: [],
  };
}

function createCriticalReadings(): ZoneEnvironmentReadings {
  return {
    timestamp: new Date().toISOString(),
    temperatureC: 35, // Critical high
    humidityPercent: 40, // Critical low
    co2Ppm: 500, // Low
    vpd: 2.5,
    ppfd: 100, // Low
    dli: 5,
    waterTempC: 30, // Too high
    ec: 0.5, // Too low
    ph: 7.5, // Too high
    dissolvedOxygenPpm: 4,
    airflowMs: 0.1,
    alerts: [],
  };
}
