/**
 * Queen Coordinator Class
 * 
 * The Queen manages high-level coordination, decision-making,
 * and strategic planning for the Hive Mind swarm.
 */

import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
import { Agent } from './Agent.js';
import { DatabaseManager } from './DatabaseManager.js';
import { MCPToolWrapper } from '../integration/MCPToolWrapper.js';
import {
  SwarmTopology,
  Task,
  AgentType,
  QueenMode,
  ConsensusProposal,
  QueenDecision,
  CoordinationStrategy
} from '../types.js';

interface QueenConfig {
  swarmId: string;
  mode: QueenMode;
  topology: SwarmTopology;
}

export class Queen extends EventEmitter {
  private id: string;
  private config: QueenConfig;
  private agents: Map<string, Agent>;
  private taskQueue: Map<string, Task>;
  private strategies: Map<string, CoordinationStrategy>;
  private db: DatabaseManager;
  private mcpWrapper: MCPToolWrapper;
  private isActive: boolean = false;

  constructor(config: QueenConfig) {
    super();
    this.id = uuidv4();
    this.config = config;
    this.agents = new Map();
    this.taskQueue = new Map();
    this.strategies = new Map();
    this.initializeStrategies();
  }

  /**
   * Initialize the Queen and her coordination capabilities
   */
  async initialize(): Promise<void> {
    this.db = await DatabaseManager.getInstance();
    this.mcpWrapper = new MCPToolWrapper();
    
    // Create Queen as a special coordinator agent
    await this.db.createAgent({
      id: this.id,
      swarmId: this.config.swarmId,
      name: 'Queen',
      type: 'coordinator',
      capabilities: JSON.stringify([
        'strategic_planning',
        'task_allocation',
        'consensus_coordination',
        'performance_optimization',
        'swarm_governance'
      ]),
      status: 'active',
      metadata: JSON.stringify({ role: 'queen', mode: this.config.mode })
    });
    
    this.isActive = true;
    
    // Start coordination loops
    this.startCoordinationLoop();
    this.startOptimizationLoop();
    
    this.emit('initialized');
  }

  /**
   * Register a new agent with the Queen
   */
  async registerAgent(agent: Agent): Promise<void> {
    this.agents.set(agent.id, agent);
    
    // Analyze agent capabilities and update strategies
    await this.analyzeAgentCapabilities(agent);
    
    // Notify other agents in distributed mode
    if (this.config.mode === 'distributed') {
      await this.broadcastAgentRegistration(agent);
    }
    
    this.emit('agentRegistered', { agent });
  }

  /**
   * Handle task submission
   */
  async onTaskSubmitted(task: Task): Promise<QueenDecision> {
    this.taskQueue.set(task.id, task);
    
    // Analyze task requirements
    const analysis = await this.analyzeTask(task);
    
    // Make strategic decision
    const decision = await this.makeStrategicDecision(task, analysis);
    
    // If consensus required, initiate consensus process
    if (task.requireConsensus) {
      await this.initiateConsensus(task, decision);
    }
    
    // Apply decision
    await this.applyDecision(decision);
    
    this.emit('taskDecision', { task, decision });
    
    return decision;
  }

  /**
   * Make a strategic decision about task execution
   */
  private async makeStrategicDecision(task: Task, analysis: any): Promise<QueenDecision> {
    // Use MCP neural capabilities for decision making
    const neuralAnalysis = await this.mcpWrapper.analyzePattern({
      action: 'analyze',
      operation: 'task_strategy',
      metadata: {
        task: task.description,
        priority: task.priority,
        topology: this.config.topology,
        availableAgents: this.getAvailableAgents().length
      }
    });
    
    // Select optimal strategy
    const strategy = this.selectOptimalStrategy(task, analysis, neuralAnalysis);
    
    // Identify best agents for the task
    const selectedAgents = await this.selectAgentsForTask(task, strategy);
    
    // Create execution plan
    const executionPlan = this.createExecutionPlan(task, selectedAgents, strategy);
    
    return {
      id: uuidv4(),
      taskId: task.id,
      strategy,
      selectedAgents: selectedAgents.map(a => a.id),
      executionPlan,
      confidence: analysis.confidence || 0.85,
      rationale: analysis.rationale || 'Strategic analysis completed',
      timestamp: new Date()
    };
  }

