// Privacy Filter - Scrubs PII and sensitive data from telemetry
// CRITICAL: Never log URLs, domains, user data, or identifying information

export class PrivacyFilter {
  constructor(config = {}) {
    this.config = config;
    this.strictMode = config.strictPrivacy !== false;
  }

  /**
   * Filter detection data for telemetry
   * @param {object} detection - Detection result
   * @returns {object} - Filtered detection data
   */
  filterDetection(detection) {
    return {
      matched: detection.matched,
      riskScore: detection.riskScore,
      findingCount: detection.findings?.length || 0,
      findings: detection.findings?.map(f => this.filterFinding(f)) || [],
      timestamp: Date.now()
    };
  }

  /**
   * Filter individual finding
   * @param {object} finding - Finding to filter
   * @returns {object} - Filtered finding
   */
  filterFinding(finding) {
    return {
      detector: finding.detector,
      pattern: finding.pattern,
      severity: finding.severity,
      weight: finding.weight,
      category: finding.category,
      technique: finding.metadata?.technique || null,
      // NO matched value (may contain PII)
      // NO URLs, domains, or user data
    };
  }

  /**
   * Filter policy decision for telemetry
   * @param {object} decision - Policy decision
   * @returns {object} - Filtered decision
   */
  filterDecision(decision) {
    return {
      action: decision.action,
      riskScore: decision.riskScore,
      findingCount: decision.findings?.length || 0,
      enforcementType: decision.enforcement?.type || null,
      matchedRuleCount: decision.matchedRules?.length || 0,
      timestamp: Date.now()
    };
  }

  /**
   * Filter performance data
   * @param {object} perfData - Performance data
   * @returns {object} - Filtered performance data
   */
  filterPerformance(perfData) {
    return {
      detector: perfData.detector,
      latency: perfData.latency,
      matched: perfData.matched,
      // Truncate timestamp to hour for privacy
      hour: Math.floor(perfData.timestamp / 3600000)
    };
  }

  /**
   * Filter URL for logging (if absolutely necessary)
   * @param {string} url - URL to filter
   * @returns {object} - Filtered URL components
   */
  filterUrl(url) {
    if (!url) return null;

    try {
      const urlObj = new URL(url);

      return {
        protocol: urlObj.protocol,
        // NO hostname/domain
        // NO pathname
        // NO search params
        hasParams: urlObj.search.length > 0,
        hasHash: urlObj.hash.length > 0,
        port: urlObj.port || null
      };
    } catch {
      return null;
    }
  }

  /**
   * Scrub PII from string
   * @param {string} text - Text to scrub
   * @returns {string} - Scrubbed text
   */
  scrubPII(text) {
    if (!text) return '';

    let scrubbed = text;

    // Remove email addresses
    scrubbed = scrubbed.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL]');

