// @lifecycle canonical - Analytics service for metrics emission.
/**
 * Metrics Collector - Centralized Performance and Usage Metrics Collection
 *
 * Provides comprehensive metrics collection and reporting for all MCP tools
 * without coupling to execution logic. Uses event-driven architecture to
 * observe tool operations and provide detailed insights.
 */

import { EventEmitter } from 'events';

import {
  type MetricsCollector,
  type ExecutionData,
  type GateValidationData,
  type GateUsageMetric,
  type FrameworkSwitchData,
  type ExecutionStats,
  type SystemMetrics,
  type FrameworkUsage,
  type AnalyticsQueryOptions,
  type AnalyticsSummary,
  type PerformanceTrend,
  type PipelineStageMetric,
  type CommandExecutionMetric,
  type CommandExecutionMode,
} from './types.js';
import { Logger } from '../../logging/index.js';

/**
 * Centralized Metrics Collector
 */
export class InMemoryMetricsCollector extends EventEmitter implements MetricsCollector {
  private logger: Logger;
  private startTime: number;

  // Analytics data storage
  private executionStats: ExecutionStats;
  private systemMetrics: SystemMetrics;
  private frameworkUsage: FrameworkUsage;
  private gateValidationStats = {
    totalValidations: 0,
    successfulValidations: 0,
    totalValidationTime: 0,
    validationHistory: [] as GateValidationData[],
  };
  private gateUsageHistory: GateUsageMetric[] = [];

  // Raw data storage for queries
  private executionHistory: ExecutionData[] = [];
  private frameworkSwitchHistory: FrameworkSwitchData[] = [];
  private performanceTrends: PerformanceTrend[] = [];
  private pipelineStageHistory: PipelineStageMetric[] = [];
  private commandExecutionHistory: CommandExecutionMetric[] = [];

  // Performance monitoring
  private memoryCheckInterval?: NodeJS.Timeout;
  private readonly MAX_HISTORY_SIZE = 1000;

  constructor(logger: Logger) {
    super();
    this.logger = logger;
    this.startTime = Date.now();

    // Initialize analytics data
    this.executionStats = {
      totalExecutions: 0,
      successfulExecutions: 0,
      failedExecutions: 0,
      averageExecutionTime: 0,
      executionsByMode: {
        single: 0,
        chain: 0,
      },
      executionsByTool: {
        prompt_engine: 0,
        resource_manager: 0,
        system_control: 0,
      },
      lastUpdated: Date.now(),
    };

    this.systemMetrics = {
      uptime: 0,
      memoryUsage: {
        heapUsed: 0,
        heapTotal: 0,
        external: 0,
        rss: 0,
      },
      averageResponseTime: 0,
      requestsPerMinute: 0,
      errorRate: 0,
      performanceTrends: [],
    };

    this.frameworkUsage = {
      currentFramework: 'CAGEERF', // Default framework
      frameworkSwitches: 0,
      frameworkUsageTime: {},
      frameworkSwitchHistory: [],
      frameworkPerformance: {},
    };

    this.setupEventListeners();
    this.startPerformanceMonitoring();

    this.logger.info('AnalyticsService initialized with event-driven collection');
  }

  /**
   * Set up event listeners for analytics collection
   */
  private setupEventListeners(): void {
    this.on('execution:complete', this.handleExecutionComplete.bind(this));
    this.on('execution:error', this.handleExecutionError.bind(this));
    this.on('gate:validation', this.handleGateValidation.bind(this));
    this.on('framework:switch', this.handleFrameworkSwitch.bind(this));
    this.on('system:performance', this.handlePerformanceTrend.bind(this));
  }

  /**
   * Start performance monitoring
   */
  private startPerformanceMonitoring(): void {
    // Monitor memory usage every 30 seconds
    this.memoryCheckInterval = setInterval(() => {
      this.recordMemoryUsage();
    }, 30000);
  }

  /**
   * Record execution completion
   */
  recordExecution(executionData: ExecutionData): void {
    this.emit('execution:complete', executionData);
  }

  /**
   * Record execution error
   */
  recordExecutionError(executionData: ExecutionData): void {
    this.emit('execution:error', executionData);
  }

