import { EventEmitter } from 'events';
import { Logger } from '../utils/logger.js';
import { OpenAIIntegration, CompletionResult, ChatMessage } from '../ai/openai-integration.js';
import { CostTracker } from '../utils/cost-tracker.js';
import { ConversationManager } from '../conversations/conversation-manager.js';
import { BzzzP2PConnector } from '../p2p/bzzz-connector.js';

export interface Agent {
  id: string;
  role: string;
  capabilities: string[];
  specialization: string;
  maxTasks: number;
  currentTasks: AgentTask[];
  status: 'idle' | 'busy' | 'offline' | 'error';
  performance: number;
  available: boolean;
  systemPrompt: string;
  createdAt: string;
  lastActivity: string;
  metadata: Record<string, any>;
}

export interface AgentTask {
  id: string;
  type: string;
  description: string;
  status: 'pending' | 'in_progress' | 'completed' | 'failed';
  startTime: string;
  endTime?: string;
  result?: any;
  error?: string;
}

export interface CreateAgentOptions {
  id: string;
  role: string;
  capabilities?: string[];
  specialization?: string;
  maxTasks?: number;
  systemPrompt?: string;
}

export interface AgentManagerConfig {
  openaiIntegration: OpenAIIntegration;
  costTracker: CostTracker;
  conversationManager: ConversationManager;
  p2pConnector: BzzzP2PConnector;
}

export class AgentManager extends EventEmitter {
  private logger: Logger;
  private openaiIntegration: OpenAIIntegration;
  private costTracker: CostTracker;
  private conversationManager: ConversationManager;
  private p2pConnector: BzzzP2PConnector;
  private agents: Map<string, Agent> = new Map();
  private agentTasks: Map<string, AgentTask[]> = new Map();
  private taskHistory: Map<string, AgentTask[]> = new Map();
  private performanceMetrics: Map<string, AgentPerformanceMetrics> = new Map();

  constructor(config: AgentManagerConfig) {
    super();
    this.logger = new Logger('AgentManager');
    this.openaiIntegration = config.openaiIntegration;
    this.costTracker = config.costTracker;
    this.conversationManager = config.conversationManager;
    this.p2pConnector = config.p2pConnector;

    this.logger.info('Agent Manager initialized');
  }

  public async start(): Promise<void> {
    // Setup event listeners
    this.p2pConnector.on('message', (message) => {
      this.handleP2PMessage(message);
    });

    this.logger.info('Agent Manager started');
  }

  public async stop(): Promise<void> {
    // Stop all agents
    for (const agent of this.agents.values()) {
      await this.stopAgent(agent.id);
    }

    this.logger.info('Agent Manager stopped');
  }

  public async createAgent(options: CreateAgentOptions): Promise<Agent> {
    const { id, role, capabilities = [], specialization, maxTasks = 3, systemPrompt } = options;

    if (this.agents.has(id)) {
      throw new Error(`Agent with ID ${id} already exists`);
    }

    // Generate or use provided system prompt
    let agentSystemPrompt = systemPrompt;
    if (!agentSystemPrompt) {
      agentSystemPrompt = await this.generateSystemPrompt(role, specialization || role, capabilities);
    }

    const agent: Agent = {
      id,
      role,
      capabilities,
      specialization: specialization || role,
      maxTasks,
      currentTasks: [],
      status: 'idle',
      performance: 1.0,
      available: true,
      systemPrompt: agentSystemPrompt,
      createdAt: new Date().toISOString(),
      lastActivity: new Date().toISOString(),
      metadata: {},
    };

    this.agents.set(id, agent);
    this.agentTasks.set(id, []);
    this.taskHistory.set(id, []);
    this.performanceMetrics.set(id, {
      totalTasks: 0,
      successfulTasks: 0,
      failedTasks: 0,
      averageResponseTime: 0,
      totalResponseTime: 0,
      lastUpdated: new Date().toISOString(),
    });

    this.logger.info('Agent created successfully', {
      agentId: id,
      role,
      capabilities,
      specialization: agent.specialization,
    });

    this.emit('agent_created', agent);
    return agent;
  }

