/**
 * VerticalFarmAgent
 * Autonomous vertical farm monitoring and optimization
 *
 * Responsibilities:
 * - Monitor environmental conditions in all zones
 * - Detect anomalies and trigger alerts
 * - Optimize growing parameters
 * - Track crop batch progress
 * - Generate yield predictions
 * - Coordinate harvest scheduling
 */

import { BaseAgent } from './BaseAgent';
import { AgentConfig, AgentTask } from './types';

interface FarmEnvironment {
  temperature: number;
  humidity: number;
  co2Level: number;
  lightPPFD: number;
  nutrientEC: number;
  waterPH: number;
}

interface EnvironmentTarget {
  tempMin: number;
  tempMax: number;
  humidityMin: number;
  humidityMax: number;
  co2Min: number;
  co2Max: number;
  ppfdMin: number;
  ppfdMax: number;
}

interface ZoneStatus {
  zoneId: string;
  zoneName: string;
  currentEnv: FarmEnvironment;
  targetEnv: EnvironmentTarget;
  deviations: EnvironmentDeviation[];
  healthScore: number;
  activeBatches: number;
}

interface EnvironmentDeviation {
  parameter: 'temperature' | 'humidity' | 'co2' | 'light' | 'nutrients' | 'ph';
  currentValue: number;
  targetRange: { min: number; max: number };
  severity: 'low' | 'medium' | 'high' | 'critical';
  duration: number; // minutes
  action: string;
}

interface BatchProgress {
  batchId: string;
  cropType: string;
  zoneId: string;
  startDate: string;
  currentStage: string;
  daysInStage: number;
  expectedHarvestDate: string;
  healthScore: number;
  predictedYieldKg: number;
  issues: string[];
}

interface YieldPrediction {
  batchId: string;
  cropType: string;
  predictedYieldKg: number;
  confidence: number;
  harvestWindow: { start: string; optimal: string; end: string };
  qualityPrediction: 'premium' | 'standard' | 'below_standard';
  factors: { name: string; impact: number }[];
}

interface OptimizationRecommendation {
  zoneId: string;
  parameter: string;
  currentValue: number;
  recommendedValue: number;
  expectedImprovement: string;
  priority: 'low' | 'medium' | 'high';
  autoApply: boolean;
}

export class VerticalFarmAgent extends BaseAgent {
  private zones: Map<string, ZoneStatus> = new Map();
  private batches: Map<string, BatchProgress> = new Map();
  private yieldPredictions: Map<string, YieldPrediction> = new Map();
  private recommendations: OptimizationRecommendation[] = [];
  private environmentHistory: Map<string, FarmEnvironment[]> = new Map();

  constructor() {
    const config: AgentConfig = {
      id: 'vertical-farm-agent',
      name: 'Vertical Farm Agent',
      description: 'Monitors and optimizes vertical farm operations',
      enabled: true,
      intervalMs: 30000, // Run every 30 seconds for real-time monitoring
      priority: 'critical',
      maxRetries: 5,
      timeoutMs: 15000
    };
    super(config);

    // Initialize with sample zone
    this.initializeSampleFarm();
  }

  /**
   * Initialize sample farm for demonstration
   */
  private initializeSampleFarm(): void {
    const sampleZone: ZoneStatus = {
      zoneId: 'zone-1',
      zoneName: 'Leafy Greens Zone A',
      currentEnv: {
        temperature: 22.5,
        humidity: 65,
        co2Level: 800,
        lightPPFD: 250,
        nutrientEC: 1.8,
        waterPH: 6.2
      },
      targetEnv: {
        tempMin: 20,
        tempMax: 24,
        humidityMin: 60,
        humidityMax: 70,
        co2Min: 600,
        co2Max: 1000,
        ppfdMin: 200,
        ppfdMax: 400
      },
      deviations: [],
      healthScore: 95,
      activeBatches: 3
    };

    this.zones.set('zone-1', sampleZone);

    // Add sample batch
    const sampleBatch: BatchProgress = {
      batchId: 'batch-001',
      cropType: 'Butterhead Lettuce',
      zoneId: 'zone-1',
      startDate: new Date(Date.now() - 21 * 24 * 60 * 60 * 1000).toISOString(),
      currentStage: 'vegetative',
      daysInStage: 14,
      expectedHarvestDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(),
      healthScore: 92,
      predictedYieldKg: 45,
      issues: []
    };

    this.batches.set('batch-001', sampleBatch);
  }

