/**
 * DemandForecastAgent
 * Autonomous demand forecasting and market intelligence
 *
 * Responsibilities:
 * - Monitor consumer preference changes
 * - Generate regional demand signals
 * - Predict seasonal demand patterns
 * - Identify supply gaps and opportunities
 * - Provide early warnings for demand shifts
 */

import { BaseAgent } from './BaseAgent';
import { AgentConfig, AgentTask } from './types';
import { getDemandForecaster, DemandForecaster } from '../demand/forecaster';
import {
  DemandSignal,
  ConsumerPreference,
  ProduceCategory,
  DemandForecast
} from '../demand/types';

interface DemandTrend {
  produceType: string;
  category: ProduceCategory;
  weeklyGrowthRate: number;
  direction: 'increasing' | 'stable' | 'decreasing';
  confidence: number;
  seasonalPeak: 'spring' | 'summer' | 'fall' | 'winter' | null;
  predictedPeakWeek: number; // Week of year
}

interface MarketOpportunity {
  id: string;
  produceType: string;
  region: string;
  gapKg: number;
  estimatedRevenue: number;
  competitionLevel: 'low' | 'medium' | 'high';
  urgency: 'immediate' | 'this_week' | 'this_month' | 'next_season';
  expiresAt: string;
  confidence: number;
}

interface DemandAlert {
  type: 'surge' | 'drop' | 'new_demand' | 'seasonal_shift' | 'supply_critical';
  produceType: string;
  region: string;
  magnitude: number; // Percentage change
  description: string;
  recommendedAction: string;
}

interface RegionalDemandSummary {
  regionName: string;
  centerLat: number;
  centerLon: number;
  totalConsumers: number;
  totalWeeklyDemandKg: number;
  topProduce: { type: string; demandKg: number }[];
  supplyStatus: 'surplus' | 'balanced' | 'shortage' | 'critical';
  lastUpdated: string;
}

export class DemandForecastAgent extends BaseAgent {
  private trends: Map<string, DemandTrend> = new Map();
  private opportunities: MarketOpportunity[] = [];
  private demandAlerts: DemandAlert[] = [];
  private regionalSummaries: Map<string, RegionalDemandSummary> = new Map();
  private historicalSignals: DemandSignal[] = [];

  // Pre-configured regions for monitoring
  private monitoredRegions = [
    { name: 'Urban Core', lat: 40.7128, lon: -74.0060, radius: 25 },
    { name: 'Suburban Ring', lat: 40.7128, lon: -74.0060, radius: 50 },
    { name: 'Regional Hub', lat: 40.7128, lon: -74.0060, radius: 100 }
  ];

  constructor() {
    const config: AgentConfig = {
      id: 'demand-forecast-agent',
      name: 'Demand Forecast Agent',
      description: 'Monitors consumer demand and generates market intelligence',
      enabled: true,
      intervalMs: 300000, // Run every 5 minutes
      priority: 'high',
      maxRetries: 3,
      timeoutMs: 60000
    };
    super(config);
  }

  /**
   * Main execution cycle
   */
  async runOnce(): Promise<AgentTask | null> {
    const forecaster = getDemandForecaster();
    const currentSeason = this.getCurrentSeason();

    // Generate demand signals for monitored regions
    for (const region of this.monitoredRegions) {
      try {
        const signal = forecaster.generateDemandSignal(
          region.lat,
          region.lon,
          region.radius,
          region.name,
          currentSeason
        );

        // Store for historical analysis
        this.historicalSignals.push(signal);
        if (this.historicalSignals.length > 1000) {
          this.historicalSignals = this.historicalSignals.slice(-500);
        }

        // Update regional summary
        this.updateRegionalSummary(signal);

        // Check for alerts
        this.checkForDemandAlerts(signal);
      } catch (error) {
        console.error(`[DemandForecastAgent] Error processing region ${region.name}:`, error);
      }
    }

    // Analyze trends
    this.analyzeTrends();

    // Identify opportunities
    this.identifyOpportunities();

    // Generate forecasts
    const forecast = this.generateConsolidatedForecast();

    return this.createTaskResult('demand_forecast', 'completed', {
      regionsProcessed: this.monitoredRegions.length,
      trendsIdentified: this.trends.size,
      opportunitiesFound: this.opportunities.length,
      alertsGenerated: this.demandAlerts.length,
      forecast
    });
  }

  /**
   * Update regional demand summary
   */
  private updateRegionalSummary(signal: DemandSignal): void {
    const topProduce = signal.demandItems
      .sort((a, b) => b.weeklyDemandKg - a.weeklyDemandKg)
      .slice(0, 5)
      .map(item => ({ type: item.produceType, demandKg: item.weeklyDemandKg }));

    const summary: RegionalDemandSummary = {
      regionName: signal.region.name,
      centerLat: signal.region.centerLat,
      centerLon: signal.region.centerLon,
      totalConsumers: signal.totalConsumers,
      totalWeeklyDemandKg: signal.totalWeeklyDemandKg,
      topProduce,
      supplyStatus: signal.supplyStatus,
      lastUpdated: new Date().toISOString()
    };

    this.regionalSummaries.set(signal.region.name, summary);
  }

