/**
 * Vertical Farm Controller for LocalGreenChain
 * Manages vertical farm operations, automation, and optimization
 */

import {
  VerticalFarm,
  GrowingZone,
  ZoneEnvironmentTargets,
  ZoneEnvironmentReadings,
  EnvironmentAlert,
  GrowingRecipe,
  CropBatch,
  ResourceUsage,
  FarmAnalytics,
  LightSchedule,
  NutrientRecipe,
  BatchIssue
} from './types';

/**
 * Vertical Farm Controller
 */
export class VerticalFarmController {
  private farms: Map<string, VerticalFarm> = new Map();
  private recipes: Map<string, GrowingRecipe> = new Map();
  private batches: Map<string, CropBatch> = new Map();
  private resourceLogs: Map<string, ResourceUsage[]> = new Map();

  constructor() {
    this.initializeDefaultRecipes();
  }

  /**
   * Initialize default growing recipes
   */
  private initializeDefaultRecipes(): void {
    const defaultRecipes: GrowingRecipe[] = [
      {
        id: 'recipe-lettuce-butterhead',
        name: 'Butterhead Lettuce - Fast Cycle',
        cropType: 'lettuce',
        variety: 'butterhead',
        version: '1.0',
        stages: [
          {
            name: 'Germination',
            daysStart: 0,
            daysEnd: 3,
            temperature: { day: 20, night: 18 },
            humidity: { day: 80, night: 85 },
            co2Ppm: 800,
            lightHours: 18,
            lightPpfd: 150,
            nutrientRecipeId: 'nutrient-seedling',
            targetEc: 0.8,
            targetPh: 6.0,
            actions: []
          },
          {
            name: 'Seedling',
            daysStart: 4,
            daysEnd: 10,
            temperature: { day: 21, night: 18 },
            humidity: { day: 70, night: 75 },
            co2Ppm: 1000,
            lightHours: 18,
            lightPpfd: 200,
            nutrientRecipeId: 'nutrient-vegetative',
            targetEc: 1.2,
            targetPh: 6.0,
            actions: [
              { day: 10, action: 'transplant', description: 'Transplant to final position', automated: true }
            ]
          },
          {
            name: 'Vegetative Growth',
            daysStart: 11,
            daysEnd: 28,
            temperature: { day: 22, night: 18 },
            humidity: { day: 65, night: 70 },
            co2Ppm: 1200,
            lightHours: 16,
            lightPpfd: 300,
            nutrientRecipeId: 'nutrient-vegetative',
            targetEc: 1.6,
            targetPh: 6.0,
            actions: []
          },
          {
            name: 'Finishing',
            daysStart: 29,
            daysEnd: 35,
            temperature: { day: 20, night: 16 },
            humidity: { day: 60, night: 65 },
            co2Ppm: 800,
            lightHours: 14,
            lightPpfd: 250,
            nutrientRecipeId: 'nutrient-finishing',
            targetEc: 1.2,
            targetPh: 6.0,
            actions: []
          }
        ],
        expectedDays: 35,
        expectedYieldGrams: 180,
        expectedYieldPerSqm: 4000,
        requirements: {
          positions: 1,
          zoneType: 'NFT',
          minimumPpfd: 200,
          idealTemperatureC: 21
        },
        source: 'internal',
        rating: 4.5,
        timesUsed: 0
      },
      {
        id: 'recipe-basil-genovese',
        name: 'Genovese Basil - Aromatic',
        cropType: 'basil',
        variety: 'genovese',
        version: '1.0',
        stages: [
          {
            name: 'Germination',
            daysStart: 0,
            daysEnd: 5,
            temperature: { day: 24, night: 22 },
            humidity: { day: 80, night: 85 },
            co2Ppm: 800,
            lightHours: 16,
            lightPpfd: 100,
            nutrientRecipeId: 'nutrient-seedling',
            targetEc: 0.6,
            targetPh: 6.2,
            actions: []
          },
          {
            name: 'Seedling',
            daysStart: 6,
            daysEnd: 14,
            temperature: { day: 25, night: 22 },
            humidity: { day: 70, night: 75 },
            co2Ppm: 1000,
            lightHours: 18,
            lightPpfd: 200,
            nutrientRecipeId: 'nutrient-vegetative',
            targetEc: 1.0,
            targetPh: 6.2,
            actions: [
              { day: 14, action: 'transplant', description: 'Transplant to growing system', automated: true }
            ]
          },
          {
            name: 'Vegetative',
            daysStart: 15,
            daysEnd: 35,
            temperature: { day: 26, night: 22 },
            humidity: { day: 65, night: 70 },
            co2Ppm: 1200,
            lightHours: 18,
            lightPpfd: 400,
            nutrientRecipeId: 'nutrient-herbs',
            targetEc: 1.4,
            targetPh: 6.0,
            actions: [
              { day: 25, action: 'top', description: 'Top plants to encourage bushiness', automated: false }
            ]
          },
          {
            name: 'Harvest Ready',
            daysStart: 36,
            daysEnd: 42,
            temperature: { day: 24, night: 20 },
            humidity: { day: 60, night: 65 },
            co2Ppm: 800,
            lightHours: 16,
            lightPpfd: 350,
            nutrientRecipeId: 'nutrient-finishing',
            targetEc: 1.0,
            targetPh: 6.0,
            actions: []
          }
        ],
        expectedDays: 42,
        expectedYieldGrams: 120,
        expectedYieldPerSqm: 2400,
        requirements: {
          positions: 1,
          zoneType: 'NFT',
          minimumPpfd: 300,
          idealTemperatureC: 25
        },
        source: 'internal',
        rating: 4.3,
        timesUsed: 0
      },
      {
        id: 'recipe-microgreens-mix',
        name: 'Microgreens Mix - Quick Turn',
        cropType: 'microgreens',
        variety: 'mixed',
        version: '1.0',
        stages: [
          {
            name: 'Sowing',
            daysStart: 0,
            daysEnd: 2,
            temperature: { day: 22, night: 20 },
            humidity: { day: 90, night: 90 },
            co2Ppm: 600,
            lightHours: 0,
            lightPpfd: 0,
            nutrientRecipeId: 'nutrient-none',
            targetEc: 0,
            targetPh: 6.0,
            actions: []
          },
          {
            name: 'Germination',
            daysStart: 3,
            daysEnd: 5,
            temperature: { day: 22, night: 20 },
            humidity: { day: 80, night: 85 },
            co2Ppm: 800,
            lightHours: 12,
            lightPpfd: 100,
            nutrientRecipeId: 'nutrient-none',
            targetEc: 0,
            targetPh: 6.0,
            actions: []
          },
          {
            name: 'Growth',
            daysStart: 6,
            daysEnd: 12,
            temperature: { day: 21, night: 19 },
            humidity: { day: 65, night: 70 },
            co2Ppm: 1000,
            lightHours: 16,
            lightPpfd: 250,
            nutrientRecipeId: 'nutrient-microgreens',
            targetEc: 0.8,
            targetPh: 6.0,
            actions: []
          },
          {
            name: 'Harvest',
            daysStart: 13,
            daysEnd: 14,
            temperature: { day: 20, night: 18 },
            humidity: { day: 60, night: 65 },
            co2Ppm: 600,
            lightHours: 14,
            lightPpfd: 200,
            nutrientRecipeId: 'nutrient-none',
            targetEc: 0,
            targetPh: 6.0,
            actions: []
          }
        ],
        expectedDays: 14,
        expectedYieldGrams: 200,
        expectedYieldPerSqm: 2000,
        requirements: {
          positions: 1,
          zoneType: 'rack_system',
          minimumPpfd: 150,
          idealTemperatureC: 21
        },
        source: 'internal',
        rating: 4.7,
        timesUsed: 0
      }
    ];

    for (const recipe of defaultRecipes) {
      this.recipes.set(recipe.id, recipe);
    }
  }

