/**
 * AgentOrchestrator
 * Central management system for all LocalGreenChain agents
 *
 * Responsibilities:
 * - Start/stop all agents
 * - Coordinate inter-agent communication
 * - Aggregate metrics and alerts
 * - Handle agent failures and restarts
 * - Provide unified status dashboard
 */

import { BaseAgent } from './BaseAgent';
import { AgentMetrics, AgentAlert, AgentConfig, AgentStatus } from './types';

import { getPlantLineageAgent, PlantLineageAgent } from './PlantLineageAgent';
import { getTransportTrackerAgent, TransportTrackerAgent } from './TransportTrackerAgent';
import { getDemandForecastAgent, DemandForecastAgent } from './DemandForecastAgent';
import { getVerticalFarmAgent, VerticalFarmAgent } from './VerticalFarmAgent';
import { getEnvironmentAnalysisAgent, EnvironmentAnalysisAgent } from './EnvironmentAnalysisAgent';
import { getMarketMatchingAgent, MarketMatchingAgent } from './MarketMatchingAgent';
import { getSustainabilityAgent, SustainabilityAgent } from './SustainabilityAgent';
import { getNetworkDiscoveryAgent, NetworkDiscoveryAgent } from './NetworkDiscoveryAgent';
import { getQualityAssuranceAgent, QualityAssuranceAgent } from './QualityAssuranceAgent';
import { getGrowerAdvisoryAgent, GrowerAdvisoryAgent } from './GrowerAdvisoryAgent';

interface OrchestratorConfig {
  autoStart: boolean;
  enabledAgents: string[];
  alertAggregationIntervalMs: number;
  healthCheckIntervalMs: number;
  maxAlertsPerAgent: number;
}

interface AgentHealth {
  agentId: string;
  agentName: string;
  status: AgentStatus;
  lastRunAt: string | null;
  tasksCompleted: number;
  tasksFailed: number;
  errorRate: number;
  avgExecutionMs: number;
  isHealthy: boolean;
}

interface OrchestratorStatus {
  isRunning: boolean;
  startedAt: string | null;
  uptime: number;
  totalAgents: number;
  runningAgents: number;
  healthyAgents: number;
  totalTasksCompleted: number;
  totalTasksFailed: number;
  activeAlerts: number;
}

interface AgentSummary {
  id: string;
  name: string;
  description: string;
  status: AgentStatus;
  priority: string;
  intervalMs: number;
  metrics: AgentMetrics;
  alertCount: number;
}

export class AgentOrchestrator {
  private static instance: AgentOrchestrator;

  private config: OrchestratorConfig;
  private agents: Map<string, BaseAgent> = new Map();
  private isRunning: boolean = false;
  private startedAt: string | null = null;
  private healthCheckInterval: NodeJS.Timeout | null = null;
  private alertAggregationInterval: NodeJS.Timeout | null = null;
  private aggregatedAlerts: AgentAlert[] = [];
  private eventHandlers: Map<string, ((event: any) => void)[]> = new Map();

  private constructor(config?: Partial<OrchestratorConfig>) {
    this.config = {
      autoStart: false,
      enabledAgents: [
        'plant-lineage-agent',
        'transport-tracker-agent',
        'demand-forecast-agent',
        'vertical-farm-agent',
        'environment-analysis-agent',
        'market-matching-agent',
        'sustainability-agent',
        'network-discovery-agent',
        'quality-assurance-agent',
        'grower-advisory-agent'
      ],
      alertAggregationIntervalMs: 60000,
      healthCheckIntervalMs: 30000,
      maxAlertsPerAgent: 50,
      ...config
    };

    this.initializeAgents();
  }

  public static getInstance(config?: Partial<OrchestratorConfig>): AgentOrchestrator {
    if (!AgentOrchestrator.instance) {
      AgentOrchestrator.instance = new AgentOrchestrator(config);
    }
    return AgentOrchestrator.instance;
  }