  /**
   * Track execution (adapter for ResponseFormatter AnalyticsService interface)
   */
  trackExecution(data: {
    executionId: string;
    executionType: string;
    duration: number;
    frameworkUsed?: string;
    stepsExecuted?: number;
    success: boolean;
    sessionId?: string;
  }): void {
    const executionData: ExecutionData = {
      executionId: data.executionId,
      executionType: data.executionType as 'single' | 'chain',
      startTime: Date.now() - data.duration,
      endTime: Date.now(),
      executionTime: data.duration,
      success: data.success,
      frameworkEnabled: !!data.frameworkUsed,
      toolName: 'prompt_engine',
    };
    if (data.frameworkUsed !== undefined) {
      executionData.frameworkUsed = data.frameworkUsed;
    }
    if (data.stepsExecuted !== undefined) {
      executionData.stepsExecuted = data.stepsExecuted;
    }
    if (data.sessionId !== undefined) {
      executionData.sessionId = data.sessionId;
    }
    this.recordExecution(executionData);
  }

  /**
   * Track error (adapter for ResponseFormatter AnalyticsService interface)
   */
  trackError(data: {
    executionId: string;
    executionType: string;
    errorType: string;
    errorMessage: string;
    sessionId?: string;
  }): void {
    const executionData: ExecutionData = {
      executionId: data.executionId,
      executionType: data.executionType as 'single' | 'chain',
      startTime: Date.now(),
      endTime: Date.now(),
      executionTime: 0,
      success: false,
      frameworkEnabled: false,
      toolName: 'prompt_engine',
      error: `${data.errorType}: ${data.errorMessage}`,
    };
    if (data.sessionId !== undefined) {
      executionData.sessionId = data.sessionId;
    }
    this.recordExecutionError(executionData);
  }

  /**
   * Record gate validation
   */
  recordGateValidation(gateData: GateValidationData): void {
    this.emit('gate:validation', gateData);
  }

  /**
   * Record gate usage metrics emitted from enhancement stage.
   */
  recordGateUsage(metric: GateUsageMetric): void {
    const entry = {
      ...metric,
      timestamp: metric.timestamp ?? Date.now(),
    };
    this.gateUsageHistory.push(entry);
    this.trimHistory(this.gateUsageHistory);
    this.logger.debug('[Analytics] Gate usage recorded', {
      gateId: entry.gateId,
      type: entry.gateType,
      validation: entry.validationResult,
    });
  }

  /**
   * Record framework switch
   */
  recordFrameworkSwitch(switchData: FrameworkSwitchData): void {
    this.emit('framework:switch', switchData);
  }

  /**
   * Record an individual pipeline stage execution.
   */
  recordPipelineStage(metric: PipelineStageMetric): void {
    this.pipelineStageHistory.push(metric);
    this.trimHistory(this.pipelineStageHistory);
    this.logger.debug('[Analytics] Pipeline stage recorded', {
      stage: metric.stageName,
      status: metric.status,
      durationMs: metric.durationMs,
    });
  }

  /**
   * Record command-level execution metrics emitted by the pipeline orchestrator.
   */
  recordCommandExecutionMetric(metric: CommandExecutionMetric): void {
    this.commandExecutionHistory.push(metric);
    this.trimHistory(this.commandExecutionHistory);

    const executionData = this.createExecutionDataFromCommand(metric);
    this.recordExecution(executionData);
  }