  /**
   * Check for demand alerts
   */
  private checkForDemandAlerts(signal: DemandSignal): void {
    // Check for critical supply status
    if (signal.supplyStatus === 'critical') {
      const alert: DemandAlert = {
        type: 'supply_critical',
        produceType: 'multiple',
        region: signal.region.name,
        magnitude: Math.round((signal.supplyGapKg / signal.totalWeeklyDemandKg) * 100),
        description: `Critical supply gap of ${signal.supplyGapKg.toFixed(1)}kg in ${signal.region.name}`,
        recommendedAction: 'Urgent: Contact available growers to increase supply'
      };

      this.demandAlerts.push(alert);
      this.createAlert('critical', 'Critical Supply Gap',
        alert.description,
        { actionRequired: alert.recommendedAction, relatedEntityType: 'region' }
      );
    }

    // Check for demand surges
    const previousSignal = this.historicalSignals
      .filter(s => s.region.name === signal.region.name)
      .slice(-2)[0];

    if (previousSignal) {
      const demandChange = signal.totalWeeklyDemandKg - previousSignal.totalWeeklyDemandKg;
      const changePercent = (demandChange / previousSignal.totalWeeklyDemandKg) * 100;

      if (changePercent > 20) {
        this.demandAlerts.push({
          type: 'surge',
          produceType: 'multiple',
          region: signal.region.name,
          magnitude: Math.round(changePercent),
          description: `Demand surge of ${changePercent.toFixed(0)}% detected in ${signal.region.name}`,
          recommendedAction: 'Increase production capacity to meet growing demand'
        });
      } else if (changePercent < -20) {
        this.demandAlerts.push({
          type: 'drop',
          produceType: 'multiple',
          region: signal.region.name,
          magnitude: Math.round(Math.abs(changePercent)),
          description: `Demand drop of ${Math.abs(changePercent).toFixed(0)}% detected in ${signal.region.name}`,
          recommendedAction: 'Review inventory and adjust planting plans'
        });
      }
    }

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

  /**
   * Analyze demand trends
   */
  private analyzeTrends(): void {
    // Group signals by produce type
    const produceSignals = new Map<string, number[]>();

    for (const signal of this.historicalSignals.slice(-100)) {
      for (const item of signal.demandItems) {
        const history = produceSignals.get(item.produceType) || [];
        history.push(item.weeklyDemandKg);
        produceSignals.set(item.produceType, history);
      }
    }

    // Calculate trends
    for (const [produceType, history] of produceSignals) {
      if (history.length < 3) continue;

      // Calculate growth rate (simple linear regression slope)
      const n = history.length;
      const sumX = (n * (n - 1)) / 2;
      const sumY = history.reduce((a, b) => a + b, 0);
      const sumXY = history.reduce((sum, y, i) => sum + i * y, 0);
      const sumX2 = (n * (n - 1) * (2 * n - 1)) / 6;

      const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
      const avgDemand = sumY / n;
      const weeklyGrowthRate = avgDemand > 0 ? (slope / avgDemand) * 100 : 0;

      let direction: DemandTrend['direction'];
      if (weeklyGrowthRate > 2) direction = 'increasing';
      else if (weeklyGrowthRate < -2) direction = 'decreasing';
      else direction = 'stable';

      // Determine seasonal peak (simplified)
      const seasonalPeak = this.determineSeasonalPeak(produceType);

      this.trends.set(produceType, {
        produceType,
        category: 'leafy_greens', // Would need actual category lookup
        weeklyGrowthRate: Math.round(weeklyGrowthRate * 100) / 100,
        direction,
        confidence: Math.min(95, 50 + history.length * 5),
        seasonalPeak,
        predictedPeakWeek: seasonalPeak ? this.getSeasonPeakWeek(seasonalPeak) : 0
      });
    }
  }

  /**
   * Determine seasonal peak for produce type
   */
  private determineSeasonalPeak(produceType: string): 'spring' | 'summer' | 'fall' | 'winter' | null {
    const seasonalData: Record<string, 'spring' | 'summer' | 'fall' | 'winter'> = {
      'lettuce': 'spring',
      'tomato': 'summer',
      'spinach': 'fall',
      'kale': 'fall',
      'basil': 'summer',
      'cucumber': 'summer',
      'pepper': 'summer',
      'strawberry': 'spring',
      'pumpkin': 'fall',
      'squash': 'fall'
    };

    return seasonalData[produceType.toLowerCase()] || null;
  }

  /**
   * Get approximate peak week for season
   */
  private getSeasonPeakWeek(season: 'spring' | 'summer' | 'fall' | 'winter'): number {
    const seasonWeeks: Record<string, number> = {
      'spring': 14,
      'summer': 28,
      'fall': 40,
      'winter': 4
    };
    return seasonWeeks[season];
  }

  /**
   * Identify market opportunities
   */
  private identifyOpportunities(): void {
    const newOpportunities: MarketOpportunity[] = [];

    for (const summary of this.regionalSummaries.values()) {
      if (summary.supplyStatus === 'shortage' || summary.supplyStatus === 'critical') {
        // Find specific produce gaps
        const regionalSignal = this.historicalSignals
          .filter(s => s.region.name === summary.regionName)
          .pop();

        if (regionalSignal) {
          for (const item of regionalSignal.demandItems) {
            if (item.gapKg > 10) { // Minimum threshold
              const existingOpp = newOpportunities.find(o =>
                o.produceType === item.produceType && o.region === summary.regionName
              );

              if (!existingOpp) {
                newOpportunities.push({
                  id: `opp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
                  produceType: item.produceType,
                  region: summary.regionName,
                  gapKg: item.gapKg,
                  estimatedRevenue: item.gapKg * item.averageWillingPrice,
                  competitionLevel: item.matchedGrowers < 2 ? 'low' :
                    item.matchedGrowers < 5 ? 'medium' : 'high',
                  urgency: item.urgency,
                  expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
                  confidence: regionalSignal.confidenceLevel
                });
              }
            }
          }
        }
      }
    }

    // Sort by estimated revenue
    this.opportunities = newOpportunities
      .sort((a, b) => b.estimatedRevenue - a.estimatedRevenue)
      .slice(0, 50);
  }

  /**
   * Generate consolidated forecast
   */
  private generateConsolidatedForecast(): {
    weeklyForecast: { produceType: string; demandKg: number; confidence: number }[];
    monthlyProjection: number;
    topGrowthItems: string[];
    topDeclineItems: string[];
  } {
    const weeklyForecast = Array.from(this.trends.values())
      .map(trend => ({
        produceType: trend.produceType,
        demandKg: this.getLatestDemand(trend.produceType),
        confidence: trend.confidence
      }))
      .sort((a, b) => b.demandKg - a.demandKg)
      .slice(0, 10);

    const monthlyProjection = weeklyForecast.reduce((sum, item) => sum + item.demandKg * 4, 0);

    const topGrowthItems = Array.from(this.trends.values())
      .filter(t => t.direction === 'increasing')
      .sort((a, b) => b.weeklyGrowthRate - a.weeklyGrowthRate)
      .slice(0, 5)
      .map(t => t.produceType);

    const topDeclineItems = Array.from(this.trends.values())
      .filter(t => t.direction === 'decreasing')
      .sort((a, b) => a.weeklyGrowthRate - b.weeklyGrowthRate)
      .slice(0, 5)
      .map(t => t.produceType);

    return {
      weeklyForecast,
      monthlyProjection: Math.round(monthlyProjection * 100) / 100,
      topGrowthItems,
      topDeclineItems
    };
  }

  /**
   * Get latest demand for produce type
   */
  private getLatestDemand(produceType: string): number {
    for (let i = this.historicalSignals.length - 1; i >= 0; i--) {
      const item = this.historicalSignals[i].demandItems.find(
        d => d.produceType === produceType
      );
      if (item) return item.weeklyDemandKg;
    }
    return 0;
  }

  /**
   * Get current season
   */
  private getCurrentSeason(): 'spring' | 'summer' | 'fall' | 'winter' {
    const month = new Date().getMonth();
    if (month >= 2 && month <= 4) return 'spring';
    if (month >= 5 && month <= 7) return 'summer';
    if (month >= 8 && month <= 10) return 'fall';
    return 'winter';
  }

  /**
   * Add a region to monitor
   */
  addMonitoredRegion(name: string, lat: number, lon: number, radius: number): void {
    this.monitoredRegions.push({ name, lat, lon, radius });
  }

  /**
   * Get regional summaries
   */
  getRegionalSummaries(): RegionalDemandSummary[] {
    return Array.from(this.regionalSummaries.values());
  }

  /**
   * Get market opportunities
   */
  getOpportunities(): MarketOpportunity[] {
    return this.opportunities;
  }

  /**
   * Get demand trends
   */
  getTrends(): DemandTrend[] {
    return Array.from(this.trends.values());
  }

  /**
   * Get demand alerts
   */
  getDemandAlerts(): DemandAlert[] {
    return this.demandAlerts;
  }
}

// Singleton instance
let demandAgentInstance: DemandForecastAgent | null = null;

export function getDemandForecastAgent(): DemandForecastAgent {
  if (!demandAgentInstance) {
    demandAgentInstance = new DemandForecastAgent();
  }
  return demandAgentInstance;
}
