// Telemetry Collector - Collects performance and detection metrics
// Privacy-first: NO PII, URLs, domains, or sensitive user data

export class TelemetryCollector {
  constructor(config = {}) {
    this.config = config;
    this.enabled = config.telemetryEnabled !== false;
    this.metrics = {
      detections: new Map(),      // Per-detector hit counts
      performance: new Map(),      // Per-detector latency stats
      riskDistribution: {          // Risk level distribution
        critical: 0,
        high: 0,
        medium: 0,
        low: 0
      },
      actions: {                   // Policy actions taken
        block: 0,
        quarantine: 0,
        warn: 0,
        report: 0,
        allow: 0
      },
      categories: new Map(),       // Detections by category
      techniques: new Map(),       // MITRE techniques detected
      sessionStats: {
        totalAnalyses: 0,
        avgRiskScore: 0,
        startTime: Date.now()
      }
    };

    this.performanceBuffer = [];
    this.maxBufferSize = config.maxBufferSize || 1000;
  }

  /**
   * Record detection event
   * @param {object} detection - Detection result
   * @param {object} context - Additional context
   */
  recordDetection(detection, context = {}) {
    if (!this.enabled) return;

    // Record per-detector hits
    for (const finding of detection.findings || []) {
      const detector = finding.detector || 'unknown';

      const detectorStats = this.metrics.detections.get(detector) || {
        hits: 0,
        totalWeight: 0,
        severities: {}
      };

      detectorStats.hits++;
      detectorStats.totalWeight += finding.weight || 0;

      const severity = finding.severity || 'unknown';
      detectorStats.severities[severity] =
        (detectorStats.severities[severity] || 0) + 1;

      this.metrics.detections.set(detector, detectorStats);

      // Record category
      if (finding.category) {
        const categoryCount = this.metrics.categories.get(finding.category) || 0;
        this.metrics.categories.set(finding.category, categoryCount + 1);
      }

      // Record technique
      if (finding.metadata?.technique) {
        const techniqueCount = this.metrics.techniques.get(finding.metadata.technique) || 0;
        this.metrics.techniques.set(finding.metadata.technique, techniqueCount + 1);
      }
    }

    // Update risk distribution
    const riskLevel = this.getRiskLevel(detection.riskScore);
    this.metrics.riskDistribution[riskLevel]++;

    // Update session stats
    this.metrics.sessionStats.totalAnalyses++;

    // Update average risk score
    const currentAvg = this.metrics.sessionStats.avgRiskScore;
    const total = this.metrics.sessionStats.totalAnalyses;
    this.metrics.sessionStats.avgRiskScore =
      ((currentAvg * (total - 1)) + detection.riskScore) / total;
  }

  /**
   * Record detector performance
   * @param {string} detectorName - Detector name
   * @param {number} latency - Execution time in ms
   * @param {boolean} matched - Whether detector matched
   */
  recordPerformance(detectorName, latency, matched = false) {
    if (!this.enabled) return;

    const stats = this.metrics.performance.get(detectorName) || {
      count: 0,
      totalLatency: 0,
      minLatency: Infinity,
      maxLatency: 0,
      latencies: [],
      hits: 0
    };

    stats.count++;
    stats.totalLatency += latency;
    stats.minLatency = Math.min(stats.minLatency, latency);
    stats.maxLatency = Math.max(stats.maxLatency, latency);

    // Keep latencies for percentile calculation (with limit)
    stats.latencies.push(latency);
    if (stats.latencies.length > 100) {
      stats.latencies.shift(); // Remove oldest
    }

    if (matched) {
      stats.hits++;
    }

    this.metrics.performance.set(detectorName, stats);

    // Add to performance buffer for aggregation
    this.performanceBuffer.push({
      detector: detectorName,
      latency,
      matched,
      timestamp: Date.now()
    });

    // Trim buffer if too large
    if (this.performanceBuffer.length > this.maxBufferSize) {
      this.performanceBuffer.shift();
    }
  }

  /**
   * Record policy action
   * @param {string} action - Action taken
   */
  recordAction(action) {
    if (!this.enabled) return;

    if (action in this.metrics.actions) {
      this.metrics.actions[action]++;
    }
  }

  /**
   * Get detector statistics
   * @param {string} detectorName - Optional detector name filter
   * @returns {object} - Detector statistics
   */
  getDetectorStats(detectorName = null) {
    if (detectorName) {
      return {
        detection: this.metrics.detections.get(detectorName) || null,
        performance: this.getPerformanceStats(detectorName)
      };
    }

    // Return all detector stats
    const stats = {};

    for (const [detector, detectionStats] of this.metrics.detections) {
      stats[detector] = {
        detection: detectionStats,
        performance: this.getPerformanceStats(detector)
      };
    }

    return stats;
  }

