// SIEM Reporter - Security Information and Event Management integration
// Supports CEF, JSON, and Syslog formats for real-time alert forwarding

export class SIEMReporter {
  constructor(config = {}) {
    this.config = config;
    this.enabled = config.siemEnabled || false;
    this.endpoint = config.siemEndpoint;
    this.format = config.siemFormat || 'cef'; // cef, json, syslog
    this.protocol = config.siemProtocol || 'https'; // https, http, syslog
    this.batchSize = config.siemBatchSize || 100;
    this.flushInterval = config.siemFlushInterval || 60000; // 1 minute
    this.severityMapping = config.severityMapping || this.getDefaultSeverityMapping();
    this.eventBuffer = [];
    this.lastFlushTime = Date.now();
    this.totalEventsSent = 0;
    this.failedEvents = 0;
  }

  /**
   * Initialize SIEM reporter
   */
  async initialize() {
    if (!this.enabled) {
      console.log('SIEM reporter disabled');
      return false;
    }

    if (!this.endpoint) {
      console.error('SIEM endpoint not configured');
      return false;
    }

    // Test connection
    const connected = await this.testConnection();

    if (!connected) {
      console.warn('Failed to connect to SIEM endpoint - will retry on events');
    }

    // Start periodic flush
    this.startPeriodicFlush();

    console.log('SIEM reporter initialized successfully');
    return true;
  }

  /**
   * Report detection event to SIEM
   * @param {object} detection - Detection result
   * @param {object} context - Additional context
   */
  async reportDetection(detection, context = {}) {
    if (!this.enabled) return;

    const event = this.formatDetectionEvent(detection, context);

    // Add to buffer
    this.eventBuffer.push(event);

    // Flush if buffer is full or if critical severity
    if (this.eventBuffer.length >= this.batchSize || detection.riskScore >= 15) {
      await this.flushEvents();
    }
  }

  /**
   * Report policy decision to SIEM
   * @param {object} decision - Policy decision
   * @param {object} context - Additional context
   */
  async reportPolicyDecision(decision, context = {}) {
    if (!this.enabled) return;

    const event = this.formatPolicyEvent(decision, context);
    this.eventBuffer.push(event);

    // Flush if buffer is full or if blocking action
    if (this.eventBuffer.length >= this.batchSize || decision.action === 'block') {
      await this.flushEvents();
    }
  }

  /**
   * Report enforcement action to SIEM
   * @param {object} enforcement - Enforcement action
   * @param {object} context - Additional context
   */
  async reportEnforcement(enforcement, context = {}) {
    if (!this.enabled) return;

    const event = this.formatEnforcementEvent(enforcement, context);
    this.eventBuffer.push(event);

    // Flush immediately for block/quarantine actions
    if (enforcement.action === 'block' || enforcement.action === 'quarantine') {
      await this.flushEvents();
    }
  }

  /**
   * Format detection event for SIEM
   * @param {object} detection - Detection result
   * @param {object} context - Context
   * @returns {object} - Formatted event
   */
  formatDetectionEvent(detection, context) {
    const baseEvent = {
      eventType: 'threat_detection',
      timestamp: Date.now(),
      severity: this.mapSeverity(detection.riskScore),
      riskScore: detection.riskScore,
      matched: detection.matched,
      findingCount: detection.findings?.length || 0,
      detectors: detection.findings?.map(f => f.detector) || [],
      categories: [...new Set(detection.findings?.map(f => f.category).filter(Boolean))],
      techniques: [...new Set(detection.findings?.map(f => f.metadata?.technique).filter(Boolean))],
      deviceId: context.deviceId || 'unknown',
      sessionId: context.sessionId ? this.hashValue(context.sessionId) : null,
      // NO PII: No URLs, domains, or user data
    };

    return baseEvent;
  }

  /**
   * Format policy decision event for SIEM
   * @param {object} decision - Policy decision
   * @param {object} context - Context
   * @returns {object} - Formatted event
   */
  formatPolicyEvent(decision, context) {
    return {
      eventType: 'policy_decision',
      timestamp: Date.now(),
      action: decision.action,
      severity: this.mapActionToSeverity(decision.action),
      riskScore: decision.riskScore,
      reason: decision.reason,
      policyId: context.policyId || 'default',
      matchedRuleCount: decision.matchedRules?.length || 0,
      enforcementType: decision.enforcement?.type || null,
      deviceId: context.deviceId || 'unknown',
      sessionId: context.sessionId ? this.hashValue(context.sessionId) : null
    };
  }

  /**
   * Format enforcement action event for SIEM
   * @param {object} enforcement - Enforcement action
   * @param {object} context - Context
   * @returns {object} - Formatted event
   */
  formatEnforcementEvent(enforcement, context) {
    return {
      eventType: 'enforcement_action',
      timestamp: Date.now(),
      action: enforcement.action,
      severity: this.mapActionToSeverity(enforcement.action),
      enforcementType: enforcement.type,
      message: enforcement.message,
      userOverride: enforcement.userOverride || false,
      deviceId: context.deviceId || 'unknown',
      sessionId: context.sessionId ? this.hashValue(context.sessionId) : null
    };
  }

