import { 
  Activity, 
  ActivityConfig,
  ProcessExecution,
  ConditionalBranch
} from './types.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
// import { AgentOrchestrator } from '../agent-orchestration/orchestrator.js';
// import { Agent } from '../agent-orchestration/types.js';

export class ActivityExecutor {
  private processEngine: any; // Avoid circular dependency typing
  private server?: Server;
  // private agentOrchestrator: AgentOrchestrator;

  constructor(processEngine: any, agentOrchestrator?: any) {
    this.processEngine = processEngine;
    // this.agentOrchestrator = agentOrchestrator;
  }

  setServer(server: Server): void {
    this.server = server;
  }

  async execute(
    activity: Activity,
    inputs: Record<string, any>,
    execution: ProcessExecution
  ): Promise<Record<string, any>> {
    switch (activity.type) {
      case 'tool':
        return this.executeToolActivity(activity, inputs);
      
      case 'human':
        return this.executeHumanActivity(activity, inputs, execution);
      
      case 'agent':
        return this.executeAgentActivity(activity, inputs);
      
      case 'conditional':
        return this.executeConditionalActivity(activity, inputs, execution);
      
      case 'loop':
        return this.executeLoopActivity(activity, inputs, execution);
      
      case 'parallel':
        return this.executeParallelActivity(activity, inputs, execution);
      
      case 'external':
        return this.executeExternalActivity(activity, inputs);
      
      default:
        throw new Error(`Unknown activity type: ${activity.type}`);
    }
  }

  private async executeToolActivity(
    activity: Activity,
    inputs: Record<string, any>
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.toolName) {
      throw new Error('Tool activity missing toolName');
    }

    if (!this.server) {
      throw new Error('Server not initialized for tool execution');
    }

    // In a real implementation, this would call the actual MCP tool
    // For now, we'll simulate the response
    console.log(`Executing tool: ${config.toolName} with args:`, config.toolArgs);
    