  /**
   * Main execution cycle
   */
  async runOnce(): Promise<AgentTask | null> {
    const startTime = Date.now();
    let alertsGenerated = 0;
    let recommendationsGenerated = 0;

    // Monitor each zone
    for (const [zoneId, zone] of this.zones) {
      // Simulate environment reading (in production, this would come from sensors)
      this.simulateEnvironmentUpdate(zone);

      // Record history
      const history = this.environmentHistory.get(zoneId) || [];
      history.push({ ...zone.currentEnv });
      if (history.length > 1440) { // Keep 24 hours at 1-minute intervals
        history.shift();
      }
      this.environmentHistory.set(zoneId, history);

      // Check for deviations
      const deviations = this.checkDeviations(zone);
      zone.deviations = deviations;
      zone.healthScore = this.calculateZoneHealth(zone);

      // Generate alerts for critical deviations
      for (const deviation of deviations) {
        if (deviation.severity === 'critical' || deviation.severity === 'high') {
          this.createAlert(
            deviation.severity === 'critical' ? 'critical' : 'warning',
            `${zone.zoneName}: ${deviation.parameter} deviation`,
            `${deviation.parameter} at ${deviation.currentValue} (target: ${deviation.targetRange.min}-${deviation.targetRange.max})`,
            { actionRequired: deviation.action, relatedEntityId: zoneId, relatedEntityType: 'zone' }
          );
          alertsGenerated++;
        }
      }

      // Generate optimization recommendations
      const zoneRecs = this.generateOptimizations(zone);
      this.recommendations = [...this.recommendations.filter(r => r.zoneId !== zoneId), ...zoneRecs];
      recommendationsGenerated += zoneRecs.length;
    }

    // Update batch progress
    for (const [batchId, batch] of this.batches) {
      this.updateBatchProgress(batch);
      this.updateYieldPrediction(batch);

      // Alert for batches nearing harvest
      const daysToHarvest = Math.floor(
        (new Date(batch.expectedHarvestDate).getTime() - Date.now()) / (24 * 60 * 60 * 1000)
      );

      if (daysToHarvest <= 3 && daysToHarvest > 0) {
        this.createAlert('info', `Harvest Approaching: ${batch.cropType}`,
          `Batch ${batch.batchId} ready for harvest in ${daysToHarvest} days`,
          { relatedEntityId: batchId, relatedEntityType: 'batch' }
        );
      }
    }

    // Keep recommendations manageable
    if (this.recommendations.length > 50) {
      this.recommendations = this.recommendations.slice(-50);
    }

    return this.createTaskResult('farm_monitoring', 'completed', {
      zonesMonitored: this.zones.size,
      batchesTracked: this.batches.size,
      alertsGenerated,
      recommendationsGenerated,
      executionTimeMs: Date.now() - startTime
    });
  }

  /**
   * Simulate environment updates (for demo purposes)
   */
  private simulateEnvironmentUpdate(zone: ZoneStatus): void {
    // Add small random variations
    zone.currentEnv.temperature += (Math.random() - 0.5) * 0.5;
    zone.currentEnv.humidity += (Math.random() - 0.5) * 2;
    zone.currentEnv.co2Level += (Math.random() - 0.5) * 20;
    zone.currentEnv.lightPPFD += (Math.random() - 0.5) * 10;
    zone.currentEnv.nutrientEC += (Math.random() - 0.5) * 0.1;
    zone.currentEnv.waterPH += (Math.random() - 0.5) * 0.1;

    // Clamp values to reasonable ranges
    zone.currentEnv.temperature = Math.max(15, Math.min(30, zone.currentEnv.temperature));
    zone.currentEnv.humidity = Math.max(40, Math.min(90, zone.currentEnv.humidity));
    zone.currentEnv.co2Level = Math.max(400, Math.min(1500, zone.currentEnv.co2Level));
    zone.currentEnv.lightPPFD = Math.max(0, Math.min(600, zone.currentEnv.lightPPFD));
    zone.currentEnv.nutrientEC = Math.max(0.5, Math.min(3.5, zone.currentEnv.nutrientEC));
    zone.currentEnv.waterPH = Math.max(5.0, Math.min(7.5, zone.currentEnv.waterPH));
  }