  /**
   * Get performance statistics for a detector
   * @param {string} detectorName - Detector name
   * @returns {object} - Performance stats
   */
  getPerformanceStats(detectorName) {
    const stats = this.metrics.performance.get(detectorName);

    if (!stats) return null;

    const latencies = [...stats.latencies].sort((a, b) => a - b);

    return {
      count: stats.count,
      avgLatency: Math.round(stats.totalLatency / stats.count),
      minLatency: stats.minLatency,
      maxLatency: stats.maxLatency,
      p50: this.getPercentile(latencies, 50),
      p95: this.getPercentile(latencies, 95),
      p99: this.getPercentile(latencies, 99),
      hitRate: stats.count > 0 ? (stats.hits / stats.count) : 0
    };
  }

  /**
   * Get percentile value from sorted array
   * @param {array} sortedValues - Sorted values
   * @param {number} percentile - Percentile (0-100)
   * @returns {number} - Percentile value
   */
  getPercentile(sortedValues, percentile) {
    if (sortedValues.length === 0) return 0;

    const index = Math.ceil((percentile / 100) * sortedValues.length) - 1;
    return sortedValues[Math.max(0, index)];
  }

  /**
   * Get risk distribution statistics
   * @returns {object} - Risk distribution
   */
  getRiskDistribution() {
    return { ...this.metrics.riskDistribution };
  }

  /**
   * Get action distribution
   * @returns {object} - Action counts
   */
  getActionDistribution() {
    return { ...this.metrics.actions };
  }

  /**
   * Get top detectors by hit count
   * @param {number} limit - Number of top detectors
   * @returns {array} - Top detectors
   */
  getTopDetectors(limit = 10) {
    const detectors = Array.from(this.metrics.detections.entries())
      .map(([name, stats]) => ({
        name,
        hits: stats.hits,
        avgWeight: stats.totalWeight / stats.hits
      }))
      .sort((a, b) => b.hits - a.hits)
      .slice(0, limit);

    return detectors;
  }

  /**
   * Get top techniques detected
   * @param {number} limit - Number of top techniques
   * @returns {array} - Top techniques
   */
  getTopTechniques(limit = 10) {
    return Array.from(this.metrics.techniques.entries())
      .map(([technique, count]) => ({ technique, count }))
      .sort((a, b) => b.count - a.count)
      .slice(0, limit);
  }

  /**
   * Get category distribution
   * @returns {object} - Category counts
   */
  getCategoryDistribution() {
    const distribution = {};

    for (const [category, count] of this.metrics.categories) {
      distribution[category] = count;
    }

    return distribution;
  }

  /**
   * Get session statistics
   * @returns {object} - Session stats
   */
  getSessionStats() {
    return {
      ...this.metrics.sessionStats,
      uptime: Date.now() - this.metrics.sessionStats.startTime,
      avgRiskScore: Math.round(this.metrics.sessionStats.avgRiskScore * 100) / 100
    };
  }

  /**
   * Get comprehensive telemetry summary
   * @returns {object} - Complete telemetry data
   */
  getSummary() {
    return {
      enabled: this.enabled,
      session: this.getSessionStats(),
      riskDistribution: this.getRiskDistribution(),
      actionDistribution: this.getActionDistribution(),
      topDetectors: this.getTopDetectors(10),
      topTechniques: this.getTopTechniques(10),
      categoryDistribution: this.getCategoryDistribution(),
      detectorStats: this.getDetectorStats(),
      timestamp: Date.now()
    };
  }

  /**
   * Get risk level from score
   * @param {number} riskScore - Risk score
   * @returns {string} - Risk level
   */
  getRiskLevel(riskScore) {
    if (riskScore >= 15) return 'critical';
    if (riskScore >= 10) return 'high';
    if (riskScore >= 5) return 'medium';
    return 'low';
  }

  /**
   * Reset all metrics
   */
  reset() {
    this.metrics = {
      detections: new Map(),
      performance: new Map(),
      riskDistribution: { critical: 0, high: 0, medium: 0, low: 0 },
      actions: { block: 0, quarantine: 0, warn: 0, report: 0, allow: 0 },
      categories: new Map(),
      techniques: new Map(),
      sessionStats: {
        totalAnalyses: 0,
        avgRiskScore: 0,
        startTime: Date.now()
      }
    };

    this.performanceBuffer = [];
  }

  /**
   * Export metrics for external analysis
   * @returns {object} - Exportable metrics
   */
  exportMetrics() {
    const exported = this.getSummary();

    // Convert Maps to objects for JSON serialization
    exported.detectorStats = {};
    for (const [name, stats] of this.metrics.detections) {
      exported.detectorStats[name] = {
        ...stats,
        performance: this.getPerformanceStats(name)
      };
    }

    return exported;
  }

  /**
   * Enable/disable telemetry
   * @param {boolean} enabled - Enable flag
   */
  setEnabled(enabled) {
    this.enabled = enabled;

    if (!enabled) {
      console.log('Telemetry collection disabled');
    }
  }
}