    // Simulate tool execution
    return {
      success: true,
      toolName: config.toolName,
      executedAt: new Date().toISOString(),
      // Tool-specific outputs would go here
    };
  }

  private async executeHumanActivity(
    activity: Activity,
    inputs: Record<string, any>,
    execution: ProcessExecution
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    // Change execution status to waiting
    execution.status = 'waiting';
    this.processEngine.emit('execution:waiting', { execution, activity });
    
    // In a real implementation, this would:
    // 1. Create a task/notification for the assigned users
    // 2. Wait for user response
    // 3. Validate the response
    // 4. Return the user's input
    
    console.log(`Waiting for human input: ${activity.name}`);
    console.log(`Assigned to: ${config.assignTo?.join(', ')}`);
    console.log(`Prompt: ${config.prompt}`);
    
    // For now, simulate immediate response
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    execution.status = 'running';
    
    return {
      approved: true,
      approvedBy: config.assignTo?.[0] || 'system',
      approvedAt: new Date().toISOString(),
      comments: 'Simulated approval'
    };
  }

  private async executeAgentActivity(
    activity: Activity,
    inputs: Record<string, any>
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.agentType) {
      throw new Error('Agent activity missing agentType in config');
    }
    
    if (!config.agentTask) {
      throw new Error('Agent activity missing agentTask in config');
    }
    
    try {
      // 1. Create an orchestration session for this activity
      const sessionName = `Process Activity: ${activity.name || 'Agent Task'}`;
      // const session = this.agentOrchestrator.createSession(sessionName, config.agentTask);
      throw new Error('Agent activities are not currently supported');
      
      /* Agent code is commented out until agent-orchestration module is available
      // 2. Create agent with appropriate template
      const agentRole = this.mapAgentTypeToRole(config.agentType);
      const template = this.getAgentTemplate(agentRole);
      const agent = this.agentOrchestrator.createAgent(template, `${config.agentType}-${Date.now()}`);
      
      // 3. Create a task for the agent
      const capabilities = config.agentCapabilities || this.getDefaultCapabilities(agentRole);
      const task = this.agentOrchestrator.createTask(
        config.agentTask,
        capabilities,
        [] // no dependencies for now
      );
      
      // 4. Execute the task with the agent
      const taskResult = await this.agentOrchestrator.executeTask(task.id);
      
      // 5. Complete the session
      this.agentOrchestrator.completeSession();
      
      // 6. Return real agent results
      return {
        agentType: config.agentType,
        agentId: agent.id,
        taskId: task.id,
        taskCompleted: taskResult.success,
        results: taskResult.output || {},
        artifacts: taskResult.artifacts || [],
        duration: taskResult.performance?.duration || 0,
        executionSummary: `Agent ${agent.name} completed task: ${config.agentTask}`,
        sessionId: session.id
      };
      */
      
    } catch (error) {
      console.error(`Agent activity failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
      
      // Fallback to simulation if orchestration fails
      console.log(`Falling back to simulated execution for agent: ${config.agentType}`);
      console.log(`Task: ${config.agentTask}`);
      
      return {
        agentType: config.agentType,
        agentId: 'fallback-agent',
        taskId: 'fallback-task',
        taskCompleted: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        results: {
          analysis: 'Agent execution failed - simulated fallback',
          recommendations: ['Check agent orchestration configuration']
        },
        artifacts: [],
        duration: 0,
        executionSummary: `Fallback execution for ${config.agentType}: ${config.agentTask}`
      };
    }
  }
  
  private mapAgentTypeToRole(agentType: string): any /*Agent['role']*/ {
    const roleMap: Record<string, any /*Agent['role']*/> = {
      'researcher': 'researcher',
      'coder': 'coder',
      'reviewer': 'reviewer',
      'tester': 'tester',
      'documenter': 'documenter',
      'analyzer': 'researcher', // Analyzer maps to researcher role
      'developer': 'coder',
      'qa': 'tester',
      'coordinator': 'primary'
    };
    
    return roleMap[agentType.toLowerCase()] || 'primary';
  }
  
  private getAgentTemplate(role: any /*Agent['role']*/) {
    const templates = {
      primary: {
        name: 'Primary Coordinator',
        role: 'primary' as const,
        systemPrompt: 'You are the primary coordinator. Manage the overall task and delegate to specialists.',
        capabilities: ['coordination', 'planning', 'delegation', 'summary'],
      },
      researcher: {
        name: 'Research Specialist',
        role: 'researcher' as const,
        systemPrompt: 'You are a research specialist. Find information and analyze requirements.',
        capabilities: ['research', 'analysis', 'documentation', 'search'],
      },
      coder: {
        name: 'Code Developer',
        role: 'coder' as const,
        systemPrompt: 'You are a code developer. Write clean, efficient, and tested code.',
        capabilities: ['coding', 'debugging', 'refactoring', 'optimization'],
      },
      reviewer: {
        name: 'Code Reviewer',
        role: 'reviewer' as const,
        systemPrompt: 'You are a code reviewer. Examine code for quality, security, and best practices.',
        capabilities: ['review', 'quality-assurance', 'security', 'best-practices'],
      },
      tester: {
        name: 'Quality Tester',
        role: 'tester' as const,
        systemPrompt: 'You are a quality tester. Design and execute tests to ensure software quality.',
        capabilities: ['testing', 'automation', 'quality-assurance', 'bug-detection'],
      },
      documenter: {
        name: 'Documentation Specialist',
        role: 'documenter' as const,
        systemPrompt: 'You are a documentation specialist. Create clear, comprehensive documentation.',
        capabilities: ['documentation', 'writing', 'knowledge-management', 'tutorials'],
      }
    };
    
    return templates[role];
  }
  
  private getDefaultCapabilities(role: any /*Agent['role']*/): string[] {
    const capabilityMap: Record<any /*Agent['role']*/, string[]> = {
      primary: ['coordination', 'planning', 'delegation'],
      researcher: ['research', 'analysis', 'documentation'],
      coder: ['coding', 'debugging', 'refactoring'],
      reviewer: ['review', 'quality-assurance', 'security'],
      tester: ['testing', 'automation', 'quality-assurance'],
      documenter: ['documentation', 'writing', 'knowledge-management'],
      custom: ['general', 'flexible', 'adaptable']
    };
    
    return capabilityMap[role] || ['general'];
  }

  private async executeConditionalActivity(
    activity: Activity,
    inputs: Record<string, any>,
    execution: ProcessExecution
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.conditions) {
      throw new Error('Conditional activity missing conditions');
    }

    // Evaluate each condition
    for (const branch of config.conditions) {
      if (await this.evaluateCondition(branch.condition, execution.variables)) {
        // Execute activities in this branch
        const results: Record<string, any> = {};
        
        for (const branchActivity of branch.activities) {
          const result = await this.execute(branchActivity, inputs, execution);
          Object.assign(results, result);
        }
        
        return results;
      }
    }
    
    // Execute default branch if no conditions matched
    if (config.defaultBranch) {
      const results: Record<string, any> = {};
      
      for (const defaultActivity of config.defaultBranch) {
        const result = await this.execute(defaultActivity, inputs, execution);
        Object.assign(results, result);
      }
      
      return results;
    }
    
    return { conditionMatched: false };
  }

  private async executeLoopActivity(
    activity: Activity,
    inputs: Record<string, any>,
    execution: ProcessExecution
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.collection || !config.activities) {
      throw new Error('Loop activity missing collection or activities');
    }

    // Get collection from variables
    const collection = execution.variables[config.collection] || [];
    const itemVariable = config.itemVariable || 'item';
    const maxIterations = config.maxIterations || 1000;
    
    const results: any[] = [];
    let iteration = 0;
    
    for (const item of collection) {
      if (iteration >= maxIterations) {
        console.warn(`Loop reached max iterations (${maxIterations})`);
        break;
      }
      
      // Set loop variable
      execution.variables[itemVariable] = item;
      execution.variables[`${itemVariable}Index`] = iteration;
      
      // Execute loop activities
      const iterationResults: Record<string, any> = {};
      
      for (const loopActivity of config.activities) {
        const result = await this.execute(loopActivity, inputs, execution);
        Object.assign(iterationResults, result);
      }
      
      results.push(iterationResults);
      iteration++;
    }
    
    // Clean up loop variables
    delete execution.variables[itemVariable];
    delete execution.variables[`${itemVariable}Index`];
    
    return {
      loopCompleted: true,
      iterations: iteration,
      results
    };
  }

  private async executeParallelActivity(
    activity: Activity,
    inputs: Record<string, any>,
    execution: ProcessExecution
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.branches) {
      throw new Error('Parallel activity missing branches');
    }

    const waitForAll = config.waitForAll !== false; // Default true
    
    // Execute all branches in parallel
    const branchPromises = config.branches.map(async (branch, index) => {
      const branchResults: Record<string, any> = {};
      
      try {
        for (const branchActivity of branch) {
          const result = await this.execute(branchActivity, inputs, execution);
          Object.assign(branchResults, result);
        }
        
        return { success: true, results: branchResults, branchIndex: index };
      } catch (error) {
        return { 
          success: false, 
          error: error instanceof Error ? error.message : String(error),
          branchIndex: index 
        };
      }
    });
    
    if (waitForAll) {
      // Wait for all branches to complete
      const branchResults = await Promise.all(branchPromises);
      return {
        parallelCompleted: true,
        branches: branchResults
      };
    } else {
      // Return first completed branch
      const firstResult = await Promise.race(branchPromises);
      return {
        parallelCompleted: true,
        firstCompleted: firstResult
      };
    }
  }

  private async executeExternalActivity(
    activity: Activity,
    inputs: Record<string, any>
  ): Promise<Record<string, any>> {
    const config = activity.config as ActivityConfig;
    
    if (!config.url) {
      throw new Error('External activity missing URL');
    }

    // In a real implementation, this would make an HTTP request
    console.log(`Making external request to: ${config.url}`);
    console.log(`Method: ${config.method || 'GET'}`);
    
    // Simulate external call
    await new Promise(resolve => setTimeout(resolve, 1500));
    
    return {
      status: 200,
      response: {
        message: 'External call completed',
        data: { example: 'response data' }
      }
    };
  }

  private async evaluateCondition(
    condition: string,
    variables: Record<string, any>
  ): Promise<boolean> {
    // In a real implementation, use a proper expression evaluator
    // For now, simple string matching
    console.log(`Evaluating condition: ${condition}`);
    
    // Handle simple equality checks
    const match = condition.match(/(\w+)\s*===?\s*(.+)/);
    if (match) {
      const [, varName, value] = match;
      const varValue = variables[varName];
      const expectedValue = value.replace(/['"]/g, '').trim();
      
      return String(varValue) === expectedValue;
    }
    
    // Default to true for demo
    return true;
  }
}