  /**
   * Flush events to SIEM
   */
  async flushEvents() {
    if (this.eventBuffer.length === 0) return;

    const batch = [...this.eventBuffer];
    this.eventBuffer = [];

    try {
      let formattedData;

      switch (this.format) {
        case 'cef':
          formattedData = this.formatAsCEF(batch);
          break;
        case 'json':
          formattedData = this.formatAsJSON(batch);
          break;
        case 'syslog':
          formattedData = this.formatAsSyslog(batch);
          break;
        default:
          console.error(`Unknown SIEM format: ${this.format}`);
          return;
      }

      await this.sendToSIEM(formattedData);

      this.totalEventsSent += batch.length;
      this.lastFlushTime = Date.now();

      console.log(`Flushed ${batch.length} events to SIEM`);
    } catch (error) {
      console.error('Failed to flush events to SIEM:', error);
      this.failedEvents += batch.length;

      // Re-add to buffer for retry (with limit)
      if (this.eventBuffer.length < this.batchSize * 2) {
        this.eventBuffer.unshift(...batch);
      }
    }
  }

  /**
   * Format events as Common Event Format (CEF)
   * @param {array} events - Events to format
   * @returns {string} - CEF formatted string
   */
  formatAsCEF(events) {
    const cefEvents = events.map(event => {
      // CEF Header format:
      // CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|Extension

      const version = 0;
      const deviceVendor = 'PhishProtect';
      const deviceProduct = 'BrowserProtection';
      const deviceVersion = chrome.runtime.getManifest().version;
      const signatureId = event.eventType;
      const name = this.getEventName(event);
      const severity = event.severity;

      // Extension fields (key=value pairs)
      const extensions = [];

      if (event.riskScore !== undefined) {
        extensions.push(`cs1=${event.riskScore}`);
        extensions.push(`cs1Label=RiskScore`);
      }

      if (event.action) {
        extensions.push(`act=${event.action}`);
      }

      if (event.findingCount !== undefined) {
        extensions.push(`cnt=${event.findingCount}`);
      }

      if (event.detectors && event.detectors.length > 0) {
        extensions.push(`cs2=${event.detectors.join(',')}`);
        extensions.push(`cs2Label=Detectors`);
      }

      if (event.categories && event.categories.length > 0) {
        extensions.push(`cs3=${event.categories.join(',')}`);
        extensions.push(`cs3Label=Categories`);
      }

      if (event.techniques && event.techniques.length > 0) {
        extensions.push(`cs4=${event.techniques.join(',')}`);
        extensions.push(`cs4Label=MITRETechniques`);
      }

      if (event.deviceId) {
        extensions.push(`dvchost=${event.deviceId}`);
      }

      if (event.sessionId) {
        extensions.push(`cs5=${event.sessionId}`);
        extensions.push(`cs5Label=SessionID`);
      }

      extensions.push(`rt=${event.timestamp}`);

      const extensionStr = extensions.join(' ');

      return `CEF:${version}|${deviceVendor}|${deviceProduct}|${deviceVersion}|${signatureId}|${name}|${severity}|${extensionStr}`;
    });

    return cefEvents.join('\n');
  }

  /**
   * Format events as JSON
   * @param {array} events - Events to format
   * @returns {string} - JSON formatted string
   */
  formatAsJSON(events) {
    const payload = {
      source: 'PhishProtect',
      version: chrome.runtime.getManifest().version,
      timestamp: Date.now(),
      eventCount: events.length,
      events
    };

    return JSON.stringify(payload);
  }

  /**
   * Format events as Syslog (RFC 5424)
   * @param {array} events - Events to format
   * @returns {string} - Syslog formatted string
   */
  formatAsSyslog(events) {
    const syslogEvents = events.map(event => {
      // Syslog format: <PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID STRUCTURED-DATA MSG

      const facility = 16; // local use 0
      const severityLevel = this.mapToSyslogSeverity(event.severity);
      const pri = (facility * 8) + severityLevel;

      const version = 1;
      const timestamp = new Date(event.timestamp).toISOString();
      const hostname = event.deviceId || 'unknown';
      const appName = 'PhishProtect';
      const procId = '-';
      const msgId = event.eventType;

      // Structured data
      const structuredData = this.formatSyslogStructuredData(event);

      // Message
      const msg = this.getEventName(event);

      return `<${pri}>${version} ${timestamp} ${hostname} ${appName} ${procId} ${msgId} ${structuredData} ${msg}`;
    });

    return syslogEvents.join('\n');
  }

  /**
   * Format syslog structured data
   * @param {object} event - Event to format
   * @returns {string} - Structured data string
   */
  formatSyslogStructuredData(event) {
    const elements = [];

    if (event.riskScore !== undefined) {
      elements.push(`riskScore="${event.riskScore}"`);
    }

    if (event.action) {
      elements.push(`action="${event.action}"`);
    }

    if (event.findingCount !== undefined) {
      elements.push(`findingCount="${event.findingCount}"`);
    }

    if (event.detectors && event.detectors.length > 0) {
      elements.push(`detectors="${event.detectors.join(',')}"`);
    }

    if (elements.length === 0) {
      return '-';
    }

    return `[phishprotect@32473 ${elements.join(' ')}]`;
  }