  /**
   * Check for environmental deviations
   */
  private checkDeviations(zone: ZoneStatus): EnvironmentDeviation[] {
    const deviations: EnvironmentDeviation[] = [];
    const env = zone.currentEnv;
    const target = zone.targetEnv;

    // Temperature check
    if (env.temperature < target.tempMin || env.temperature > target.tempMax) {
      const severity = this.calculateDeviationSeverity(
        env.temperature,
        target.tempMin,
        target.tempMax,
        2, 5 // Warning at 2°C, critical at 5°C
      );
      deviations.push({
        parameter: 'temperature',
        currentValue: Math.round(env.temperature * 10) / 10,
        targetRange: { min: target.tempMin, max: target.tempMax },
        severity,
        duration: 0,
        action: env.temperature < target.tempMin
          ? 'Increase heating or reduce ventilation'
          : 'Increase cooling or ventilation'
      });
    }

    // Humidity check
    if (env.humidity < target.humidityMin || env.humidity > target.humidityMax) {
      const severity = this.calculateDeviationSeverity(
        env.humidity,
        target.humidityMin,
        target.humidityMax,
        10, 20
      );
      deviations.push({
        parameter: 'humidity',
        currentValue: Math.round(env.humidity),
        targetRange: { min: target.humidityMin, max: target.humidityMax },
        severity,
        duration: 0,
        action: env.humidity < target.humidityMin
          ? 'Increase misting or reduce dehumidification'
          : 'Increase dehumidification or ventilation'
      });
    }

    // CO2 check
    if (env.co2Level < target.co2Min || env.co2Level > target.co2Max) {
      const severity = this.calculateDeviationSeverity(
        env.co2Level,
        target.co2Min,
        target.co2Max,
        200, 400
      );
      deviations.push({
        parameter: 'co2',
        currentValue: Math.round(env.co2Level),
        targetRange: { min: target.co2Min, max: target.co2Max },
        severity,
        duration: 0,
        action: env.co2Level < target.co2Min
          ? 'Increase CO2 supplementation'
          : 'Increase ventilation to reduce CO2'
      });
    }

    // Light check
    if (env.lightPPFD < target.ppfdMin || env.lightPPFD > target.ppfdMax) {
      const severity = this.calculateDeviationSeverity(
        env.lightPPFD,
        target.ppfdMin,
        target.ppfdMax,
        50, 100
      );
      deviations.push({
        parameter: 'light',
        currentValue: Math.round(env.lightPPFD),
        targetRange: { min: target.ppfdMin, max: target.ppfdMax },
        severity,
        duration: 0,
        action: env.lightPPFD < target.ppfdMin
          ? 'Increase light intensity or duration'
          : 'Reduce light intensity or increase plant density'
      });
    }

    return deviations;
  }

  /**
   * Calculate deviation severity
   */
  private calculateDeviationSeverity(
    current: number,
    min: number,
    max: number,
    warningDelta: number,
    criticalDelta: number
  ): 'low' | 'medium' | 'high' | 'critical' {
    const deviation = current < min ? min - current : current - max;

    if (deviation >= criticalDelta) return 'critical';
    if (deviation >= warningDelta) return 'high';
    if (deviation >= warningDelta / 2) return 'medium';
    return 'low';
  }

  /**
   * Calculate zone health score (0-100)
   */
  private calculateZoneHealth(zone: ZoneStatus): number {
    let score = 100;

    for (const deviation of zone.deviations) {
      switch (deviation.severity) {
        case 'critical': score -= 25; break;
        case 'high': score -= 15; break;
        case 'medium': score -= 8; break;
        case 'low': score -= 3; break;
      }
    }

    return Math.max(0, score);
  }

