/**
 * Vertical Farm API Tests
 * Tests for vertical farm-related API endpoints
 */

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

describe('Vertical Farm API', () => {
  let controller: VerticalFarmController;

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

  describe('POST /api/vertical-farm/register', () => {
    it('should register new farm', () => {
      const farm = createVerticalFarm('api-farm-001');

      controller.registerFarm(farm);

      const retrieved = controller.getFarm('api-farm-001');
      expect(retrieved).toBeDefined();
      expect(retrieved?.name).toBe('Test Vertical Farm');
    });

    it('should allow updating farm', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      const updatedFarm = createVerticalFarm('api-farm-001');
      updatedFarm.name = 'Updated Farm Name';
      controller.registerFarm(updatedFarm);

      const retrieved = controller.getFarm('api-farm-001');
      expect(retrieved?.name).toBe('Updated Farm Name');
    });
  });

  describe('GET /api/vertical-farm/[farmId]', () => {
    it('should return farm details', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      const retrieved = controller.getFarm('api-farm-001');

      expect(retrieved).toBeDefined();
      expect(retrieved?.id).toBe('api-farm-001');
      expect(retrieved?.zones.length).toBeGreaterThan(0);
    });

    it('should return undefined for unknown farm', () => {
      const retrieved = controller.getFarm('unknown-farm');
      expect(retrieved).toBeUndefined();
    });
  });

  describe('GET /api/vertical-farm/[farmId]/zones', () => {
    it('should return farm zones', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      const retrieved = controller.getFarm('api-farm-001');
      const zones = retrieved?.zones;

      expect(zones).toBeDefined();
      expect(zones?.length).toBeGreaterThan(0);
      expect(zones?.[0].id).toBe('zone-001');
    });
  });

  describe('POST /api/vertical-farm/[farmId]/zones', () => {
    it('should add zone to existing farm', () => {
      const farm = createVerticalFarm('api-farm-001');
      farm.zones = []; // Start with no zones
      controller.registerFarm(farm);

      // Add zone by updating farm
      const updatedFarm = controller.getFarm('api-farm-001')!;
      updatedFarm.zones.push(createGrowingZone('new-zone', 'New Zone', 1));

      expect(updatedFarm.zones.length).toBe(1);
    });
  });

  describe('GET /api/vertical-farm/[farmId]/analytics', () => {
    it('should return farm analytics', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      expect(analytics.farmId).toBe('api-farm-001');
      expect(analytics.period).toBe('30 days');
    });

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

      // Start and complete a batch for analytics data
      const recipes = controller.getRecipes();
      const batch = controller.startCropBatch(
        'api-farm-001',
        'zone-001',
        recipes[0].id,
        'seed-batch-001',
        100
      );
      controller.completeHarvest(batch.id, 15.0, 'A');

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

      expect(analytics.totalYieldKg).toBeGreaterThan(0);
    });

    it('should throw error for unknown farm', () => {
      expect(() => {
        controller.generateAnalytics('unknown-farm', 30);
      }).toThrow('Farm unknown-farm not found');
    });
  });

  describe('POST /api/vertical-farm/batch/start', () => {
    it('should start crop batch', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      expect(batch.id).toBeDefined();
      expect(batch.plantCount).toBe(100);
      expect(batch.status).toBe('germinating');
    });

    it('should validate farm exists', () => {
      expect(() => {
        controller.startCropBatch(
          'unknown-farm',
          'zone-001',
          'recipe-001',
          'seed-001',
          100
        );
      }).toThrow('Farm unknown-farm not found');
    });

    it('should validate zone exists', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      expect(() => {
        controller.startCropBatch(
          'api-farm-001',
          'unknown-zone',
          'recipe-001',
          'seed-001',
          100
        );
      }).toThrow();
    });

    it('should validate recipe exists', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      expect(() => {
        controller.startCropBatch(
          'api-farm-001',
          'zone-001',
          'unknown-recipe',
          'seed-001',
          100
        );
      }).toThrow();
    });
  });

  describe('GET /api/vertical-farm/batch/[batchId]', () => {
    it('should return batch details', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      // Update progress
      const updated = controller.updateBatchProgress(batch.id);

      expect(updated.id).toBe(batch.id);
      expect(updated.currentDay).toBeGreaterThanOrEqual(0);
    });

    it('should throw error for unknown batch', () => {
      expect(() => {
        controller.updateBatchProgress('unknown-batch');
      }).toThrow('Batch unknown-batch not found');
    });
  });

  describe('PUT /api/vertical-farm/batch/[batchId]/environment', () => {
    it('should record environment readings', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      const readings: ZoneEnvironmentReadings = {
        timestamp: new Date().toISOString(),
        temperatureC: 22,
        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: [],
      };

      const alerts = controller.recordEnvironment('zone-001', readings);
      expect(Array.isArray(alerts)).toBe(true);
    });

    it('should detect environment alerts', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      const readings: ZoneEnvironmentReadings = {
        timestamp: new Date().toISOString(),
        temperatureC: 35, // Too high
        humidityPercent: 30, // 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: [],
      };

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

  describe('POST /api/vertical-farm/batch/[batchId]/harvest', () => {
    it('should complete harvest', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      const completed = controller.completeHarvest(batch.id, 15.5, 'A');

      expect(completed.status).toBe('completed');
      expect(completed.actualYieldKg).toBe(15.5);
      expect(completed.qualityGrade).toBe('A');
    });

    it('should throw error for unknown batch', () => {
      expect(() => {
        controller.completeHarvest('unknown-batch', 10, 'A');
      }).toThrow('Batch unknown-batch not found');
    });
  });

  describe('GET /api/vertical-farm/recipes', () => {
    it('should return all recipes', () => {
      const recipes = controller.getRecipes();

      expect(recipes.length).toBeGreaterThan(0);
      expect(recipes[0].id).toBeDefined();
      expect(recipes[0].stages.length).toBeGreaterThan(0);
    });

    it('should include default recipes', () => {
      const recipes = controller.getRecipes();
      const cropTypes = recipes.map(r => r.cropType);

      expect(cropTypes).toContain('lettuce');
      expect(cropTypes).toContain('basil');
      expect(cropTypes).toContain('microgreens');
    });
  });

  describe('Response Formats', () => {
    it('should return consistent farm format', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

      const retrieved = controller.getFarm('api-farm-001');

      expect(retrieved?.id).toBeDefined();
      expect(retrieved?.name).toBeDefined();
      expect(retrieved?.ownerId).toBeDefined();
      expect(retrieved?.location).toBeDefined();
      expect(retrieved?.specs).toBeDefined();
      expect(retrieved?.zones).toBeDefined();
      expect(retrieved?.status).toBeDefined();
    });

    it('should return consistent batch format', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      expect(batch.id).toBeDefined();
      expect(batch.farmId).toBeDefined();
      expect(batch.zoneId).toBeDefined();
      expect(batch.cropType).toBeDefined();
      expect(batch.plantCount).toBeDefined();
      expect(batch.status).toBeDefined();
      expect(batch.expectedYieldKg).toBeDefined();
    });

    it('should return consistent analytics format', () => {
      const farm = createVerticalFarm('api-farm-001');
      controller.registerFarm(farm);

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

      expect(analytics.farmId).toBeDefined();
      expect(analytics.generatedAt).toBeDefined();
      expect(analytics.period).toBeDefined();
      expect(analytics.totalYieldKg).toBeDefined();
      expect(analytics.cropCyclesCompleted).toBeDefined();
      expect(analytics.topCropsByYield).toBeDefined();
    });
  });
});

// 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',
  };
}