  /**
   * Select optimal coordination strategy
   */
  private selectOptimalStrategy(task: Task, analysis: any, neuralAnalysis: any): CoordinationStrategy {
    // Strategy selection based on multiple factors
    const factors = {
      taskComplexity: analysis.complexity || 'medium',
      agentAvailability: this.getAvailableAgents().length,
      topology: this.config.topology,
      priority: task.priority,
      consensusRequired: task.requireConsensus
    };
    
    // Use topology-specific strategies
    if (this.config.topology === 'hierarchical' && factors.taskComplexity === 'high') {
      return this.strategies.get('hierarchical-cascade')!;
    }
    
    if (this.config.topology === 'mesh' && factors.consensusRequired) {
      return this.strategies.get('mesh-consensus')!;
    }
    
    if (factors.priority === 'critical') {
      return this.strategies.get('priority-fast-track')!;
    }
    
    // Default adaptive strategy
    return this.strategies.get('adaptive-default')!;
  }

  /**
   * Select best agents for a task
   */
  private async selectAgentsForTask(task: Task, strategy: CoordinationStrategy): Promise<Agent[]> {
    const availableAgents = this.getAvailableAgents();
    const requiredCapabilities = task.requiredCapabilities || [];
    
    // Score agents based on capabilities and current load
    const scoredAgents = await Promise.all(
      availableAgents.map(async (agent) => {
        const score = await this.scoreAgentForTask(agent, task, requiredCapabilities);
        return { agent, score };
      })
    );
    
    // Sort by score and select top agents
    scoredAgents.sort((a, b) => b.score - a.score);
    
    const maxAgents = Math.min(task.maxAgents, strategy.maxAgents || 3);
    return scoredAgents.slice(0, maxAgents).map(sa => sa.agent);
  }

  /**
   * Score an agent for a specific task
   */
  private async scoreAgentForTask(agent: Agent, task: Task, requiredCapabilities: string[]): Promise<number> {
    let score = 0;
    
    // Capability match
    const capabilityMatches = requiredCapabilities.filter(cap => 
      agent.capabilities.includes(cap)
    ).length;
    score += capabilityMatches * 10;
    
    // Agent type suitability
    const typeSuitability = this.getTypeSuitabilityForTask(agent.type, task);
    score += typeSuitability * 5;
    
    // Current workload (prefer less busy agents)
    if (agent.status === 'idle') score += 8;
    else if (agent.status === 'active') score += 4;
    
    // Historical performance (from database)
    const performance = await this.db.getAgentPerformance(agent.id);
    if (performance) {
      score += performance.successRate * 10;
    }
    
    // Specialty bonus
    if (agent.type === 'specialist' && requiredCapabilities.length > 0) {
      score += 5;
    }
    
    return score;
  }

  /**
   * Get type suitability score for a task
   */
  private getTypeSuitabilityForTask(agentType: AgentType, task: Task): number {
    const suitabilityMap: Record<string, Record<AgentType, number>> = {
      research: { researcher: 10, analyst: 8, specialist: 6, coder: 4, coordinator: 5, architect: 5, tester: 3, reviewer: 4, optimizer: 4, documenter: 6, monitor: 3 },
      development: { coder: 10, architect: 8, tester: 7, reviewer: 6, coordinator: 5, specialist: 6, researcher: 4, analyst: 4, optimizer: 5, documenter: 4, monitor: 3 },
      analysis: { analyst: 10, researcher: 8, specialist: 6, reviewer: 5, coordinator: 5, architect: 4, coder: 4, tester: 3, optimizer: 5, documenter: 4, monitor: 4 },
      testing: { tester: 10, reviewer: 8, analyst: 6, coder: 5, coordinator: 4, specialist: 5, researcher: 3, architect: 4, optimizer: 4, documenter: 3, monitor: 4 },
      optimization: { optimizer: 10, analyst: 8, coder: 7, architect: 6, coordinator: 5, specialist: 6, researcher: 4, tester: 4, reviewer: 5, documenter: 3, monitor: 4 }
    };
    
    // Detect task type from description
    const taskType = this.detectTaskType(task.description);
    return suitabilityMap[taskType]?.[agentType] || 5;
  }

  /**
   * Detect task type from description
   */
  private detectTaskType(description: string): string {
    const lower = description.toLowerCase();
    
    if (lower.includes('research') || lower.includes('investigate') || lower.includes('explore')) {
      return 'research';
    }
    if (lower.includes('develop') || lower.includes('implement') || lower.includes('build') || lower.includes('create')) {
      return 'development';
    }
    if (lower.includes('analyze') || lower.includes('review') || lower.includes('assess')) {
      return 'analysis';
    }
    if (lower.includes('test') || lower.includes('validate') || lower.includes('verify')) {
      return 'testing';
    }
    if (lower.includes('optimize') || lower.includes('improve') || lower.includes('enhance')) {
      return 'optimization';
    }
    
    return 'general';
  }

