/**
 * Environment Alert Tests
 * Tests for environment monitoring and alert generation
 */

import {
  VerticalFarmController,
} from '../../../lib/vertical-farming/controller';
import {
  VerticalFarm,
  GrowingZone,
  ZoneEnvironmentReadings,
  EnvironmentAlert,
} from '../../../lib/vertical-farming/types';

describe('Environment Alerts', () => {
  let controller: VerticalFarmController;

  beforeEach(() => {
    controller = new VerticalFarmController();
  });

  describe('Temperature Alerts', () => {
    it('should detect low temperature', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 15, // Below min of 18
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const tempAlert = alerts.find(a => a.parameter === 'temperature');
      expect(tempAlert).toBeDefined();
      expect(tempAlert?.type).toBe('low');
    });

    it('should detect high temperature', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 30, // Above max of 24
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const tempAlert = alerts.find(a => a.parameter === 'temperature');
      expect(tempAlert).toBeDefined();
      expect(tempAlert?.type).toBe('high');
    });

    it('should detect critical low temperature', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 10, // More than 5 below min
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const tempAlert = alerts.find(a => a.parameter === 'temperature');
      expect(tempAlert?.type).toBe('critical_low');
    });

    it('should detect critical high temperature', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 35, // More than 5 above max
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const tempAlert = alerts.find(a => a.parameter === 'temperature');
      expect(tempAlert?.type).toBe('critical_high');
    });

    it('should not alert for normal temperature', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 21, // Within range
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const tempAlert = alerts.find(a => a.parameter === 'temperature');
      expect(tempAlert).toBeUndefined();
    });
  });

  describe('Humidity Alerts', () => {
    it('should detect low humidity', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        humidityPercent: 50, // Below min of 60
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const humidityAlert = alerts.find(a => a.parameter === 'humidity');
      expect(humidityAlert).toBeDefined();
      expect(humidityAlert?.type).toBe('low');
    });

    it('should detect high humidity', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        humidityPercent: 90, // Above max of 80
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const humidityAlert = alerts.find(a => a.parameter === 'humidity');
      expect(humidityAlert).toBeDefined();
      expect(humidityAlert?.type).toBe('high');
    });
  });

  describe('EC Alerts', () => {
    it('should detect low EC', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        ec: 0.8, // Below min of 1.2
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const ecAlert = alerts.find(a => a.parameter === 'ec');
      expect(ecAlert).toBeDefined();
      expect(ecAlert?.type).toBe('low');
    });

    it('should detect high EC', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        ec: 2.5, // Above max of 1.8
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const ecAlert = alerts.find(a => a.parameter === 'ec');
      expect(ecAlert).toBeDefined();
      expect(ecAlert?.type).toBe('high');
    });
  });

  describe('pH Alerts', () => {
    it('should detect low pH', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        ph: 5.2, // Below min of 5.8
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const phAlert = alerts.find(a => a.parameter === 'ph');
      expect(phAlert).toBeDefined();
      expect(phAlert?.type).toBe('low');
    });

    it('should detect high pH', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        ph: 7.0, // Above max of 6.2
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      const phAlert = alerts.find(a => a.parameter === 'ph');
      expect(phAlert).toBeDefined();
      expect(phAlert?.type).toBe('high');
    });
  });

  describe('Multiple Alerts', () => {
    it('should detect multiple issues simultaneously', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 30,
        humidityPercent: 40,
        ec: 0.5,
        ph: 7.5,
      });

      const alerts = controller.recordEnvironment('zone-001', readings);

      expect(alerts.length).toBeGreaterThanOrEqual(4);
      expect(alerts.some(a => a.parameter === 'temperature')).toBe(true);
      expect(alerts.some(a => a.parameter === 'humidity')).toBe(true);
      expect(alerts.some(a => a.parameter === 'ec')).toBe(true);
      expect(alerts.some(a => a.parameter === 'ph')).toBe(true);
    });
  });

  describe('Alert Properties', () => {
    it('should include threshold value', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 15,
      });

      const alerts = controller.recordEnvironment('zone-001', readings);
      const tempAlert = alerts.find(a => a.parameter === 'temperature');

      expect(tempAlert?.threshold).toBeDefined();
      expect(tempAlert?.threshold).toBe(18); // Min threshold
    });

    it('should include actual value', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 15,
      });

      const alerts = controller.recordEnvironment('zone-001', readings);
      const tempAlert = alerts.find(a => a.parameter === 'temperature');

      expect(tempAlert?.value).toBe(15);
    });

    it('should include timestamp', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 15,
      });

      const alerts = controller.recordEnvironment('zone-001', readings);
      const tempAlert = alerts.find(a => a.parameter === 'temperature');

      expect(tempAlert?.timestamp).toBeDefined();
    });

    it('should set acknowledged to false by default', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 15,
      });

      const alerts = controller.recordEnvironment('zone-001', readings);
      const tempAlert = alerts.find(a => a.parameter === 'temperature');

      expect(tempAlert?.acknowledged).toBe(false);
    });
  });

  describe('Zone Environment Updates', () => {
    it('should update zone current environment', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 22,
        humidityPercent: 65,
      });

      controller.recordEnvironment('zone-001', readings);

      const updatedFarm = controller.getFarm('farm-001');
      const zone = updatedFarm?.zones.find(z => z.id === 'zone-001');

      expect(zone?.currentEnvironment.temperatureC).toBe(22);
      expect(zone?.currentEnvironment.humidityPercent).toBe(65);
    });

    it('should store alerts in zone readings', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 30, // Will trigger alert
      });

      controller.recordEnvironment('zone-001', readings);

      const updatedFarm = controller.getFarm('farm-001');
      const zone = updatedFarm?.zones.find(z => z.id === 'zone-001');

      expect(zone?.currentEnvironment.alerts.length).toBeGreaterThan(0);
    });
  });

  describe('Batch Health Score', () => {
    it('should decrease health score on alerts', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

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

      const initialHealth = batch.healthScore;

      // Record problematic environment
      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 30, // High temp alert
      });

      controller.recordEnvironment('zone-001', readings);

      // Health should decrease
      expect(batch.healthScore).toBeLessThan(initialHealth);
    });

    it('should decrease health more for critical alerts', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

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

      const initialHealth = batch.healthScore;

      // Record critical environment issue
      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 5, // Critical low temp
      });

      controller.recordEnvironment('zone-001', readings);

      // Health should decrease significantly
      expect(batch.healthScore).toBeLessThan(initialHealth - 3);
    });

    it('should not go below 0', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

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

      // Record many critical issues
      for (let i = 0; i < 50; i++) {
        const readings: ZoneEnvironmentReadings = createReadings({
          temperatureC: 5,
        });
        controller.recordEnvironment('zone-001', readings);
      }

      expect(batch.healthScore).toBeGreaterThanOrEqual(0);
    });
  });

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

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

      const readings: ZoneEnvironmentReadings = createReadings({});
      controller.recordEnvironment('zone-001', readings);

      expect(batch.environmentLog.length).toBe(1);
      expect(batch.environmentLog[0].readings).toBeDefined();
    });

    it('should accumulate environment logs', () => {
      const farm = createVerticalFarm('farm-001');
      controller.registerFarm(farm);

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

      for (let i = 0; i < 5; i++) {
        const readings: ZoneEnvironmentReadings = createReadings({
          temperatureC: 20 + i,
        });
        controller.recordEnvironment('zone-001', readings);
      }

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

  describe('Unknown Zone', () => {
    it('should return empty alerts for unknown zone', () => {
      const readings: ZoneEnvironmentReadings = createReadings({
        temperatureC: 5, // Would normally trigger alert
      });

      const alerts = controller.recordEnvironment('unknown-zone', readings);
      expect(alerts.length).toBe(0);
    });
  });
});

// Helper functions
function createVerticalFarm(id: string): VerticalFarm {
  return {
    id,
    name: 'Test Farm',
    ownerId: 'owner-001',
    location: {
      latitude: 40.7128,
      longitude: -74.006,
      address: '123 Test St',
      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: [],
      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 createReadings(overrides: Partial<ZoneEnvironmentReadings>): 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: [],
    ...overrides,
  };
}