  /**
   * Handle execution completion event
   */
  private handleExecutionComplete(executionData: ExecutionData): void {
    // Update execution statistics
    this.executionStats.totalExecutions++;

    if (executionData.success) {
      this.executionStats.successfulExecutions++;
    } else {
      this.executionStats.failedExecutions++;
    }

    // Update average execution time
    const totalTime =
      this.executionStats.averageExecutionTime * (this.executionStats.totalExecutions - 1);
    this.executionStats.averageExecutionTime =
      (totalTime + executionData.executionTime) / this.executionStats.totalExecutions;

    // Update execution by mode
    if (this.executionStats.executionsByMode[executionData.executionType] !== undefined) {
      this.executionStats.executionsByMode[executionData.executionType]++;
    }

    // Update execution by tool
    if (
      this.executionStats.executionsByTool[
        executionData.toolName as keyof typeof this.executionStats.executionsByTool
      ] !== undefined
    ) {
      this.executionStats.executionsByTool[
        executionData.toolName as keyof typeof this.executionStats.executionsByTool
      ]++;
    }

    // Update framework performance
    if (executionData.frameworkUsed) {
      if (!this.frameworkUsage.frameworkPerformance[executionData.frameworkUsed]) {
        this.frameworkUsage.frameworkPerformance[executionData.frameworkUsed] = {
          averageExecutionTime: 0,
          successRate: 0,
          usageCount: 0,
        };
      }

      const perf = this.frameworkUsage.frameworkPerformance[executionData.frameworkUsed];
      if (perf) {
        const totalTime = perf.averageExecutionTime * perf.usageCount;
        perf.usageCount++;
        perf.averageExecutionTime = (totalTime + executionData.executionTime) / perf.usageCount;

        // Update success rate
        const prevSuccesses = perf.successRate * (perf.usageCount - 1);
        const newSuccesses = prevSuccesses + (executionData.success ? 1 : 0);
        perf.successRate = newSuccesses / perf.usageCount;
      }
    }

    // Store execution history
    this.executionHistory.push(executionData);
    this.trimHistory(this.executionHistory);

    // Record performance trend
    this.recordPerformanceTrend(
      'execution_time',
      executionData.executionTime,
      executionData.toolName
    );

    this.executionStats.lastUpdated = Date.now();

    this.logger.debug(
      `Analytics updated: Total executions: ${this.executionStats.totalExecutions}, Success rate: ${this.getSuccessRate()}%`
    );
  }

  /**
   * Handle execution error event
   */
  private handleExecutionError(executionData: ExecutionData): void {
    // Error handling is included in handleExecutionComplete
    this.handleExecutionComplete(executionData);
  }

  /**
   * Handle gate validation event
   */
  private handleGateValidation(gateData: GateValidationData): void {
    this.gateValidationStats.totalValidations++;

    if (gateData.passedGates === gateData.totalGates) {
      this.gateValidationStats.successfulValidations++;
    }

    this.gateValidationStats.totalValidationTime += gateData.validationTime;
    this.gateValidationStats.validationHistory.push(gateData);
    this.trimHistory(this.gateValidationStats.validationHistory);

    this.logger.debug(
      `Gate validation recorded: ${gateData.passedGates}/${gateData.totalGates} passed`
    );
  }

  /**
   * Handle framework switch event
   */
  private handleFrameworkSwitch(switchData: FrameworkSwitchData): void {
    this.frameworkUsage.frameworkSwitches++;
    this.frameworkUsage.currentFramework = switchData.toFramework;

    const switchEntry: FrameworkUsage['frameworkSwitchHistory'][number] = {
      timestamp: switchData.switchTime,
      fromFramework: switchData.fromFramework,
      toFramework: switchData.toFramework,
    };
    if (switchData.reason !== undefined) {
      switchEntry.reason = switchData.reason;
    }

    this.frameworkUsage.frameworkSwitchHistory.push(switchEntry);

    this.frameworkSwitchHistory.push(switchData);
    this.trimHistory(this.frameworkSwitchHistory);

    this.logger.debug(
      `Framework switch recorded: ${switchData.fromFramework} → ${switchData.toFramework}`
    );
  }

  /**
   * Handle performance trend event
   */
  private handlePerformanceTrend(trend: PerformanceTrend): void {
    this.performanceTrends.push(trend);
    this.systemMetrics.performanceTrends.push(trend);
    this.trimHistory(this.performanceTrends);
    this.trimHistory(this.systemMetrics.performanceTrends);
  }

  /**
   * Record memory usage
   */
  private recordMemoryUsage(): void {
    const usage = process.memoryUsage();
    this.systemMetrics.memoryUsage = {
      heapUsed: usage.heapUsed,
      heapTotal: usage.heapTotal,
      external: usage.external,
      rss: usage.rss,
    };

    this.recordPerformanceTrend('memory_usage', usage.heapUsed, 'system');
  }

