/**
 * Threshold Manager
 * Configurable notification thresholds for security threat detection
 * CWE-778 Compliance: Logs all threshold configurations and evaluations
 */

const logger = require('./logger');
const logAggregator = require('./logAggregator');
const { db } = require('../database/db');

class ThresholdManager {
  constructor() {
    this.thresholds = new Map();
    this.initialize();
  }

  /**
   * Initialize threshold manager
   */
  async initialize() {
    await this.createThresholdsTable();
    await this.loadThresholds();
    
    logger.info('[ThresholdManager] Initialized with configurable thresholds');
    
    // Log initialization (CWE-778)
    logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold manager initialized', {
      totalThresholds: this.thresholds.size
    });
  }

  /**
   * Create thresholds table
   */
  async createThresholdsTable() {
    return new Promise((resolve, reject) => {
      db.run(`
        CREATE TABLE IF NOT EXISTS security_thresholds (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          threshold_id TEXT UNIQUE NOT NULL,
          name TEXT NOT NULL,
          description TEXT,
          pattern_type TEXT NOT NULL,
          metric_name TEXT NOT NULL,
          operator TEXT NOT NULL,
          threshold_value INTEGER NOT NULL,
          time_window_minutes INTEGER DEFAULT 30,
          severity TEXT NOT NULL,
          enabled INTEGER DEFAULT 1,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
      `, async (err) => {
        if (err) reject(err);
        else {
          db.run(`CREATE INDEX IF NOT EXISTS idx_thresholds_pattern ON security_thresholds(pattern_type, enabled)`);
          db.run(`CREATE INDEX IF NOT EXISTS idx_thresholds_enabled ON security_thresholds(enabled)`);
          await this.createDefaultThresholds();
          resolve();
        }
      });
    });
  }

  /**
   * Create default thresholds for common security patterns
   */
  async createDefaultThresholds() {
    const defaultThresholds = [
      {
        threshold_id: 'THRESHOLD-BRUTE-FORCE',
        name: 'Brute Force Attack Threshold',
        description: 'Alert when failed login attempts exceed threshold',
        pattern_type: 'brute_force_attack',
        metric_name: 'failed_login_count',
        operator: '>=',
        threshold_value: 5,
        time_window_minutes: 10,
        severity: 'critical'
      },
      {
        threshold_id: 'THRESHOLD-CREDENTIAL-STUFFING',
        name: 'Credential Stuffing Threshold',
        description: 'Alert on multiple username attempts from same IP',
        pattern_type: 'credential_stuffing',
        metric_name: 'unique_username_count',
        operator: '>=',
        threshold_value: 5,
        time_window_minutes: 5,
        severity: 'critical'
      },
      {
        threshold_id: 'THRESHOLD-PRIVILEGE-ESC',
        name: 'Privilege Escalation Threshold',
        description: 'Alert on repeated unauthorized access attempts',
        pattern_type: 'privilege_escalation',
        metric_name: 'escalation_attempt_count',
        operator: '>=',
        threshold_value: 3,
        time_window_minutes: 30,
        severity: 'critical'
      },
      {
        threshold_id: 'THRESHOLD-SUSPICIOUS-IP',
        name: 'Suspicious IP Activity Threshold',
        description: 'Alert on excessive requests from single IP',
        pattern_type: 'suspicious_ip',
        metric_name: 'request_count',
        operator: '>=',
        threshold_value: 100,
        time_window_minutes: 15,
        severity: 'high'
      },
      {
        threshold_id: 'THRESHOLD-DATA-EXFIL',
        name: 'Data Exfiltration Threshold',
        description: 'Alert on excessive data downloads',
        pattern_type: 'data_exfiltration',
        metric_name: 'download_count',
        operator: '>=',
        threshold_value: 10,
        time_window_minutes: 60,
        severity: 'high'
      },
      {
        threshold_id: 'THRESHOLD-SESSION-ANOMALY',
        name: 'Session Anomaly Threshold',
        description: 'Alert on unusual session patterns',
        pattern_type: 'session_anomaly',
        metric_name: 'anomaly_score',
        operator: '>=',
        threshold_value: 70,
        time_window_minutes: 30,
        severity: 'medium'
      },
      {
        threshold_id: 'THRESHOLD-IMPOSSIBLE-TRAVEL',
        name: 'Impossible Travel Threshold',
        description: 'Alert on geographically impossible travel speed',
        pattern_type: 'impossible_travel',
        metric_name: 'travel_speed_kmh',
        operator: '>=',
        threshold_value: 800,
        time_window_minutes: 60,
        severity: 'high'
      },
      {
        threshold_id: 'THRESHOLD-THREAT-SCORE',
        name: 'Critical Threat Score Threshold',
        description: 'Alert when overall threat score is critical',
        pattern_type: 'threat_score',
        metric_name: 'threat_score',
        operator: '>=',
        threshold_value: 80,
        time_window_minutes: 60,
        severity: 'critical'
      }
    ];

    for (const threshold of defaultThresholds) {
      await new Promise((resolve, reject) => {
        db.run(
          `INSERT OR IGNORE INTO security_thresholds 
           (threshold_id, name, description, pattern_type, metric_name, operator, threshold_value, time_window_minutes, severity)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
          [
            threshold.threshold_id,
            threshold.name,
            threshold.description,
            threshold.pattern_type,
            threshold.metric_name,
            threshold.operator,
            threshold.threshold_value,
            threshold.time_window_minutes,
            threshold.severity
          ],
          (err) => {
            if (err) reject(err);
            else resolve();
          }
        );
      });
    }

    logger.info(`[ThresholdManager] Created ${defaultThresholds.length} default thresholds`);
  }

  /**
   * Load thresholds from database into memory
   */
  async loadThresholds() {
    return new Promise((resolve, reject) => {
      db.all(
        `SELECT * FROM security_thresholds WHERE enabled = 1`,
        [],
        (err, rows) => {
          if (err) {
            reject(err);
          } else {
            this.thresholds.clear();
            rows.forEach(row => {
              this.thresholds.set(row.threshold_id, row);
            });
            logger.info(`[ThresholdManager] Loaded ${rows.length} active thresholds`);
            resolve();
          }
        }
      );
    });
  }

  /**
   * Evaluate if a metric value exceeds threshold
   * CWE-778: Logs all threshold evaluations
   */
  async evaluateThreshold(patternType, metricName, value, context = {}) {
    const matchingThresholds = Array.from(this.thresholds.values()).filter(
      t => t.pattern_type === patternType && t.metric_name === metricName
    );

    if (matchingThresholds.length === 0) {
      return { exceeded: false, thresholds: [] };
    }

    const exceededThresholds = [];

    for (const threshold of matchingThresholds) {
      const exceeded = this.compareValue(value, threshold.operator, threshold.threshold_value);

      // Log threshold evaluation (CWE-778)
      logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold evaluated', {
        thresholdId: threshold.threshold_id,
        patternType,
        metricName,
        value,
        operator: threshold.operator,
        thresholdValue: threshold.threshold_value,
        exceeded,
        severity: threshold.severity,
        context
      });

      if (exceeded) {
        exceededThresholds.push({
          ...threshold,
          actualValue: value,
          context
        });

        logger.warn(`[ThresholdManager] Threshold exceeded: ${threshold.name} (${value} ${threshold.operator} ${threshold.threshold_value})`);
      }
    }

    return {
      exceeded: exceededThresholds.length > 0,
      thresholds: exceededThresholds
    };
  }

  /**
   * Compare value against threshold using operator
   */
  compareValue(value, operator, threshold) {
    switch (operator) {
      case '>=': return value >= threshold;
      case '>': return value > threshold;
      case '<=': return value <= threshold;
      case '<': return value < threshold;
      case '==': return value == threshold;
      case '!=': return value != threshold;
      default: return false;
    }
  }

  /**
   * Get all thresholds
   */
  async getThresholds(filters = {}) {
    const { patternType, enabled, limit = 100 } = filters;

    let whereClause = [];
    let params = [];

    if (patternType) {
      whereClause.push('pattern_type = ?');
      params.push(patternType);
    }

    if (enabled !== undefined) {
      whereClause.push('enabled = ?');
      params.push(enabled ? 1 : 0);
    }

    const where = whereClause.length > 0 ? `WHERE ${whereClause.join(' AND ')}` : '';
    params.push(limit);

    return new Promise((resolve, reject) => {
      db.all(
        `SELECT * FROM security_thresholds ${where}
         ORDER BY pattern_type, threshold_value DESC
         LIMIT ?`,
        params,
        (err, rows) => {
          if (err) reject(err);
          else resolve(rows);
        }
      );
    });
  }

  /**
   * Get threshold by ID
   */
  async getThresholdById(thresholdId) {
    return new Promise((resolve, reject) => {
      db.get(
        `SELECT * FROM security_thresholds WHERE threshold_id = ?`,
        [thresholdId],
        (err, row) => {
          if (err) reject(err);
          else resolve(row);
        }
      );
    });
  }

  /**
   * Create new threshold
   * CWE-778: Logs threshold creation
   */
  async createThreshold(data, userId) {
    const thresholdId = `THRESHOLD-${Date.now()}-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;

    return new Promise((resolve, reject) => {
      db.run(
        `INSERT INTO security_thresholds 
         (threshold_id, name, description, pattern_type, metric_name, operator, threshold_value, time_window_minutes, severity, enabled)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          thresholdId,
          data.name,
          data.description || '',
          data.pattern_type,
          data.metric_name,
          data.operator,
          data.threshold_value,
          data.time_window_minutes || 30,
          data.severity,
          data.enabled !== undefined ? (data.enabled ? 1 : 0) : 1
        ],
        async (err) => {
          if (err) {
            reject(err);
          } else {
            await this.loadThresholds();

            // Log threshold creation (CWE-778)
            logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold created', {
              thresholdId,
              userId,
              name: data.name,
              patternType: data.pattern_type,
              metricName: data.metric_name,
              thresholdValue: data.threshold_value,
              severity: data.severity
            });

            logger.info(`[ThresholdManager] Threshold created: ${thresholdId} by user ${userId}`);
            resolve({ thresholdId });
          }
        }
      );
    });
  }

  /**
   * Update threshold
   * CWE-778: Logs threshold modifications
   */
  async updateThreshold(thresholdId, updates, userId) {
    const allowedFields = ['name', 'description', 'operator', 'threshold_value', 'time_window_minutes', 'severity', 'enabled'];
    const setClause = [];
    const params = [];

    for (const [key, value] of Object.entries(updates)) {
      if (allowedFields.includes(key)) {
        setClause.push(`${key} = ?`);
        params.push(key === 'enabled' ? (value ? 1 : 0) : value);
      }
    }

    if (setClause.length === 0) {
      throw new Error('No valid fields to update');
    }

    setClause.push('updated_at = CURRENT_TIMESTAMP');
    params.push(thresholdId);

    return new Promise((resolve, reject) => {
      db.run(
        `UPDATE security_thresholds 
         SET ${setClause.join(', ')}
         WHERE threshold_id = ?`,
        params,
        async (err) => {
          if (err) {
            reject(err);
          } else {
            await this.loadThresholds();

            // Log threshold update (CWE-778)
            logAggregator.aggregate('threshold_manager', 'info', 'security', 'Threshold updated', {
              thresholdId,
              userId,
              updates
            });

            logger.info(`[ThresholdManager] Threshold updated: ${thresholdId} by user ${userId}`);
            resolve({ success: true });
          }
        }
      );
    });
  }

  /**
   * Delete threshold
   * CWE-778: Logs threshold deletion
   */
  async deleteThreshold(thresholdId, userId) {
    return new Promise((resolve, reject) => {
      db.run(
        `DELETE FROM security_thresholds WHERE threshold_id = ?`,
        [thresholdId],
        async (err) => {
          if (err) {
            reject(err);
          } else {
            await this.loadThresholds();

            // Log threshold deletion (CWE-778)
            logAggregator.aggregate('threshold_manager', 'warn', 'security', 'Threshold deleted', {
              thresholdId,
              userId
            });

            logger.info(`[ThresholdManager] Threshold deleted: ${thresholdId} by user ${userId}`);
            resolve({ success: true });
          }
        }
      );
    });
  }

  /**
   * Get threshold statistics
   */
  async getStatistics() {
    return new Promise((resolve, reject) => {
      db.get(
        `SELECT 
           COUNT(*) as total,
           SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) as enabled,
           SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) as disabled,
           COUNT(DISTINCT pattern_type) as unique_patterns,
           COUNT(DISTINCT severity) as unique_severities
         FROM security_thresholds`,
        [],
        (err, row) => {
          if (err) reject(err);
          else resolve(row);
        }
      );
    });
  }
}

// Create singleton instance
const thresholdManager = new ThresholdManager();

module.exports = thresholdManager;