  /**
   * Generate optimization recommendations
   */
  private generateOptimizations(zone: ZoneStatus): OptimizationRecommendation[] {
    const recs: OptimizationRecommendation[] = [];
    const env = zone.currentEnv;
    const target = zone.targetEnv;

    // Temperature optimization
    const optimalTemp = (target.tempMin + target.tempMax) / 2;
    if (Math.abs(env.temperature - optimalTemp) > 1) {
      recs.push({
        zoneId: zone.zoneId,
        parameter: 'temperature',
        currentValue: Math.round(env.temperature * 10) / 10,
        recommendedValue: optimalTemp,
        expectedImprovement: 'Improved growth rate and reduced stress',
        priority: Math.abs(env.temperature - optimalTemp) > 2 ? 'high' : 'medium',
        autoApply: false
      });
    }

    // CO2 optimization during light hours
    const hour = new Date().getHours();
    const isLightPeriod = hour >= 6 && hour <= 22;
    const optimalCO2 = isLightPeriod ? 900 : 600;

    if (Math.abs(env.co2Level - optimalCO2) > 100) {
      recs.push({
        zoneId: zone.zoneId,
        parameter: 'co2',
        currentValue: Math.round(env.co2Level),
        recommendedValue: optimalCO2,
        expectedImprovement: isLightPeriod
          ? 'Enhanced photosynthesis during light hours'
          : 'Reduced CO2 usage during dark period',
        priority: 'low',
        autoApply: true
      });
    }

    return recs;
  }

  /**
   * Update batch progress
   */
  private updateBatchProgress(batch: BatchProgress): void {
    const daysSinceStart = Math.floor(
      (Date.now() - new Date(batch.startDate).getTime()) / (24 * 60 * 60 * 1000)
    );

    // Simple stage progression
    if (daysSinceStart < 7) {
      batch.currentStage = 'germination';
      batch.daysInStage = daysSinceStart;
    } else if (daysSinceStart < 21) {
      batch.currentStage = 'vegetative';
      batch.daysInStage = daysSinceStart - 7;
    } else if (daysSinceStart < 35) {
      batch.currentStage = 'finishing';
      batch.daysInStage = daysSinceStart - 21;
    } else {
      batch.currentStage = 'harvest_ready';
      batch.daysInStage = daysSinceStart - 35;
    }

    // Update health score based on zone conditions
    const zone = this.zones.get(batch.zoneId);
    if (zone) {
      batch.healthScore = Math.round((batch.healthScore * 0.9 + zone.healthScore * 0.1));
    }

    // Update issues
    batch.issues = [];
    if (batch.healthScore < 80) {
      batch.issues.push('Environmental stress detected');
    }
    if (batch.daysInStage > 20 && batch.currentStage === 'vegetative') {
      batch.issues.push('Extended vegetative period - check nutrient levels');
    }
  }

  /**
   * Update yield prediction
   */
  private updateYieldPrediction(batch: BatchProgress): void {
    // Base yield based on crop type
    const baseYields: Record<string, number> = {
      'Butterhead Lettuce': 50,
      'Romaine Lettuce': 45,
      'Basil': 25,
      'Microgreens': 15,
      'Spinach': 30
    };

    const baseYield = baseYields[batch.cropType] || 40;

    // Adjust based on health
    const healthFactor = batch.healthScore / 100;

    // Adjust based on stage
    const stageFactor = batch.currentStage === 'harvest_ready' ? 1.0 :
      batch.currentStage === 'finishing' ? 0.95 :
        batch.currentStage === 'vegetative' ? 0.8 : 0.5;

    const predictedYield = baseYield * healthFactor * stageFactor;

    // Calculate harvest window
    const optimalDate = new Date(batch.expectedHarvestDate);
    const startDate = new Date(optimalDate.getTime() - 2 * 24 * 60 * 60 * 1000);
    const endDate = new Date(optimalDate.getTime() + 5 * 24 * 60 * 60 * 1000);

    const prediction: YieldPrediction = {
      batchId: batch.batchId,
      cropType: batch.cropType,
      predictedYieldKg: Math.round(predictedYield * 10) / 10,
      confidence: Math.min(95, 50 + batch.healthScore * 0.45),
      harvestWindow: {
        start: startDate.toISOString(),
        optimal: optimalDate.toISOString(),
        end: endDate.toISOString()
      },
      qualityPrediction: batch.healthScore >= 90 ? 'premium' :
        batch.healthScore >= 70 ? 'standard' : 'below_standard',
      factors: [
        { name: 'Health Score', impact: Math.round((healthFactor - 1) * 100) },
        { name: 'Growth Stage', impact: Math.round((stageFactor - 1) * 100) }
      ]
    };

    this.yieldPredictions.set(batch.batchId, prediction);
    batch.predictedYieldKg = prediction.predictedYieldKg;
  }