  /**
   * Register a new vertical farm
   */
  registerFarm(farm: VerticalFarm): void {
    this.farms.set(farm.id, farm);
    this.resourceLogs.set(farm.id, []);
  }

  /**
   * Get farm by ID
   */
  getFarm(farmId: string): VerticalFarm | undefined {
    return this.farms.get(farmId);
  }

  /**
   * Start a new crop batch
   */
  startCropBatch(
    farmId: string,
    zoneId: string,
    recipeId: string,
    seedBatchId: string,
    plantCount: number
  ): CropBatch {
    const farm = this.farms.get(farmId);
    if (!farm) throw new Error(`Farm ${farmId} not found`);

    const zone = farm.zones.find(z => z.id === zoneId);
    if (!zone) throw new Error(`Zone ${zoneId} not found in farm ${farmId}`);

    const recipe = this.recipes.get(recipeId);
    if (!recipe) throw new Error(`Recipe ${recipeId} not found`);

    const now = new Date();
    const expectedHarvest = new Date(now.getTime() + recipe.expectedDays * 24 * 60 * 60 * 1000);

    const batch: CropBatch = {
      id: `batch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
      farmId,
      zoneId,
      cropType: recipe.cropType,
      variety: recipe.variety,
      recipeId,
      seedBatchId,
      plantIds: [],
      plantCount,
      plantingDate: now.toISOString(),
      currentStage: recipe.stages[0].name,
      currentDay: 0,
      healthScore: 100,
      expectedHarvestDate: expectedHarvest.toISOString(),
      expectedYieldKg: (recipe.expectedYieldGrams * plantCount) / 1000,
      status: 'germinating',
      issues: [],
      environmentLog: []
    };

    // Generate plant IDs
    for (let i = 0; i < plantCount; i++) {
      batch.plantIds.push(`${batch.id}-plant-${i}`);
    }

    // Update zone
    zone.currentCrop = recipe.cropType;
    zone.plantIds = batch.plantIds;
    zone.plantingDate = batch.plantingDate;
    zone.expectedHarvestDate = batch.expectedHarvestDate;
    zone.status = 'planted';

    // Set environment targets from recipe
    const firstStage = recipe.stages[0];
    zone.environmentTargets = {
      temperatureC: { min: firstStage.temperature.night - 2, max: firstStage.temperature.day + 2, target: firstStage.temperature.day },
      humidityPercent: { min: firstStage.humidity.day - 10, max: firstStage.humidity.night + 5, target: firstStage.humidity.day },
      co2Ppm: { min: firstStage.co2Ppm - 200, max: firstStage.co2Ppm + 200, target: firstStage.co2Ppm },
      lightPpfd: { min: firstStage.lightPpfd * 0.8, max: firstStage.lightPpfd * 1.2, target: firstStage.lightPpfd },
      lightHours: firstStage.lightHours,
      nutrientEc: { min: firstStage.targetEc - 0.2, max: firstStage.targetEc + 0.2, target: firstStage.targetEc },
      nutrientPh: { min: firstStage.targetPh - 0.3, max: firstStage.targetPh + 0.3, target: firstStage.targetPh },
      waterTempC: { min: 18, max: 24, target: 20 }
    };

    this.batches.set(batch.id, batch);
    recipe.timesUsed++;

    return batch;
  }

  /**
   * Update batch progress
   */
  updateBatchProgress(batchId: string): CropBatch {
    const batch = this.batches.get(batchId);
    if (!batch) throw new Error(`Batch ${batchId} not found`);

    const recipe = this.recipes.get(batch.recipeId);
    if (!recipe) throw new Error(`Recipe ${batch.recipeId} not found`);

    const plantingDate = new Date(batch.plantingDate);
    const now = new Date();
    batch.currentDay = Math.floor((now.getTime() - plantingDate.getTime()) / (24 * 60 * 60 * 1000));

    // Determine current stage
    for (const stage of recipe.stages) {
      if (batch.currentDay >= stage.daysStart && batch.currentDay <= stage.daysEnd) {
        batch.currentStage = stage.name;

        // Update zone targets
        const farm = this.farms.get(batch.farmId);
        const zone = farm?.zones.find(z => z.id === batch.zoneId);
        if (zone) {
          zone.environmentTargets = {
            temperatureC: { min: stage.temperature.night - 2, max: stage.temperature.day + 2, target: stage.temperature.day },
            humidityPercent: { min: stage.humidity.day - 10, max: stage.humidity.night + 5, target: stage.humidity.day },
            co2Ppm: { min: stage.co2Ppm - 200, max: stage.co2Ppm + 200, target: stage.co2Ppm },
            lightPpfd: { min: stage.lightPpfd * 0.8, max: stage.lightPpfd * 1.2, target: stage.lightPpfd },
            lightHours: stage.lightHours,
            nutrientEc: { min: stage.targetEc - 0.2, max: stage.targetEc + 0.2, target: stage.targetEc },
            nutrientPh: { min: stage.targetPh - 0.3, max: stage.targetPh + 0.3, target: stage.targetPh },
            waterTempC: { min: 18, max: 24, target: 20 }
          };
        }
        break;
      }
    }

    // Update status
    if (batch.currentDay >= recipe.expectedDays) {
      batch.status = 'ready';
    } else if (batch.currentDay > 3) {
      batch.status = 'growing';
    }

    return batch;
  }

  /**
   * Record environment reading
   */
  recordEnvironment(zoneId: string, readings: ZoneEnvironmentReadings): EnvironmentAlert[] {
    const alerts: EnvironmentAlert[] = [];

    // Find the zone
    let targetZone: GrowingZone | undefined;
    let farm: VerticalFarm | undefined;

    for (const f of this.farms.values()) {
      const zone = f.zones.find(z => z.id === zoneId);
      if (zone) {
        targetZone = zone;
        farm = f;
        break;
      }
    }

    if (!targetZone || !farm) return alerts;

    const targets = targetZone.environmentTargets;

    // Check temperature
    if (readings.temperatureC < targets.temperatureC.min) {
      alerts.push({
        parameter: 'temperature',
        type: readings.temperatureC < targets.temperatureC.min - 5 ? 'critical_low' : 'low',
        value: readings.temperatureC,
        threshold: targets.temperatureC.min,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    } else if (readings.temperatureC > targets.temperatureC.max) {
      alerts.push({
        parameter: 'temperature',
        type: readings.temperatureC > targets.temperatureC.max + 5 ? 'critical_high' : 'high',
        value: readings.temperatureC,
        threshold: targets.temperatureC.max,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    }

    // Check humidity
    if (readings.humidityPercent < targets.humidityPercent.min) {
      alerts.push({
        parameter: 'humidity',
        type: 'low',
        value: readings.humidityPercent,
        threshold: targets.humidityPercent.min,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    } else if (readings.humidityPercent > targets.humidityPercent.max) {
      alerts.push({
        parameter: 'humidity',
        type: 'high',
        value: readings.humidityPercent,
        threshold: targets.humidityPercent.max,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    }

    // Check EC
    if (readings.ec < targets.nutrientEc.min) {
      alerts.push({
        parameter: 'ec',
        type: 'low',
        value: readings.ec,
        threshold: targets.nutrientEc.min,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    } else if (readings.ec > targets.nutrientEc.max) {
      alerts.push({
        parameter: 'ec',
        type: 'high',
        value: readings.ec,
        threshold: targets.nutrientEc.max,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    }

    // Check pH
    if (readings.ph < targets.nutrientPh.min) {
      alerts.push({
        parameter: 'ph',
        type: 'low',
        value: readings.ph,
        threshold: targets.nutrientPh.min,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    } else if (readings.ph > targets.nutrientPh.max) {
      alerts.push({
        parameter: 'ph',
        type: 'high',
        value: readings.ph,
        threshold: targets.nutrientPh.max,
        timestamp: readings.timestamp,
        acknowledged: false
      });
    }

    // Update zone readings
    readings.alerts = alerts;
    targetZone.currentEnvironment = readings;

    // Log to batch if exists
    const batch = Array.from(this.batches.values()).find(b =>
      b.zoneId === zoneId && b.status !== 'completed' && b.status !== 'failed'
    );
    if (batch) {
      batch.environmentLog.push({
        timestamp: readings.timestamp,
        readings
      });

      // Adjust health score based on alerts
      if (alerts.some(a => a.type.includes('critical'))) {
        batch.healthScore = Math.max(0, batch.healthScore - 5);
      } else if (alerts.length > 0) {
        batch.healthScore = Math.max(0, batch.healthScore - 1);
      }
    }

    return alerts;
  }

  /**
   * Complete harvest
   */
  completeHarvest(batchId: string, actualYieldKg: number, qualityGrade: string): CropBatch {
    const batch = this.batches.get(batchId);
    if (!batch) throw new Error(`Batch ${batchId} not found`);

    batch.actualHarvestDate = new Date().toISOString();
    batch.actualYieldKg = actualYieldKg;
    batch.qualityGrade = qualityGrade;
    batch.status = 'completed';

    // Update zone
    const farm = this.farms.get(batch.farmId);
    const zone = farm?.zones.find(z => z.id === batch.zoneId);
    if (zone) {
      zone.status = 'cleaning';
      zone.currentCrop = '';
      zone.plantIds = [];
    }

    return batch;
  }

  /**
   * Generate farm analytics
   */
  generateAnalytics(farmId: string, periodDays: number = 30): FarmAnalytics {
    const farm = this.farms.get(farmId);
    if (!farm) throw new Error(`Farm ${farmId} not found`);

    const now = new Date();
    const periodStart = new Date(now.getTime() - periodDays * 24 * 60 * 60 * 1000);

    // Get completed batches in period
    const completedBatches = Array.from(this.batches.values()).filter(b =>
      b.farmId === farmId &&
      b.status === 'completed' &&
      b.actualHarvestDate &&
      new Date(b.actualHarvestDate) >= periodStart
    );

    const totalYieldKg = completedBatches.reduce((sum, b) => sum + (b.actualYieldKg || 0), 0);
    const totalExpectedYield = completedBatches.reduce((sum, b) => sum + b.expectedYieldKg, 0);

    // Calculate yield per sqm per year
    const growingAreaSqm = farm.specs.growingAreaSqm;
    const yearlyMultiplier = 365 / periodDays;
    const yieldPerSqmPerYear = growingAreaSqm > 0 ? (totalYieldKg * yearlyMultiplier) / growingAreaSqm : 0;

    // Quality breakdown
    const gradeACounts = completedBatches.filter(b => b.qualityGrade === 'A').length;
    const gradeAPercent = completedBatches.length > 0 ? (gradeACounts / completedBatches.length) * 100 : 0;

    // Wastage (difference between expected and actual)
    const wastageKg = Math.max(0, totalExpectedYield - totalYieldKg);
    const wastagePercent = totalExpectedYield > 0 ? (wastageKg / totalExpectedYield) * 100 : 0;

    // Success rate
    const allBatches = Array.from(this.batches.values()).filter(b =>
      b.farmId === farmId &&
      new Date(b.plantingDate) >= periodStart
    );
    const failedBatches = allBatches.filter(b => b.status === 'failed').length;
    const cropSuccessRate = allBatches.length > 0 ? ((allBatches.length - failedBatches) / allBatches.length) * 100 : 100;

    // Resource usage
    const resourceHistory = this.resourceLogs.get(farmId) || [];
    const periodResources = resourceHistory.filter(r =>
      new Date(r.periodStart) >= periodStart
    );

    const totalElectricity = periodResources.reduce((sum, r) => sum + r.electricityKwh, 0);
    const totalWater = periodResources.reduce((sum, r) => sum + r.waterUsageL, 0);
    const totalCost = periodResources.reduce((sum, r) =>
      sum + r.electricityCostUsd + r.waterCostUsd + r.nutrientCostUsd + r.co2CostUsd, 0
    );

    // Top crops
    const cropYields = new Map<string, number>();
    const cropRevenue = new Map<string, number>();

    for (const batch of completedBatches) {
      const currentYield = cropYields.get(batch.cropType) || 0;
      cropYields.set(batch.cropType, currentYield + (batch.actualYieldKg || 0));

      // Estimate revenue (placeholder - would come from actual sales)
      const estimatedRevenue = (batch.actualYieldKg || 0) * 10; // $10/kg placeholder
      const currentRevenue = cropRevenue.get(batch.cropType) || 0;
      cropRevenue.set(batch.cropType, currentRevenue + estimatedRevenue);
    }

    const totalRevenue = Array.from(cropRevenue.values()).reduce((a, b) => a + b, 0);

    return {
      farmId,
      generatedAt: now.toISOString(),
      period: `${periodDays} days`,
      totalYieldKg: Math.round(totalYieldKg * 10) / 10,
      yieldPerSqmPerYear: Math.round(yieldPerSqmPerYear * 10) / 10,
      cropCyclesCompleted: completedBatches.length,
      averageCyclesDays: completedBatches.length > 0
        ? completedBatches.reduce((sum, b) => {
            const start = new Date(b.plantingDate);
            const end = new Date(b.actualHarvestDate!);
            return sum + (end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000);
          }, 0) / completedBatches.length
        : 0,
      averageQualityScore: completedBatches.length > 0
        ? completedBatches.reduce((sum, b) => sum + b.healthScore, 0) / completedBatches.length
        : 0,
      gradeAPercent: Math.round(gradeAPercent),
      wastagePercent: Math.round(wastagePercent * 10) / 10,
      cropSuccessRate: Math.round(cropSuccessRate),
      spaceUtilization: farm.currentCapacityUtilization,
      laborHoursPerKg: totalYieldKg > 0 ? 0.5 : 0, // Placeholder
      revenueUsd: Math.round(totalRevenue),
      costUsd: Math.round(totalCost),
      profitMarginPercent: totalRevenue > 0 ? Math.round(((totalRevenue - totalCost) / totalRevenue) * 100) : 0,
      revenuePerSqm: growingAreaSqm > 0 ? Math.round((totalRevenue / growingAreaSqm) * yearlyMultiplier) : 0,
      carbonFootprintKgPerKg: totalYieldKg > 0 ? 0.3 : 0, // Estimated - very low for VF
      waterUseLPerKg: totalYieldKg > 0 ? totalWater / totalYieldKg : 0,
      energyUseKwhPerKg: totalYieldKg > 0 ? totalElectricity / totalYieldKg : 0,
      topCropsByYield: Array.from(cropYields.entries())
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5)
        .map(([crop, yieldKg]) => ({ crop, yieldKg: Math.round(yieldKg * 10) / 10 })),
      topCropsByRevenue: Array.from(cropRevenue.entries())
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5)
        .map(([crop, revenueUsd]) => ({ crop, revenueUsd: Math.round(revenueUsd) })),
      topCropsByEfficiency: Array.from(cropYields.entries())
        .map(([crop, yieldKg]) => {
          const batches = completedBatches.filter(b => b.cropType === crop);
          const avgHealth = batches.reduce((sum, b) => sum + b.healthScore, 0) / batches.length;
          return { crop, efficiencyScore: Math.round(avgHealth) };
        })
        .sort((a, b) => b.efficiencyScore - a.efficiencyScore)
        .slice(0, 5)
    };
  }

  /**
   * Get all recipes
   */
  getRecipes(): GrowingRecipe[] {
    return Array.from(this.recipes.values());
  }

  /**
   * Add custom recipe
   */
  addRecipe(recipe: GrowingRecipe): void {
    this.recipes.set(recipe.id, recipe);
  }

  /**
   * Export state
   */
  toJSON(): object {
    return {
      farms: Array.from(this.farms.entries()),
      recipes: Array.from(this.recipes.entries()),
      batches: Array.from(this.batches.entries()),
      resourceLogs: Array.from(this.resourceLogs.entries())
    };
  }

  /**
   * Import state
   */
  static fromJSON(data: any): VerticalFarmController {
    const controller = new VerticalFarmController();

    if (data.farms) {
      for (const [key, value] of data.farms) {
        controller.farms.set(key, value);
      }
    }
    if (data.recipes) {
      for (const [key, value] of data.recipes) {
        controller.recipes.set(key, value);
      }
    }
    if (data.batches) {
      for (const [key, value] of data.batches) {
        controller.batches.set(key, value);
      }
    }
    if (data.resourceLogs) {
      for (const [key, value] of data.resourceLogs) {
        controller.resourceLogs.set(key, value);
      }
    }

    return controller;
  }
}

// Singleton
let controllerInstance: VerticalFarmController | null = null;

export function getVerticalFarmController(): VerticalFarmController {
  if (!controllerInstance) {
    controllerInstance = new VerticalFarmController();
  }
  return controllerInstance;
}