  /**
   * Initialize all agents
   */
  private initializeAgents(): void {
    const agentFactories: Record<string, () => BaseAgent> = {
      'plant-lineage-agent': getPlantLineageAgent,
      'transport-tracker-agent': getTransportTrackerAgent,
      'demand-forecast-agent': getDemandForecastAgent,
      'vertical-farm-agent': getVerticalFarmAgent,
      'environment-analysis-agent': getEnvironmentAnalysisAgent,
      'market-matching-agent': getMarketMatchingAgent,
      'sustainability-agent': getSustainabilityAgent,
      'network-discovery-agent': getNetworkDiscoveryAgent,
      'quality-assurance-agent': getQualityAssuranceAgent,
      'grower-advisory-agent': getGrowerAdvisoryAgent
    };

    for (const agentId of this.config.enabledAgents) {
      const factory = agentFactories[agentId];
      if (factory) {
        const agent = factory();
        this.agents.set(agentId, agent);
        console.log(`[Orchestrator] Registered agent: ${agent.config.name}`);
      }
    }
  }

  /**
   * Start all agents
   */
  async startAll(): Promise<void> {
    if (this.isRunning) {
      console.log('[Orchestrator] Already running');
      return;
    }

    console.log('[Orchestrator] Starting all agents...');
    this.isRunning = true;
    this.startedAt = new Date().toISOString();

    // Start agents in priority order
    const sortedAgents = this.getSortedAgentsByPriority();

    for (const agent of sortedAgents) {
      if (agent.config.enabled) {
        try {
          await agent.start();
          console.log(`[Orchestrator] Started: ${agent.config.name}`);
        } catch (error) {
          console.error(`[Orchestrator] Failed to start ${agent.config.name}:`, error);
        }
      }
    }

    // Start health check interval
    this.healthCheckInterval = setInterval(() => {
      this.performHealthCheck();
    }, this.config.healthCheckIntervalMs);

    // Start alert aggregation interval
    this.alertAggregationInterval = setInterval(() => {
      this.aggregateAlerts();
    }, this.config.alertAggregationIntervalMs);

    this.emit('orchestrator_started', { agentCount: this.agents.size });
    console.log(`[Orchestrator] All agents started (${this.agents.size} total)`);
  }

  /**
   * Stop all agents
   */
  async stopAll(): Promise<void> {
    if (!this.isRunning) {
      console.log('[Orchestrator] Not running');
      return;
    }

    console.log('[Orchestrator] Stopping all agents...');

    // Clear intervals
    if (this.healthCheckInterval) {
      clearInterval(this.healthCheckInterval);
      this.healthCheckInterval = null;
    }

    if (this.alertAggregationInterval) {
      clearInterval(this.alertAggregationInterval);
      this.alertAggregationInterval = null;
    }

    // Stop all agents
    for (const agent of Array.from(this.agents.values())) {
      try {
        await agent.stop();
        console.log(`[Orchestrator] Stopped: ${agent.config.name}`);
      } catch (error) {
        console.error(`[Orchestrator] Error stopping ${agent.config.name}:`, error);
      }
    }

    this.isRunning = false;
    this.emit('orchestrator_stopped', {});
    console.log('[Orchestrator] All agents stopped');
  }