  /**
   * Create execution plan for task
   */
  private createExecutionPlan(task: Task, agents: Agent[], strategy: CoordinationStrategy): any {
    return {
      phases: strategy.phases || ['preparation', 'execution', 'validation'],
      agentAssignments: agents.map(agent => ({
        agentId: agent.id,
        role: this.determineAgentRole(agent, task),
        responsibilities: this.getAgentResponsibilities(agent, task)
      })),
      coordinationPoints: strategy.coordinationPoints || ['start', 'midpoint', 'completion'],
      checkpoints: this.createCheckpoints(task, strategy),
      fallbackPlan: this.createFallbackPlan(task, agents)
    };
  }

  /**
   * Initiate consensus process
   */
  private async initiateConsensus(task: Task, decision: QueenDecision): Promise<void> {
    const proposal: ConsensusProposal = {
      id: uuidv4(),
      swarmId: this.config.swarmId,
      taskId: task.id,
      proposal: {
        decision,
        task: task.description,
        rationale: decision.rationale
      },
      requiredThreshold: 0.66,
      deadline: new Date(Date.now() + 5 * 60 * 1000) // 5 minutes
    };
    
    await this.db.createConsensusProposal(proposal);
    
    // Notify all agents to vote
    await this.broadcastConsensusRequest(proposal);
  }

  /**
   * Apply Queen's decision
   */
  private async applyDecision(decision: QueenDecision): Promise<void> {
    // Update task with assignments
    await this.db.updateTask(decision.taskId, {
      assigned_agents: JSON.stringify(decision.selectedAgents),
      status: 'assigned',
      assigned_at: new Date()
    });
    
    // Notify selected agents
    for (const agentId of decision.selectedAgents) {
      const agent = this.agents.get(agentId);
      if (agent) {
        await agent.assignTask(decision.taskId, decision.executionPlan);
      }
    }
    
    // Store decision in memory for learning
    await this.mcpWrapper.storeMemory({
      action: 'store',
      key: `decision/${decision.taskId}`,
      value: JSON.stringify(decision),
      namespace: 'queen-decisions',
      ttl: 86400 * 7 // 7 days
    });
  }

  /**
   * Start coordination loop
   */
  private startCoordinationLoop(): void {
    setInterval(async () => {
      if (!this.isActive) return;
      
      try {
        // Monitor agent health
        await this.monitorAgentHealth();
        
        // Check task progress
        await this.checkTaskProgress();
        
        // Rebalance if needed
        await this.checkRebalancing();
        
      } catch (error) {
        this.emit('error', error);
      }
    }, 5000); // Every 5 seconds
  }

  /**
   * Start optimization loop
   */
  private startOptimizationLoop(): void {
    setInterval(async () => {
      if (!this.isActive) return;
      
      try {
        // Analyze performance patterns
        await this.analyzePerformancePatterns();
        
        // Optimize strategies
        await this.optimizeStrategies();
        
        // Train neural patterns
        await this.trainNeuralPatterns();
        
      } catch (error) {
        this.emit('error', error);
      }
    }, 60000); // Every minute
  }

  /**
   * Initialize coordination strategies
   */
  private initializeStrategies(): void {
    // Hierarchical cascade strategy
    this.strategies.set('hierarchical-cascade', {
      name: 'Hierarchical Cascade',
      description: 'Top-down task distribution with clear delegation',
      phases: ['planning', 'delegation', 'execution', 'aggregation'],
      maxAgents: 5,
      coordinationPoints: ['phase-transition', 'milestone', 'completion'],
      suitable_for: ['complex-tasks', 'multi-phase-projects']
    });
    
    // Mesh consensus strategy
    this.strategies.set('mesh-consensus', {
      name: 'Mesh Consensus',
      description: 'Peer-to-peer coordination with consensus requirements',
      phases: ['proposal', 'discussion', 'consensus', 'execution'],
      maxAgents: 7,
      coordinationPoints: ['consensus-check', 'progress-sync', 'final-vote'],
      suitable_for: ['critical-decisions', 'collaborative-tasks']
    });
    
    // Priority fast-track strategy
    this.strategies.set('priority-fast-track', {
      name: 'Priority Fast Track',
      description: 'Rapid execution for critical tasks',
      phases: ['immediate-assignment', 'parallel-execution', 'quick-validation'],
      maxAgents: 3,
      coordinationPoints: ['start', 'critical-path', 'completion'],
      suitable_for: ['urgent-tasks', 'critical-fixes']
    });
    
    // Adaptive default strategy
    this.strategies.set('adaptive-default', {
      name: 'Adaptive Default',
      description: 'Flexible strategy that adapts to task requirements',
      phases: ['analysis', 'planning', 'execution', 'review'],
      maxAgents: 4,
      coordinationPoints: ['checkpoint', 'adaptation-point', 'completion'],
      suitable_for: ['general-tasks', 'unknown-complexity']
    });
  }

