/**
 * Recipe Stage Logic Tests
 * Tests for growing recipe and stage management
 */

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

describe('Growing Recipes', () => {
  let controller: VerticalFarmController;

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

  describe('Default Recipes', () => {
    it('should have complete stage definitions', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        expect(recipe.stages.length).toBeGreaterThan(0);
        expect(recipe.expectedDays).toBeGreaterThan(0);

        // Stages should cover full duration
        const lastStage = recipe.stages[recipe.stages.length - 1];
        expect(lastStage.daysEnd).toBeLessThanOrEqual(recipe.expectedDays);
      });
    });

    it('should have non-overlapping stages', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        for (let i = 1; i < recipe.stages.length; i++) {
          const prevStage = recipe.stages[i - 1];
          const currentStage = recipe.stages[i];
          expect(currentStage.daysStart).toBeGreaterThan(prevStage.daysStart);
          expect(currentStage.daysStart).toBeGreaterThanOrEqual(prevStage.daysEnd);
        }
      });
    });

    it('should have valid environment targets per stage', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        recipe.stages.forEach(stage => {
          // Temperature
          expect(stage.temperature.day).toBeGreaterThan(0);
          expect(stage.temperature.night).toBeGreaterThan(0);
          expect(stage.temperature.night).toBeLessThanOrEqual(stage.temperature.day);

          // Humidity
          expect(stage.humidity.day).toBeGreaterThan(0);
          expect(stage.humidity.day).toBeLessThanOrEqual(100);
          expect(stage.humidity.night).toBeGreaterThan(0);
          expect(stage.humidity.night).toBeLessThanOrEqual(100);

          // CO2
          expect(stage.co2Ppm).toBeGreaterThanOrEqual(0);

          // Light
          expect(stage.lightHours).toBeGreaterThanOrEqual(0);
          expect(stage.lightHours).toBeLessThanOrEqual(24);
          expect(stage.lightPpfd).toBeGreaterThanOrEqual(0);

          // Nutrients
          expect(stage.targetEc).toBeGreaterThanOrEqual(0);
          expect(stage.targetPh).toBeGreaterThan(0);
          expect(stage.targetPh).toBeLessThan(14);
        });
      });
    });
  });

  describe('Lettuce Recipe', () => {
    it('should have correct growth stages', () => {
      const recipes = controller.getRecipes();
      const lettuce = recipes.find(r => r.cropType === 'lettuce')!;

      expect(lettuce.stages.length).toBeGreaterThanOrEqual(3);

      const stageNames = lettuce.stages.map(s => s.name);
      expect(stageNames).toContain('Germination');
      expect(stageNames).toContain('Seedling');
    });

    it('should have approximately 35 day cycle', () => {
      const recipes = controller.getRecipes();
      const lettuce = recipes.find(r => r.cropType === 'lettuce')!;

      expect(lettuce.expectedDays).toBeGreaterThanOrEqual(30);
      expect(lettuce.expectedDays).toBeLessThanOrEqual(45);
    });

    it('should have transplant action', () => {
      const recipes = controller.getRecipes();
      const lettuce = recipes.find(r => r.cropType === 'lettuce')!;

      const hasTransplant = lettuce.stages.some(stage =>
        stage.actions.some(action => action.action === 'transplant')
      );
      expect(hasTransplant).toBe(true);
    });
  });

  describe('Basil Recipe', () => {
    it('should have higher temperature requirements', () => {
      const recipes = controller.getRecipes();
      const basil = recipes.find(r => r.cropType === 'basil')!;
      const lettuce = recipes.find(r => r.cropType === 'lettuce')!;

      // Basil prefers warmer temperatures
      const basilMaxTemp = Math.max(...basil.stages.map(s => s.temperature.day));
      const lettuceMaxTemp = Math.max(...lettuce.stages.map(s => s.temperature.day));

      expect(basilMaxTemp).toBeGreaterThanOrEqual(lettuceMaxTemp);
    });

    it('should have longer cycle than microgreens', () => {
      const recipes = controller.getRecipes();
      const basil = recipes.find(r => r.cropType === 'basil')!;
      const microgreens = recipes.find(r => r.cropType === 'microgreens')!;

      expect(basil.expectedDays).toBeGreaterThan(microgreens.expectedDays);
    });
  });

  describe('Microgreens Recipe', () => {
    it('should have short cycle', () => {
      const recipes = controller.getRecipes();
      const microgreens = recipes.find(r => r.cropType === 'microgreens')!;

      expect(microgreens.expectedDays).toBeLessThanOrEqual(21);
    });

    it('should start in darkness (sowing stage)', () => {
      const recipes = controller.getRecipes();
      const microgreens = recipes.find(r => r.cropType === 'microgreens')!;

      const sowingStage = microgreens.stages.find(s => s.name === 'Sowing');
      if (sowingStage) {
        expect(sowingStage.lightHours).toBe(0);
        expect(sowingStage.lightPpfd).toBe(0);
      }
    });

    it('should have high humidity in early stages', () => {
      const recipes = controller.getRecipes();
      const microgreens = recipes.find(r => r.cropType === 'microgreens')!;

      const earlyStage = microgreens.stages[0];
      expect(earlyStage.humidity.day).toBeGreaterThanOrEqual(80);
    });
  });

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

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

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

      // Initial stage
      const initialStage = recipe.stages[0];
      const farmAfter = controller.getFarm('farm-001');
      const zone = farmAfter?.zones.find(z => z.id === 'zone-001');

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

  describe('Recipe Requirements', () => {
    it('should specify zone type requirements', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        expect(recipe.requirements.zoneType).toBeDefined();
        expect(['NFT', 'DWC', 'ebb_flow', 'aeroponics', 'vertical_towers', 'rack_system'])
          .toContain(recipe.requirements.zoneType);
      });
    });

    it('should specify minimum light requirements', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        expect(recipe.requirements.minimumPpfd).toBeGreaterThan(0);
      });
    });

    it('should specify temperature requirements', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        expect(recipe.requirements.idealTemperatureC).toBeGreaterThan(0);
        expect(recipe.requirements.idealTemperatureC).toBeLessThan(40);
      });
    });
  });

  describe('Yield Expectations', () => {
    it('should have reasonable yield estimates', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        expect(recipe.expectedYieldGrams).toBeGreaterThan(0);
        expect(recipe.expectedYieldPerSqm).toBeGreaterThan(0);
      });
    });

    it('should have consistent yield calculations', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        // Yield per sqm should be reasonable multiple of per-plant yield
        const plantsPerSqm = recipe.expectedYieldPerSqm / recipe.expectedYieldGrams;
        expect(plantsPerSqm).toBeGreaterThan(0);
        expect(plantsPerSqm).toBeLessThan(100); // Reasonable plant density
      });
    });
  });

  describe('Stage Actions', () => {
    it('should have day specified for each action', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        recipe.stages.forEach(stage => {
          stage.actions.forEach(action => {
            expect(action.day).toBeGreaterThanOrEqual(stage.daysStart);
            expect(action.day).toBeLessThanOrEqual(stage.daysEnd);
          });
        });
      });
    });

    it('should have automation flag for actions', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        recipe.stages.forEach(stage => {
          stage.actions.forEach(action => {
            expect(typeof action.automated).toBe('boolean');
          });
        });
      });
    });

    it('should have descriptions for actions', () => {
      const recipes = controller.getRecipes();

      recipes.forEach(recipe => {
        recipe.stages.forEach(stage => {
          stage.actions.forEach(action => {
            expect(action.description).toBeDefined();
            expect(action.description.length).toBeGreaterThan(0);
          });
        });
      });
    });
  });

  describe('Custom Recipes', () => {
    it('should validate custom recipe structure', () => {
      const customRecipe: GrowingRecipe = {
        id: 'recipe-custom',
        name: 'Custom Recipe',
        cropType: 'cucumber',
        version: '1.0',
        stages: [
          {
            name: 'Stage 1',
            daysStart: 0,
            daysEnd: 30,
            temperature: { day: 25, night: 22 },
            humidity: { day: 70, night: 75 },
            co2Ppm: 1000,
            lightHours: 16,
            lightPpfd: 400,
            nutrientRecipeId: 'nutrient-veg',
            targetEc: 2.0,
            targetPh: 5.8,
            actions: [],
          },
        ],
        expectedDays: 60,
        expectedYieldGrams: 500,
        expectedYieldPerSqm: 10000,
        requirements: {
          positions: 1,
          zoneType: 'NFT',
          minimumPpfd: 300,
          idealTemperatureC: 24,
        },
        source: 'internal',
        timesUsed: 0,
      };

      controller.addRecipe(customRecipe);
      const recipes = controller.getRecipes();
      const found = recipes.find(r => r.id === 'recipe-custom');

      expect(found).toBeDefined();
      expect(found?.cropType).toBe('cucumber');
    });
  });
});

// Helper function
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: [
      {
        id: 'zone-001',
        name: 'Zone A',
        level: 1,
        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',
      },
    ],
    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,
  };
}