  /**
   * Register a new zone
   */
  registerZone(zoneId: string, zoneName: string, targetEnv: EnvironmentTarget): void {
    const zone: ZoneStatus = {
      zoneId,
      zoneName,
      currentEnv: {
        temperature: (targetEnv.tempMin + targetEnv.tempMax) / 2,
        humidity: (targetEnv.humidityMin + targetEnv.humidityMax) / 2,
        co2Level: (targetEnv.co2Min + targetEnv.co2Max) / 2,
        lightPPFD: (targetEnv.ppfdMin + targetEnv.ppfdMax) / 2,
        nutrientEC: 1.8,
        waterPH: 6.0
      },
      targetEnv,
      deviations: [],
      healthScore: 100,
      activeBatches: 0
    };

    this.zones.set(zoneId, zone);
  }

  /**
   * Start a new batch
   */
  startBatch(batchId: string, cropType: string, zoneId: string, expectedDays: number): BatchProgress | null {
    const zone = this.zones.get(zoneId);
    if (!zone) return null;

    const batch: BatchProgress = {
      batchId,
      cropType,
      zoneId,
      startDate: new Date().toISOString(),
      currentStage: 'germination',
      daysInStage: 0,
      expectedHarvestDate: new Date(Date.now() + expectedDays * 24 * 60 * 60 * 1000).toISOString(),
      healthScore: 100,
      predictedYieldKg: 0,
      issues: []
    };

    this.batches.set(batchId, batch);
    zone.activeBatches++;

    return batch;
  }

  /**
   * Get zone status
   */
  getZoneStatus(zoneId: string): ZoneStatus | null {
    return this.zones.get(zoneId) || null;
  }

  /**
   * Get all zones
   */
  getAllZones(): ZoneStatus[] {
    return Array.from(this.zones.values());
  }

  /**
   * Get batch progress
   */
  getBatchProgress(batchId: string): BatchProgress | null {
    return this.batches.get(batchId) || null;
  }

  /**
   * Get all batches
   */
  getAllBatches(): BatchProgress[] {
    return Array.from(this.batches.values());
  }

  /**
   * Get yield predictions
   */
  getYieldPredictions(): YieldPrediction[] {
    return Array.from(this.yieldPredictions.values());
  }

  /**
   * Get optimization recommendations
   */
  getRecommendations(): OptimizationRecommendation[] {
    return this.recommendations;
  }

  /**
   * Get farm summary
   */
  getFarmSummary(): {
    totalZones: number;
    avgHealthScore: number;
    activeBatches: number;
    upcomingHarvests: number;
    criticalAlerts: number;
    predictedWeeklyYieldKg: number;
  } {
    const zones = Array.from(this.zones.values());
    const batches = Array.from(this.batches.values());
    const predictions = Array.from(this.yieldPredictions.values());

    const avgHealth = zones.length > 0
      ? zones.reduce((sum, z) => sum + z.healthScore, 0) / zones.length
      : 0;

    const weekFromNow = Date.now() + 7 * 24 * 60 * 60 * 1000;
    const upcomingHarvests = batches.filter(b =>
      new Date(b.expectedHarvestDate).getTime() <= weekFromNow
    ).length;

    const criticalDeviations = zones.reduce((count, z) =>
      count + z.deviations.filter(d => d.severity === 'critical').length, 0
    );

    const weeklyYield = predictions
      .filter(p => {
        const harvestDate = new Date(p.harvestWindow.optimal).getTime();
        return harvestDate <= weekFromNow;
      })
      .reduce((sum, p) => sum + p.predictedYieldKg, 0);

    return {
      totalZones: zones.length,
      avgHealthScore: Math.round(avgHealth),
      activeBatches: batches.length,
      upcomingHarvests,
      criticalAlerts: criticalDeviations,
      predictedWeeklyYieldKg: Math.round(weeklyYield * 10) / 10
    };
  }
}

// Singleton instance
let farmAgentInstance: VerticalFarmAgent | null = null;

export function getVerticalFarmAgent(): VerticalFarmAgent {
  if (!farmAgentInstance) {
    farmAgentInstance = new VerticalFarmAgent();
  }
  return farmAgentInstance;
}