  /**
   * Helper methods
   */
  
  private getAvailableAgents(): Agent[] {
    return Array.from(this.agents.values()).filter(
      agent => agent.status === 'idle' || agent.status === 'active'
    );
  }

  private async analyzeTask(task: Task): Promise<any> {
    // Use MCP tools to analyze task complexity and requirements
    return this.mcpWrapper.analyzePattern({
      action: 'analyze',
      operation: 'task_analysis',
      metadata: {
        description: task.description,
        priority: task.priority,
        dependencies: task.dependencies
      }
    });
  }

  private async analyzeAgentCapabilities(agent: Agent): Promise<void> {
    // Analyze and store agent capability patterns
    await this.mcpWrapper.storeMemory({
      action: 'store',
      key: `agent-capabilities/${agent.id}`,
      value: JSON.stringify({
        type: agent.type,
        capabilities: agent.capabilities,
        registeredAt: new Date()
      }),
      namespace: 'agent-registry'
    });
  }

  private async broadcastAgentRegistration(agent: Agent): Promise<void> {
    // In distributed mode, notify other Queens/coordinators
    await this.db.createCommunication({
      from_agent_id: this.id,
      to_agent_id: null, // broadcast
      swarm_id: this.config.swarmId,
      message_type: 'broadcast',
      content: JSON.stringify({
        type: 'agent_registered',
        agent: {
          id: agent.id,
          type: agent.type,
          capabilities: agent.capabilities
        }
      }),
      priority: 'high'
    });
  }

  private async broadcastConsensusRequest(proposal: ConsensusProposal): Promise<void> {
    await this.db.createCommunication({
      from_agent_id: this.id,
      to_agent_id: null, // broadcast
      swarm_id: this.config.swarmId,
      message_type: 'consensus',
      content: JSON.stringify(proposal),
      priority: 'urgent',
      requires_response: true
    });
  }

  private determineAgentRole(agent: Agent, task: Task): string {
    // Determine specific role based on agent type and task
    const roleMap: Record<AgentType, string> = {
      coordinator: 'lead',
      researcher: 'investigator',
      coder: 'implementer',
      analyst: 'evaluator',
      architect: 'designer',
      tester: 'validator',
      reviewer: 'auditor',
      optimizer: 'enhancer',
      documenter: 'recorder',
      monitor: 'observer',
      specialist: 'expert'
    };
    
    return roleMap[agent.type] || 'contributor';
  }

  private getAgentResponsibilities(agent: Agent, task: Task): string[] {
    // Define specific responsibilities based on role
    const responsibilityMap: Record<AgentType, string[]> = {
      coordinator: ['coordinate team', 'track progress', 'resolve conflicts'],
      researcher: ['gather information', 'identify patterns', 'provide insights'],
      coder: ['implement solution', 'write tests', 'debug issues'],
      analyst: ['analyze data', 'identify bottlenecks', 'suggest improvements'],
      architect: ['design system', 'define interfaces', 'ensure scalability'],
      tester: ['write tests', 'find bugs', 'validate functionality'],
      reviewer: ['review code', 'ensure quality', 'suggest improvements'],
      optimizer: ['improve performance', 'reduce complexity', 'optimize resources'],
      documenter: ['create documentation', 'update guides', 'maintain clarity'],
      monitor: ['track metrics', 'alert on issues', 'ensure health'],
      specialist: ['provide expertise', 'solve complex problems', 'guide implementation']
    };
    
    return responsibilityMap[agent.type] || ['contribute to task'];
  }

  private createCheckpoints(task: Task, strategy: CoordinationStrategy): any[] {
    return strategy.coordinationPoints.map((point, index) => ({
      name: point,
      expectedProgress: Math.round((index + 1) / strategy.coordinationPoints.length * 100),
      actions: ['status_check', 'sync_progress', 'adjust_strategy']
    }));
  }