    // Remove phone numbers
    scrubbed = scrubbed.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, '[PHONE]');

    // Remove credit card numbers
    scrubbed = scrubbed.replace(/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g, '[CARD]');

    // Remove SSN
    scrubbed = scrubbed.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]');

    // Remove IP addresses
    scrubbed = scrubbed.replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '[IP]');

    // Remove URLs
    scrubbed = scrubbed.replace(/https?:\/\/[^\s]+/g, '[URL]');

    return scrubbed;
  }

  /**
   * Hash sensitive value for aggregation
   * @param {string} value - Value to hash
   * @returns {string} - Hashed value
   */
  hashValue(value) {
    if (!value) return null;

    // Simple hash function (in production, use crypto.subtle.digest)
    let hash = 0;
    for (let i = 0; i < value.length; i++) {
      const char = value.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }

    return `hash_${Math.abs(hash).toString(36)}`;
  }

  /**
   * Aggregate data for privacy (e.g., by hour instead of exact time)
   * @param {number} timestamp - Exact timestamp
   * @param {string} granularity - Aggregation level (hour, day, week)
   * @returns {number} - Aggregated timestamp
   */
  aggregateTimestamp(timestamp, granularity = 'hour') {
    const intervals = {
      'hour': 3600000,
      'day': 86400000,
      'week': 604800000
    };

    const interval = intervals[granularity] || intervals.hour;
    return Math.floor(timestamp / interval) * interval;
  }

  /**
   * Check if data contains PII
   * @param {object} data - Data to check
   * @returns {boolean} - True if PII detected
   */
  containsPII(data) {
    const dataStr = JSON.stringify(data).toLowerCase();

    const piiPatterns = [
      /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/,  // Email
      /\d{3}[-.]?\d{3}[-.]?\d{4}/,              // Phone
      /\d{3}-\d{2}-\d{4}/,                      // SSN
      /\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}/  // Credit card
    ];

    return piiPatterns.some(pattern => pattern.test(dataStr));
  }

  /**
   * Validate telemetry data before sending
   * @param {object} data - Telemetry data
   * @returns {boolean} - True if safe to send
   */
  validateTelemetry(data) {
    // Check for PII
    if (this.containsPII(data)) {
      console.error('PII detected in telemetry data - blocking send');
      return false;
    }

    // Check for URLs/domains
    const dataStr = JSON.stringify(data);
    if (dataStr.includes('http://') || dataStr.includes('https://')) {
      console.error('URL detected in telemetry data - blocking send');
      return false;
    }

    // Check for domain patterns
    if (/\w+\.(com|net|org|edu|gov)/i.test(dataStr)) {
      console.error('Domain detected in telemetry data - blocking send');
      return false;
    }

    return true;
  }

  /**
   * Create privacy-safe summary
   * @param {object} metrics - Raw metrics
   * @returns {object} - Privacy-safe summary
   */
  createSafeSummary(metrics) {
    return {
      // Aggregated counts only
      totalDetections: metrics.detectionCount || 0,
      riskDistribution: metrics.riskDistribution || {},
      actionDistribution: metrics.actionDistribution || {},

      // Category statistics (no specific techniques)
      topCategories: this.aggregateCategories(metrics.categories),

      // Performance aggregates
      performanceStats: this.aggregatePerformance(metrics.performance),

      // Temporal aggregation (hour, not exact time)
      reportingPeriod: {
        start: this.aggregateTimestamp(metrics.startTime, 'hour'),
        end: this.aggregateTimestamp(Date.now(), 'hour')
      },

      // NO URLs, domains, user data
      // NO exact timestamps
      // NO identifying information
    };
  }

  /**
   * Aggregate categories for privacy
   * @param {object} categories - Category data
   * @returns {array} - Aggregated categories
   */
  aggregateCategories(categories = {}) {
    return Object.entries(categories)
      .map(([category, count]) => ({
        category,
        count
      }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 10); // Top 10 only
  }

  /**
   * Aggregate performance data
   * @param {object} performance - Performance data
   * @returns {object} - Aggregated performance
   */
  aggregatePerformance(performance = {}) {
    return {
      avgLatency: performance.avgLatency || 0,
      p95Latency: performance.p95 || 0,
      totalAnalyses: performance.count || 0
      // NO per-detector breakdowns (could reveal user behavior)
    };
  }

  /**
   * Differential privacy: Add noise to counts
   * @param {number} value - True count
   * @param {number} epsilon - Privacy parameter (smaller = more privacy)
   * @returns {number} - Noisy count
   */
  addNoise(value, epsilon = 0.1) {
    if (!this.strictMode) return value;

    // Laplace mechanism for differential privacy
    const scale = 1 / epsilon;
    const noise = this.laplaceNoise(scale);

    return Math.max(0, Math.round(value + noise));
  }

  /**
   * Generate Laplace noise
   * @param {number} scale - Noise scale
   * @returns {number} - Noise value
   */
  laplaceNoise(scale) {
    const u = Math.random() - 0.5;
    return -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
  }

  /**
   * Apply k-anonymity (minimum threshold for reporting)
   * @param {object} data - Data to check
   * @param {number} k - Minimum count threshold
   * @returns {boolean} - True if meets k-anonymity
   */
  meetsKAnonymity(data, k = 5) {
    // Only report if count >= k
    return (data.count || 0) >= k;
  }
}