  /**
   * Send data to SIEM endpoint
   * @param {string} data - Formatted data
   */
  async sendToSIEM(data) {
    const headers = {
      'Content-Type': this.getContentType()
    };

    // Add authentication if configured
    if (this.config.siemApiKey) {
      headers['Authorization'] = `Bearer ${this.config.siemApiKey}`;
    }

    const response = await fetch(this.endpoint, {
      method: 'POST',
      headers,
      body: data
    });

    if (!response.ok) {
      throw new Error(`SIEM endpoint returned ${response.status}: ${response.statusText}`);
    }
  }

  /**
   * Get content type for format
   * @returns {string} - Content type
   */
  getContentType() {
    switch (this.format) {
      case 'cef':
        return 'text/plain';
      case 'json':
        return 'application/json';
      case 'syslog':
        return 'text/plain';
      default:
        return 'application/json';
    }
  }

  /**
   * Map risk score to severity level
   * @param {number} riskScore - Risk score
   * @returns {number} - Severity level (0-10)
   */
  mapSeverity(riskScore) {
    if (riskScore >= 15) return 10; // Critical
    if (riskScore >= 10) return 7;  // High
    if (riskScore >= 5) return 5;   // Medium
    return 3; // Low
  }

  /**
   * Map action to severity level
   * @param {string} action - Action
   * @returns {number} - Severity level
   */
  mapActionToSeverity(action) {
    const mapping = {
      'block': 10,
      'quarantine': 7,
      'warn': 5,
      'report': 3,
      'allow': 1
    };

    return mapping[action] || 5;
  }

  /**
   * Map to syslog severity (0-7)
   * @param {number} severity - Severity level (0-10)
   * @returns {number} - Syslog severity
   */
  mapToSyslogSeverity(severity) {
    if (severity >= 9) return 2; // Critical
    if (severity >= 7) return 3; // Error
    if (severity >= 5) return 4; // Warning
    if (severity >= 3) return 5; // Notice
    return 6; // Informational
  }

  /**
   * Get default severity mapping
   * @returns {object} - Severity mapping
   */
  getDefaultSeverityMapping() {
    return {
      critical: 10,
      high: 7,
      medium: 5,
      low: 3,
      info: 1
    };
  }

  /**
   * Get event name from event object
   * @param {object} event - Event
   * @returns {string} - Event name
   */
  getEventName(event) {
    const names = {
      'threat_detection': 'Threat Detected',
      'policy_decision': 'Policy Decision',
      'enforcement_action': 'Enforcement Action'
    };

    return names[event.eventType] || event.eventType;
  }

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

    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)}`;
  }

  /**
   * Start periodic event flush
   */
  startPeriodicFlush() {
    if (this.flushIntervalId) {
      clearInterval(this.flushIntervalId);
    }

    this.flushIntervalId = setInterval(async () => {
      if (this.eventBuffer.length > 0) {
        await this.flushEvents();
      }
    }, this.flushInterval);
  }

  /**
   * Stop periodic flush
   */
  stopPeriodicFlush() {
    if (this.flushIntervalId) {
      clearInterval(this.flushIntervalId);
      this.flushIntervalId = null;
    }
  }

  /**
   * Test connection to SIEM endpoint
   */
  async testConnection() {
    try {
      // Send a test event
      const testEvent = {
        eventType: 'test_connection',
        timestamp: Date.now(),
        severity: 1,
        message: 'SIEM connection test'
      };

      const formattedData = this.formatAsJSON([testEvent]);
      await this.sendToSIEM(formattedData);

      console.log('SIEM connection test successful');
      return true;
    } catch (error) {
      console.error('SIEM connection test failed:', error);
      return false;
    }
  }

  /**
   * Get statistics
   * @returns {object} - SIEM reporter statistics
   */
  getStatistics() {
    return {
      enabled: this.enabled,
      format: this.format,
      endpoint: this.endpoint,
      bufferedEvents: this.eventBuffer.length,
      totalEventsSent: this.totalEventsSent,
      failedEvents: this.failedEvents,
      lastFlushTime: this.lastFlushTime,
      batchSize: this.batchSize,
      flushInterval: this.flushInterval
    };
  }

  /**
   * Force flush events
   */
  async forceFlush() {
    console.log('Force flushing SIEM events...');
    await this.flushEvents();
  }

  /**
   * Clean up and stop reporter
   */
  cleanup() {
    this.stopPeriodicFlush();

    // Flush any remaining events
    if (this.eventBuffer.length > 0) {
      this.flushEvents().catch(error => {
        console.error('Failed to flush events during cleanup:', error);
      });
    }

    console.log('SIEM reporter cleaned up');
  }
}