  private createFallbackPlan(task: Task, agents: Agent[]): any {
    return {
      triggers: ['agent_failure', 'deadline_approaching', 'consensus_failure'],
      actions: [
        'reassign_to_available_agents',
        'escalate_to_queen',
        'activate_backup_agents',
        'simplify_task_requirements'
      ],
      escalation_path: ['team_lead', 'queen', 'human_operator']
    };
  }

  private async monitorAgentHealth(): Promise<void> {
    for (const agent of this.agents.values()) {
      if (agent.status === 'error' || !agent.isResponsive()) {
        await this.handleAgentFailure(agent);
      }
    }
  }

  private async checkTaskProgress(): Promise<void> {
    const activeTasks = await this.db.getActiveTasks(this.config.swarmId);
    
    for (const task of activeTasks) {
      if (this.isTaskStalled(task)) {
        await this.handleStalledTask(task);
      }
    }
  }

  private async checkRebalancing(): Promise<void> {
    const stats = await this.db.getSwarmStats(this.config.swarmId);
    
    if (stats.agentUtilization > 0.9 || stats.taskBacklog > stats.agentCount * 2) {
      this.emit('rebalanceNeeded', stats);
    }
  }

  private async analyzePerformancePatterns(): Promise<void> {
    const patterns = await this.mcpWrapper.analyzePattern({
      action: 'analyze',
      operation: 'performance_patterns',
      metadata: {
        swarmId: this.config.swarmId,
        timeframe: '1h'
      }
    });
    
    if (patterns.recommendations) {
      await this.applyPerformanceRecommendations(patterns.recommendations);
    }
  }

  private async optimizeStrategies(): Promise<void> {
    // Analyze strategy effectiveness and adjust
    const strategyPerformance = await this.db.getStrategyPerformance(this.config.swarmId);
    
    for (const [strategyName, performance] of Object.entries(strategyPerformance)) {
      if (performance.successRate < 0.7) {
        await this.adjustStrategy(strategyName, performance);
      }
    }
  }

  private async trainNeuralPatterns(): Promise<void> {
    // Train neural network on successful patterns
    const successfulDecisions = await this.db.getSuccessfulDecisions(this.config.swarmId);
    
    if (successfulDecisions.length > 10) {
      await this.mcpWrapper.trainNeural({
        pattern_type: 'coordination',
        training_data: JSON.stringify(successfulDecisions),
        epochs: 50
      });
    }
  }

  private async handleAgentFailure(agent: Agent): Promise<void> {
    // Reassign agent's tasks
    if (agent.currentTask) {
      await this.reassignTask(agent.currentTask, agent.id);
    }
    
    // Mark agent as offline
    await this.db.updateAgentStatus(agent.id, 'offline');
    
    this.emit('agentFailed', { agent });
  }

  private async handleStalledTask(task: any): Promise<void> {
    // Implement stalled task recovery
    this.emit('taskStalled', { task });
  }

  private isTaskStalled(task: any): boolean {
    // Check if task hasn't progressed in reasonable time
    const stalledThreshold = 10 * 60 * 1000; // 10 minutes
    return task.last_progress_update && 
           Date.now() - new Date(task.last_progress_update).getTime() > stalledThreshold;
  }

  private async reassignTask(taskId: string, fromAgentId: string): Promise<void> {
    const availableAgents = this.getAvailableAgents().filter(a => a.id !== fromAgentId);
    
    if (availableAgents.length > 0) {
      const newAgent = availableAgents[0]; // Simple selection, could be more sophisticated
      await this.db.reassignTask(taskId, newAgent.id);
      await newAgent.assignTask(taskId, {});
    }
  }

  private async applyPerformanceRecommendations(recommendations: any[]): Promise<void> {
    // Apply recommended optimizations
    for (const rec of recommendations) {
      this.emit('performanceRecommendation', rec);
    }
  }

  private async adjustStrategy(strategyName: string, performance: any): Promise<void> {
    const strategy = this.strategies.get(strategyName);
    if (strategy) {
      // Adjust strategy parameters based on performance
      if (performance.avgCompletionTime > performance.targetTime) {
        strategy.maxAgents = Math.min(strategy.maxAgents + 1, 10);
      }
      
      this.emit('strategyAdjusted', { strategyName, performance });
    }
  }

  /**
   * Shutdown the Queen
   */
  async shutdown(): Promise<void> {
    this.isActive = false;
    this.emit('shutdown');
  }
}