  private createExecutionDataFromCommand(metric: CommandExecutionMetric): ExecutionData {
    const metadata = metric.metadata ?? {};
    const frameworkUsed =
      typeof metadata['frameworkUsed'] === 'string' ? metadata['frameworkUsed'] : undefined;
    const frameworkEnabled =
      typeof metadata['frameworkEnabled'] === 'boolean'
        ? metadata['frameworkEnabled']
        : Boolean(frameworkUsed);
    const stepsExecuted =
      typeof metadata['stepsExecuted'] === 'number' ? metadata['stepsExecuted'] : undefined;
    const executionData: ExecutionData = {
      executionId: metric.commandId,
      executionType: this.mapExecutionModeToExecutionType(metric.executionMode),
      startTime: metric.startTime,
      endTime: metric.endTime,
      executionTime: metric.durationMs,
      success: metric.status === 'success',
      frameworkEnabled,
      toolName: metric.toolName,
    };

    if (frameworkUsed !== undefined) {
      executionData.frameworkUsed = frameworkUsed;
    }
    if (stepsExecuted !== undefined) {
      executionData.stepsExecuted = stepsExecuted;
    }
    if (metric.sessionId !== undefined) {
      executionData.sessionId = metric.sessionId;
    }
    if (metric.errorMessage !== undefined) {
      executionData.error = metric.errorMessage;
    }

    return executionData;
  }

  private mapExecutionModeToExecutionType(
    mode: CommandExecutionMode
  ): ExecutionData['executionType'] {
    switch (mode) {
      case 'chain':
        return 'chain';
      case 'single':
        return 'single';
      default:
        return 'single';
    }
  }

  /**
   * Record performance trend
   */
  private recordPerformanceTrend(
    metric: PerformanceTrend['metric'],
    value: number,
    context?: string
  ): void {
    const trend: PerformanceTrend = {
      timestamp: Date.now(),
      metric,
      value,
    };
    if (context !== undefined) {
      trend.context = context;
    }

    this.emit('system:performance', trend);
  }

  /**
   * Trim history arrays to max size
   */
  private trimHistory<T>(array: T[]): void {
    if (array.length > this.MAX_HISTORY_SIZE) {
      array.splice(0, array.length - this.MAX_HISTORY_SIZE);
    }
  }

  /**
   * Get execution statistics
   */
  getExecutionStats(): ExecutionStats {
    return { ...this.executionStats };
  }

  /**
   * Get system metrics
   */
  getSystemMetrics(): SystemMetrics {
    this.systemMetrics.uptime = Date.now() - this.startTime;
    this.systemMetrics.errorRate = this.getErrorRate();
    this.systemMetrics.averageResponseTime = this.executionStats.averageExecutionTime;

    return { ...this.systemMetrics };
  }

  /**
   * Get framework usage
   */
  getFrameworkUsage(): FrameworkUsage {
    return { ...this.frameworkUsage };
  }

  /**
   * Get comprehensive analytics summary
   */
  getAnalyticsSummary(options?: AnalyticsQueryOptions): AnalyticsSummary {
    const gateStats = {
      totalValidations: this.gateValidationStats.totalValidations,
      validationSuccessRate: this.getGateValidationSuccessRate(),
      averageValidationTime: this.getAverageGateValidationTime(),
      gateAdoptionRate: this.getGateAdoptionRate(),
    };

    const recommendations = this.generateRecommendations();

    return {
      executionStats: this.getExecutionStats(),
      systemMetrics: this.getSystemMetrics(),
      frameworkUsage: this.getFrameworkUsage(),
      gateValidationStats: gateStats,
      recommendations,
    };
  }

  /**
   * Reset analytics data
   */
  resetAnalytics(): void {
    this.executionStats = {
      totalExecutions: 0,
      successfulExecutions: 0,
      failedExecutions: 0,
      averageExecutionTime: 0,
      executionsByMode: {
        single: 0,
        chain: 0,
      },
      executionsByTool: {
        prompt_engine: 0,
        resource_manager: 0,
        system_control: 0,
      },
      lastUpdated: Date.now(),
    };

    this.gateValidationStats = {
      totalValidations: 0,
      successfulValidations: 0,
      totalValidationTime: 0,
      validationHistory: [],
    };

    this.frameworkUsage.frameworkSwitches = 0;
    this.frameworkUsage.frameworkSwitchHistory = [];
    this.frameworkUsage.frameworkPerformance = {};

    this.executionHistory = [];
    this.frameworkSwitchHistory = [];
    this.performanceTrends = [];
    this.systemMetrics.performanceTrends = [];
    this.pipelineStageHistory = [];
    this.commandExecutionHistory = [];

    this.logger.info('Analytics data reset');
  }