  public async stopAgent(agentId: string): Promise<void> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      throw new Error(`Agent ${agentId} not found`);
    }

    // Cancel all current tasks
    const tasks = this.agentTasks.get(agentId) || [];
    for (const task of tasks) {
      if (task.status === 'in_progress' || task.status === 'pending') {
        task.status = 'failed';
        task.error = 'Agent stopped';
        task.endTime = new Date().toISOString();
      }
    }

    agent.status = 'offline';
    agent.available = false;
    agent.lastActivity = new Date().toISOString();

    this.logger.info('Agent stopped', { agentId });
    this.emit('agent_stopped', agent);
  }

  public async deleteAgent(agentId: string): Promise<void> {
    await this.stopAgent(agentId);
    
    this.agents.delete(agentId);
    this.agentTasks.delete(agentId);
    this.taskHistory.delete(agentId);
    this.performanceMetrics.delete(agentId);

    this.logger.info('Agent deleted', { agentId });
    this.emit('agent_deleted', agentId);
  }

  public getAgent(agentId: string): Agent | undefined {
    return this.agents.get(agentId);
  }

  public getAgents(): Agent[] {
    return Array.from(this.agents.values());
  }

  public getAvailableAgents(): Agent[] {
    return this.getAgents().filter(agent => 
      agent.available && 
      agent.status === 'idle' && 
      agent.currentTasks.length < agent.maxTasks
    );
  }

  public getAgentsByRole(role: string): Agent[] {
    return this.getAgents().filter(agent => agent.role === role);
  }

  public getAgentsByCapability(capability: string): Agent[] {
    return this.getAgents().filter(agent => 
      agent.capabilities.includes(capability)
    );
  }

  public async assignTask(agentId: string, taskType: string, taskDescription: string, taskData?: any): Promise<AgentTask> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      throw new Error(`Agent ${agentId} not found`);
    }

    if (!agent.available || agent.status === 'offline') {
      throw new Error(`Agent ${agentId} is not available`);
    }

    if (agent.currentTasks.length >= agent.maxTasks) {
      throw new Error(`Agent ${agentId} is at maximum task capacity`);
    }

    const task: AgentTask = {
      id: this.generateTaskId(),
      type: taskType,
      description: taskDescription,
      status: 'pending',
      startTime: new Date().toISOString(),
    };

    agent.currentTasks.push(task);
    const tasks = this.agentTasks.get(agentId) || [];
    tasks.push(task);
    this.agentTasks.set(agentId, tasks);

    agent.status = 'busy';
    agent.lastActivity = new Date().toISOString();

    this.logger.info('Task assigned to agent', {
      agentId,
      taskId: task.id,
      taskType,
    });

    // Execute the task asynchronously
    this.executeTask(agentId, task, taskData);

    this.emit('task_assigned', { agent, task });
    return task;
  }

  public async getRecentActivity(agentId: string, maxHistory: number = 10): Promise<AgentTask[]> {
    const history = this.taskHistory.get(agentId) || [];
    return history.slice(-maxHistory).reverse();
  }

  public async getCurrentTasks(agentId: string): Promise<AgentTask[]> {
    const agent = this.agents.get(agentId);
    return agent?.currentTasks || [];
  }

  public async getAgentMetadata(agentId: string): Promise<any> {
    const agent = this.agents.get(agentId);
    const performance = this.performanceMetrics.get(agentId);
    
    if (!agent) {
      throw new Error(`Agent ${agentId} not found`);
    }

    return {
      agent: { ...agent },
      performance,
      taskCount: {
        current: agent.currentTasks.length,
        total: performance?.totalTasks || 0,
        successful: performance?.successfulTasks || 0,
        failed: performance?.failedTasks || 0,
      },
    };
  }

  public async getAgentData(agentId: string, path: string): Promise<any> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      throw new Error(`Agent ${agentId} not found`);
    }

    // Path-based data retrieval (could be extended to support different data types)
    switch (path) {
      case 'tasks':
        return await this.getCurrentTasks(agentId);
      case 'history':
        return await this.getRecentActivity(agentId);
      case 'performance':
        return this.performanceMetrics.get(agentId);
      case 'metadata':
        return agent.metadata;
      default:
        return agent.metadata[path];
    }
  }

  private async executeTask(agentId: string, task: AgentTask, taskData?: any): Promise<void> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      this.completeTask(agentId, task.id, 'failed', undefined, 'Agent not found');
      return;
    }

    task.status = 'in_progress';
    
    try {
      this.logger.debug('Executing task', {
        agentId,
        taskId: task.id,
        taskType: task.type,
      });

      let result: any;
      
      switch (task.type) {
        case 'chat_completion':
          result = await this.executeChatCompletionTask(agent, task, taskData);
          break;
        case 'code_review':
          result = await this.executeCodeReviewTask(agent, task, taskData);
          break;
        case 'documentation':
          result = await this.executeDocumentationTask(agent, task, taskData);
          break;
        case 'architecture_analysis':
          result = await this.executeArchitectureAnalysisTask(agent, task, taskData);
          break;
        default:
          result = await this.executeGenericTask(agent, task, taskData);
      }

      this.completeTask(agentId, task.id, 'completed', result);
      
    } catch (error) {
      this.logger.error('Task execution failed', {
        agentId,
        taskId: task.id,
        error: error instanceof Error ? error.message : String(error),
      });
      
      this.completeTask(agentId, task.id, 'failed', undefined, 
        error instanceof Error ? error.message : String(error));
    }
  }

  private async executeChatCompletionTask(agent: Agent, task: AgentTask, taskData: any): Promise<CompletionResult> {
    const messages: ChatMessage[] = taskData.messages || [
      { role: 'user', content: task.description }
    ];

    return await this.openaiIntegration.createChatCompletion(messages, {
      systemPrompt: agent.systemPrompt,
      model: taskData.model,
      temperature: taskData.temperature,
      maxTokens: taskData.maxTokens,
    });
  }

  private async executeCodeReviewTask(agent: Agent, task: AgentTask, taskData: any): Promise<any> {
    const { code, language, context } = taskData;
    
    const prompt = `Review the following ${language} code for:
- Code correctness and logic
- Security vulnerabilities
- Performance implications
- Best practices adherence
- Test coverage recommendations

Code to review:
\`\`\`${language}
${code}
\`\`\`

Context: ${context || 'No additional context provided.'}

Provide detailed, actionable feedback with specific suggestions for improvement.`;

    const result = await this.openaiIntegration.createCompletion(prompt, {
      systemPrompt: agent.systemPrompt,
      temperature: 0.3, // Lower temperature for consistent reviews
    });

    return {
      review: result.content,
      language,
      codeLength: code.length,
      reviewTime: new Date().toISOString(),
      tokens: result.usage,
    };
  }

  private async executeDocumentationTask(agent: Agent, task: AgentTask, taskData: any): Promise<any> {
    const { content, documentType, audience } = taskData;
    
    const prompt = `Create ${documentType} documentation for the following content.
Target audience: ${audience}

Content to document:
${content}

Requirements:
- Clear and concise language
- Proper structure and organization
- Include examples where appropriate
- Consider the target audience's technical level
- Follow standard documentation practices`;

    const result = await this.openaiIntegration.createCompletion(prompt, {
      systemPrompt: agent.systemPrompt,
      temperature: 0.4,
    });

    return {
      documentation: result.content,
      documentType,
      audience,
      generatedAt: new Date().toISOString(),
      tokens: result.usage,
    };
  }

  private async executeArchitectureAnalysisTask(agent: Agent, task: AgentTask, taskData: any): Promise<any> {
    const { systemDescription, requirements, constraints } = taskData;
    
    const prompt = `Analyze the following system architecture:

System Description: ${systemDescription}
Requirements: ${requirements}
Constraints: ${constraints}

Provide analysis on:
1. Architectural strengths and weaknesses
2. Scalability considerations
3. Security implications
4. Performance bottlenecks
5. Recommendations for improvement
6. Technology stack evaluation`;

    const result = await this.openaiIntegration.createCompletion(prompt, {
      systemPrompt: agent.systemPrompt,
      temperature: 0.5,
      maxTokens: 2000,
    });

    return {
      analysis: result.content,
      systemDescription,
      analyzedAt: new Date().toISOString(),
      tokens: result.usage,
    };
  }

  private async executeGenericTask(agent: Agent, task: AgentTask, taskData: any): Promise<any> {
    const prompt = taskData.prompt || task.description;
    
    const result = await this.openaiIntegration.createCompletion(prompt, {
      systemPrompt: agent.systemPrompt,
      temperature: taskData.temperature || 0.7,
      maxTokens: taskData.maxTokens,
    });

    return {
      response: result.content,
      taskType: task.type,
      completedAt: new Date().toISOString(),
      tokens: result.usage,
    };
  }

  private completeTask(agentId: string, taskId: string, status: 'completed' | 'failed', result?: any, error?: string): void {
    const agent = this.agents.get(agentId);
    if (!agent) return;

    const taskIndex = agent.currentTasks.findIndex(t => t.id === taskId);
    if (taskIndex === -1) return;

    const task = agent.currentTasks[taskIndex];
    task.status = status;
    task.endTime = new Date().toISOString();
    task.result = result;
    task.error = error;

    // Move to history
    agent.currentTasks.splice(taskIndex, 1);
    const history = this.taskHistory.get(agentId) || [];
    history.push(task);
    this.taskHistory.set(agentId, history);

    // Update performance metrics
    this.updatePerformanceMetrics(agentId, task);

    // Update agent status
    if (agent.currentTasks.length === 0) {
      agent.status = 'idle';
    }
    agent.lastActivity = new Date().toISOString();

    this.logger.info('Task completed', {
      agentId,
      taskId,
      status,
      duration: this.calculateTaskDuration(task),
    });

    this.emit('task_completed', { agent, task });
  }

  private async generateSystemPrompt(role: string, specialization: string, capabilities: string[]): Promise<string> {
    return await this.openaiIntegration.generateSystemPrompt(role, specialization, capabilities);
  }

  private updatePerformanceMetrics(agentId: string, task: AgentTask): void {
    const metrics = this.performanceMetrics.get(agentId);
    if (!metrics) return;

    metrics.totalTasks++;
    if (task.status === 'completed') {
      metrics.successfulTasks++;
    } else {
      metrics.failedTasks++;
    }

    const duration = this.calculateTaskDuration(task);
    metrics.totalResponseTime += duration;
    metrics.averageResponseTime = metrics.totalResponseTime / metrics.totalTasks;
    metrics.lastUpdated = new Date().toISOString();

    // Update agent performance score
    const agent = this.agents.get(agentId);
    if (agent) {
      agent.performance = metrics.successfulTasks / metrics.totalTasks;
    }
  }

  private calculateTaskDuration(task: AgentTask): number {
    if (!task.endTime) return 0;
    return new Date(task.endTime).getTime() - new Date(task.startTime).getTime();
  }

  private handleP2PMessage(message: any): void {
    // Handle messages from the P2P network that might be relevant to agents
    if (message.type === 'task_request') {
      this.handleTaskRequest(message);
    } else if (message.type === 'agent_query') {
      this.handleAgentQuery(message);
    }
  }

  private async handleTaskRequest(message: any): Promise<void> {
    // Implementation for handling task requests from the network
    this.logger.debug('Task request received from P2P network', { message });
  }

  private async handleAgentQuery(message: any): Promise<void> {
    // Implementation for handling agent queries from the network  
    this.logger.debug('Agent query received from P2P network', { message });
  }

  private generateTaskId(): string {
    return `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}

interface AgentPerformanceMetrics {
  totalTasks: number;
  successfulTasks: number;
  failedTasks: number;
  averageResponseTime: number;
  totalResponseTime: number;
  lastUpdated: string;
}