  /**
   * Start a specific agent
   */
  async startAgent(agentId: string): Promise<boolean> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      console.error(`[Orchestrator] Agent not found: ${agentId}`);
      return false;
    }

    try {
      await agent.start();
      return true;
    } catch (error) {
      console.error(`[Orchestrator] Failed to start ${agentId}:`, error);
      return false;
    }
  }

  /**
   * Stop a specific agent
   */
  async stopAgent(agentId: string): Promise<boolean> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      console.error(`[Orchestrator] Agent not found: ${agentId}`);
      return false;
    }

    try {
      await agent.stop();
      return true;
    } catch (error) {
      console.error(`[Orchestrator] Failed to stop ${agentId}:`, error);
      return false;
    }
  }

  /**
   * Restart a specific agent
   */
  async restartAgent(agentId: string): Promise<boolean> {
    await this.stopAgent(agentId);
    return this.startAgent(agentId);
  }

  /**
   * Get sorted agents by priority
   */
  private getSortedAgentsByPriority(): BaseAgent[] {
    const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
    return Array.from(this.agents.values()).sort((a, b) =>
      priorityOrder[a.config.priority] - priorityOrder[b.config.priority]
    );
  }

  /**
   * Perform health check on all agents
   */
  private performHealthCheck(): void {
    for (const [agentId, agent] of Array.from(this.agents.entries())) {
      const health = this.getAgentHealth(agentId);

      if (!health.isHealthy) {
        console.warn(`[Orchestrator] Unhealthy agent detected: ${agent.config.name}`);

        // Auto-restart if error rate is high
        if (health.errorRate > 50 && agent.status === 'running') {
          console.log(`[Orchestrator] Auto-restarting: ${agent.config.name}`);
          this.restartAgent(agentId);
        }
      }
    }
  }

  /**
   * Aggregate alerts from all agents
   */
  private aggregateAlerts(): void {
    this.aggregatedAlerts = [];

    for (const agent of Array.from(this.agents.values())) {
      const alerts = agent.getAlerts()
        .filter(a => !a.acknowledged)
        .slice(-this.config.maxAlertsPerAgent);

      this.aggregatedAlerts.push(...alerts);
    }

    // Sort by severity and timestamp
    const severityOrder = { critical: 0, error: 1, warning: 2, info: 3 };
    this.aggregatedAlerts.sort((a, b) => {
      const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
      if (severityDiff !== 0) return severityDiff;
      return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
    });

    // Emit event if critical alerts
    const criticalAlerts = this.aggregatedAlerts.filter(a => a.severity === 'critical');
    if (criticalAlerts.length > 0) {
      this.emit('critical_alerts', { count: criticalAlerts.length, alerts: criticalAlerts });
    }
  }

  /**
   * Get agent health
   */
  getAgentHealth(agentId: string): AgentHealth {
    const agent = this.agents.get(agentId);
    if (!agent) {
      return {
        agentId,
        agentName: 'Unknown',
        status: 'idle',
        lastRunAt: null,
        tasksCompleted: 0,
        tasksFailed: 0,
        errorRate: 0,
        avgExecutionMs: 0,
        isHealthy: false
      };
    }

    const metrics = agent.getMetrics();
    const totalTasks = metrics.tasksCompleted + metrics.tasksFailed;
    const errorRate = totalTasks > 0
      ? (metrics.tasksFailed / totalTasks) * 100
      : 0;

    // Check if agent is responding (last run within 2x interval)
    const lastRunTime = metrics.lastRunAt ? new Date(metrics.lastRunAt).getTime() : 0;
    const maxTimeSinceRun = agent.config.intervalMs * 2;
    const isResponding = agent.status !== 'running' ||
      (Date.now() - lastRunTime < maxTimeSinceRun);

    const isHealthy = agent.status !== 'error' &&
      errorRate < 30 &&
      isResponding;

    return {
      agentId,
      agentName: agent.config.name,
      status: agent.status,
      lastRunAt: metrics.lastRunAt,
      tasksCompleted: metrics.tasksCompleted,
      tasksFailed: metrics.tasksFailed,
      errorRate: Math.round(errorRate),
      avgExecutionMs: Math.round(metrics.averageExecutionMs),
      isHealthy
    };
  }

  /**
   * Get orchestrator status
   */
  getStatus(): OrchestratorStatus {
    const healthStatuses = Array.from(this.agents.keys()).map(id => this.getAgentHealth(id));
    const runningAgents = healthStatuses.filter(h => h.status === 'running').length;
    const healthyAgents = healthStatuses.filter(h => h.isHealthy).length;
    const totalCompleted = healthStatuses.reduce((sum, h) => sum + h.tasksCompleted, 0);
    const totalFailed = healthStatuses.reduce((sum, h) => sum + h.tasksFailed, 0);

    return {
      isRunning: this.isRunning,
      startedAt: this.startedAt,
      uptime: this.startedAt
        ? Date.now() - new Date(this.startedAt).getTime()
        : 0,
      totalAgents: this.agents.size,
      runningAgents,
      healthyAgents,
      totalTasksCompleted: totalCompleted,
      totalTasksFailed: totalFailed,
      activeAlerts: this.aggregatedAlerts.filter(a => !a.acknowledged).length
    };
  }

  /**
   * Get all agent summaries
   */
  getAgentSummaries(): AgentSummary[] {
    return Array.from(this.agents.values()).map(agent => ({
      id: agent.config.id,
      name: agent.config.name,
      description: agent.config.description,
      status: agent.status,
      priority: agent.config.priority,
      intervalMs: agent.config.intervalMs,
      metrics: agent.getMetrics(),
      alertCount: agent.getAlerts().filter(a => !a.acknowledged).length
    }));
  }

  /**
   * Get all alerts
   */
  getAlerts(severity?: string): AgentAlert[] {
    if (severity) {
      return this.aggregatedAlerts.filter(a => a.severity === severity);
    }
    return this.aggregatedAlerts;
  }

  /**
   * Acknowledge an alert
   */
  acknowledgeAlert(alertId: string): boolean {
    const alert = this.aggregatedAlerts.find(a => a.id === alertId);
    if (alert) {
      alert.acknowledged = true;
      return true;
    }
    return false;
  }

  /**
   * Get specific agent
   */
  getAgent<T extends BaseAgent>(agentId: string): T | null {
    return this.agents.get(agentId) as T || null;
  }

  /**
   * Get typed agent instances
   */
  getPlantLineageAgent(): PlantLineageAgent | null {
    return this.getAgent<PlantLineageAgent>('plant-lineage-agent');
  }

  getTransportTrackerAgent(): TransportTrackerAgent | null {
    return this.getAgent<TransportTrackerAgent>('transport-tracker-agent');
  }

  getDemandForecastAgent(): DemandForecastAgent | null {
    return this.getAgent<DemandForecastAgent>('demand-forecast-agent');
  }

  getVerticalFarmAgent(): VerticalFarmAgent | null {
    return this.getAgent<VerticalFarmAgent>('vertical-farm-agent');
  }

  getEnvironmentAnalysisAgent(): EnvironmentAnalysisAgent | null {
    return this.getAgent<EnvironmentAnalysisAgent>('environment-analysis-agent');
  }

  getMarketMatchingAgent(): MarketMatchingAgent | null {
    return this.getAgent<MarketMatchingAgent>('market-matching-agent');
  }

  getSustainabilityAgent(): SustainabilityAgent | null {
    return this.getAgent<SustainabilityAgent>('sustainability-agent');
  }

  getNetworkDiscoveryAgent(): NetworkDiscoveryAgent | null {
    return this.getAgent<NetworkDiscoveryAgent>('network-discovery-agent');
  }

  getQualityAssuranceAgent(): QualityAssuranceAgent | null {
    return this.getAgent<QualityAssuranceAgent>('quality-assurance-agent');
  }

  getGrowerAdvisoryAgent(): GrowerAdvisoryAgent | null {
    return this.getAgent<GrowerAdvisoryAgent>('grower-advisory-agent');
  }

  /**
   * Register event handler
   */
  on(event: string, handler: (data: any) => void): void {
    const handlers = this.eventHandlers.get(event) || [];
    handlers.push(handler);
    this.eventHandlers.set(event, handlers);
  }

  /**
   * Emit event
   */
  private emit(event: string, data: any): void {
    const handlers = this.eventHandlers.get(event) || [];
    for (const handler of handlers) {
      try {
        handler(data);
      } catch (error) {
        console.error(`[Orchestrator] Event handler error for ${event}:`, error);
      }
    }
  }

  /**
   * Get dashboard data
   */
  getDashboard(): {
    status: OrchestratorStatus;
    agents: AgentSummary[];
    health: AgentHealth[];
    recentAlerts: AgentAlert[];
    metrics: {
      totalTasksToday: number;
      avgErrorRate: number;
      avgExecutionTime: number;
    };
  } {
    const status = this.getStatus();
    const agents = this.getAgentSummaries();
    const health = Array.from(this.agents.keys()).map(id => this.getAgentHealth(id));
    const recentAlerts = this.aggregatedAlerts.slice(0, 10);

    const avgErrorRate = health.length > 0
      ? health.reduce((sum, h) => sum + h.errorRate, 0) / health.length
      : 0;

    const avgExecutionTime = health.length > 0
      ? health.reduce((sum, h) => sum + h.avgExecutionMs, 0) / health.length
      : 0;

    return {
      status,
      agents,
      health,
      recentAlerts,
      metrics: {
        totalTasksToday: status.totalTasksCompleted,
        avgErrorRate: Math.round(avgErrorRate),
        avgExecutionTime: Math.round(avgExecutionTime)
      }
    };
  }
}

/**
 * Get orchestrator singleton
 */
export function getOrchestrator(config?: Partial<OrchestratorConfig>): AgentOrchestrator {
  return AgentOrchestrator.getInstance(config);
}

/**
 * Start all agents
 */
export async function startAllAgents(): Promise<void> {
  const orchestrator = getOrchestrator();
  await orchestrator.startAll();
}

/**
 * Stop all agents
 */
export async function stopAllAgents(): Promise<void> {
  const orchestrator = getOrchestrator();
  await orchestrator.stopAll();
}