  /**
   * Calculate success rate
   */
  private getSuccessRate(): number {
    if (this.executionStats.totalExecutions === 0) return 100;
    return Math.round(
      (this.executionStats.successfulExecutions / this.executionStats.totalExecutions) * 100
    );
  }

  /**
   * Calculate error rate
   */
  private getErrorRate(): number {
    if (this.executionStats.totalExecutions === 0) return 0;
    return this.executionStats.failedExecutions / this.executionStats.totalExecutions;
  }

  /**
   * Calculate gate validation success rate
   */
  private getGateValidationSuccessRate(): number {
    if (this.gateValidationStats.totalValidations === 0) return 100;
    return (
      (this.gateValidationStats.successfulValidations / this.gateValidationStats.totalValidations) *
      100
    );
  }

  /**
   * Calculate average gate validation time
   */
  private getAverageGateValidationTime(): number {
    if (this.gateValidationStats.totalValidations === 0) return 0;
    return this.gateValidationStats.totalValidationTime / this.gateValidationStats.totalValidations;
  }

  /**
   * Calculate gate adoption rate
   */
  private getGateAdoptionRate(): number {
    if (this.executionStats.totalExecutions === 0) return 0;
    return (this.gateValidationStats.totalValidations / this.executionStats.totalExecutions) * 100;
  }

  /**
   * Generate recommendations based on analytics
   */
  private generateRecommendations(): string[] {
    const recommendations: string[] = [];

    // Performance recommendations
    if (this.executionStats.averageExecutionTime > 5000) {
      recommendations.push(
        'Average execution time is high (>5s). Consider optimizing prompt complexity or system resources.'
      );
    }

    // Memory recommendations
    const memoryUtilization =
      this.systemMetrics.memoryUsage.heapUsed / this.systemMetrics.memoryUsage.heapTotal;
    if (memoryUtilization > 0.8) {
      recommendations.push('High memory utilization detected (>80%). Monitor for memory leaks.');
    }

    // Gate adoption recommendations
    const gateAdoption = this.getGateAdoptionRate();
    if (gateAdoption < 50) {
      recommendations.push(
        'Low gate validation adoption (<50%). Consider enabling gates for better quality assurance.'
      );
    }

    // Error rate recommendations
    if (this.getErrorRate() > 0.1) {
      recommendations.push(
        'High error rate detected (>10%). Review failed executions for patterns.'
      );
    }

    // Framework recommendations
    const frameworks = Object.keys(this.frameworkUsage.frameworkPerformance);
    if (frameworks.length > 1) {
      const bestFramework = frameworks.reduce((best, current) => {
        const bestPerf = this.frameworkUsage.frameworkPerformance[best];
        const currentPerf = this.frameworkUsage.frameworkPerformance[current];
        if (!bestPerf) {
          return current;
        }
        if (!currentPerf) {
          return best;
        }
        return currentPerf.successRate > bestPerf.successRate ? current : best;
      });

      const bestPerf = this.frameworkUsage.frameworkPerformance[bestFramework];
      if (
        bestPerf !== undefined &&
        this.frameworkUsage.currentFramework !== bestFramework &&
        typeof bestPerf.successRate === 'number'
      ) {
        const bestSuccessRate = bestPerf.successRate;
        recommendations.push(
          `Consider switching to ${bestFramework} framework for better performance (${Math.round(
            bestSuccessRate * 100
          )}% success rate).`
        );
      }
    }

    return recommendations;
  }

  /**
   * Cleanup on shutdown
   */
  shutdown(): void {
    if (this.memoryCheckInterval) {
      clearInterval(this.memoryCheckInterval);
    }
    this.removeAllListeners();
    this.logger.info('AnalyticsService shutdown complete');
  }
}

/**
 * Create analytics service instance
 */
export function createMetricsCollector(logger: Logger): InMemoryMetricsCollector {
  return new InMemoryMetricsCollector(logger);
}
