/**
 * Real-time Alert System
 * Automated notification system for security events
 * Supports multiple notification channels and alert rules
 */

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

class AlertSystem extends EventEmitter {
  constructor() {
    super();
    this.activeAlerts = new Map();
    this.alertRules = new Map();
    this.initialize();
  }

  /**
   * Initialize alert system
   */
  async initialize() {
    await this.createAlertsTable();
    await this.createAlertRulesTable();
    await this.loadAlertRules();
    
    logger.info('[AlertSystem] Initialized - Real-time monitoring enabled');
  }

  /**
   * Create alerts table
   */
  async createAlertsTable() {
    return new Promise((resolve, reject) => {
      db.run(`
        CREATE TABLE IF NOT EXISTS security_alerts (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          alert_id TEXT UNIQUE NOT NULL,
          rule_id TEXT,
          severity TEXT NOT NULL,
          title TEXT NOT NULL,
          description TEXT NOT NULL,
          affected_entity TEXT,
          source_data TEXT,
          status TEXT DEFAULT 'active',
          acknowledged_at DATETIME,
          acknowledged_by INTEGER,
          resolved_at DATETIME,
          resolved_by INTEGER,
          resolution_notes TEXT,
          notification_sent INTEGER DEFAULT 0,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
      `, (err) => {
        if (err) reject(err);
        else {
          db.run(`CREATE INDEX IF NOT EXISTS idx_alerts_severity ON security_alerts(severity, created_at DESC)`);
          db.run(`CREATE INDEX IF NOT EXISTS idx_alerts_status ON security_alerts(status, created_at DESC)`);
          db.run(`CREATE INDEX IF NOT EXISTS idx_alerts_rule ON security_alerts(rule_id)`);
          resolve();
        }
      });
    });
  }

  /**
   * Create alert rules table
   */
  async createAlertRulesTable() {
    return new Promise((resolve, reject) => {
      db.run(`
        CREATE TABLE IF NOT EXISTS alert_rules (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          rule_id TEXT UNIQUE NOT NULL,
          name TEXT NOT NULL,
          description TEXT,
          rule_type TEXT NOT NULL,
          condition TEXT NOT NULL,
          severity TEXT NOT NULL,
          enabled INTEGER DEFAULT 1,
          notification_channels TEXT,
          cooldown_minutes INTEGER DEFAULT 10,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
      `, async (err) => {
        if (err) reject(err);
        else {
          await this.createDefaultRules();
          resolve();
        }
      });
    });
  }

  /**
   * Create default alert rules
   */
  async createDefaultRules() {
    const defaultRules = [
      {
        rule_id: 'RULE-BRUTE-FORCE',
        name: 'Brute Force Detection',
        description: 'Alert on brute force attack patterns',
        rule_type: 'anomaly',
        condition: JSON.stringify({ anomaly_type: 'brute_force_attack' }),
        severity: 'critical',
        notification_channels: JSON.stringify(['in_app', 'email']),
        cooldown_minutes: 10
      },
      {
        rule_id: 'RULE-PRIVILEGE-ESC',
        name: 'Privilege Escalation',
        description: 'Alert on privilege escalation attempts',
        rule_type: 'anomaly',
        condition: JSON.stringify({ anomaly_type: 'privilege_escalation' }),
        severity: 'critical',
        notification_channels: JSON.stringify(['in_app', 'email']),
        cooldown_minutes: 5
      },
      {
        rule_id: 'RULE-DATA-EXFIL',
        name: 'Data Exfiltration',
        description: 'Alert on potential data exfiltration',
        rule_type: 'anomaly',
        condition: JSON.stringify({ anomaly_type: 'data_exfiltration' }),
        severity: 'high',
        notification_channels: JSON.stringify(['in_app', 'email']),
        cooldown_minutes: 15
      },
      {
        rule_id: 'RULE-THREAT-CRITICAL',
        name: 'Critical Threat Level',
        description: 'Alert when threat score exceeds 80',
        rule_type: 'threshold',
        condition: JSON.stringify({ metric: 'threat_score', operator: '>=', value: 80 }),
        severity: 'critical',
        notification_channels: JSON.stringify(['in_app', 'email']),
        cooldown_minutes: 30
      },
      {
        rule_id: 'RULE-SUSPICIOUS-IP',
        name: 'Suspicious IP Activity',
        description: 'Alert on suspicious IP behavior',
        rule_type: 'anomaly',
        condition: JSON.stringify({ anomaly_type: 'suspicious_ip' }),
        severity: 'high',
        notification_channels: JSON.stringify(['in_app']),
        cooldown_minutes: 20
      },
      {
        rule_id: 'RULE-SESSION-ANOMALY',
        name: 'Session Anomaly',
        description: 'Alert on unusual session patterns',
        rule_type: 'anomaly',
        condition: JSON.stringify({ anomaly_type: 'session_anomaly' }),
        severity: 'medium',
        notification_channels: JSON.stringify(['in_app']),
        cooldown_minutes: 30
      }
    ];

    for (const rule of defaultRules) {
      await new Promise((resolve, reject) => {
        db.run(
          `INSERT OR IGNORE INTO alert_rules
           (rule_id, name, description, rule_type, condition, severity, notification_channels, cooldown_minutes)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
          [
            rule.rule_id,
            rule.name,
            rule.description,
            rule.rule_type,
            rule.condition,
            rule.severity,
            rule.notification_channels,
            rule.cooldown_minutes
          ],
          (err) => {
            if (err) reject(err);
            else resolve();
          }
        );
      });
    }

    logger.info('[AlertSystem] Default alert rules created');
  }

  /**
   * Load alert rules into memory
   */
  async loadAlertRules() {
    return new Promise((resolve, reject) => {
      db.all(
        'SELECT * FROM alert_rules WHERE enabled = 1',
        [],
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }

          this.alertRules.clear();
          for (const row of rows) {
            this.alertRules.set(row.rule_id, {
              ...row,
              condition: JSON.parse(row.condition),
              notification_channels: JSON.parse(row.notification_channels)
            });
          }

          logger.info(`[AlertSystem] Loaded ${rows.length} alert rules`);
          resolve(rows.length);
        }
      );
    });
  }

  /**
   * Trigger alert based on anomaly
   * Enhanced with automated response protocols
   */
  async triggerAnomalyAlert(anomaly) {
    // Find matching rules
    const matchingRules = [];
    for (const [ruleId, rule] of this.alertRules.entries()) {
      if (rule.rule_type === 'anomaly' && rule.condition.anomaly_type === anomaly.type) {
        matchingRules.push(rule);
      }
    }

    if (matchingRules.length === 0) {
      return;
    }

    // Create alerts for matching rules
    for (const rule of matchingRules) {
      // Check cooldown
      if (await this.isInCooldown(rule.rule_id, anomaly.affected_ip || anomaly.affected_user_id)) {
        logger.debug(`[AlertSystem] Alert ${rule.rule_id} in cooldown period`);
        continue;
      }

      const alertId = await this.createAlert({
        rule_id: rule.rule_id,
        severity: rule.severity,
        title: rule.name,
        description: anomaly.description,
        affected_entity: anomaly.affected_user_id || anomaly.affected_ip,
        source_data: JSON.stringify({
          anomalyId: anomaly.anomaly_id,
          type: anomaly.type,
          confidence: anomaly.confidence,
          patternData: JSON.parse(anomaly.pattern_data || '{}')
        })
      });

      // Send notifications
      await this.sendNotifications(alertId, rule.notification_channels);

      // Execute automated response protocols (CWE-778)
      await this.executeResponseProtocols('anomaly', {
        anomaly_type: anomaly.type,
        severity: rule.severity
      }, {
        alertId,
        ip_address: anomaly.affected_ip,
        user_id: anomaly.affected_user_id,
        confidence: anomaly.confidence
      });
    }
  }

  /**
   * Execute automated response protocols
   * CWE-778: Logs all protocol executions
   */
  async executeResponseProtocols(triggerType, triggerEvent, context = {}) {
    try {
      const result = await responseProtocolManager.executeProtocols(triggerType, triggerEvent, context);
      
      if (result.executed) {
        logger.warn(`[AlertSystem] Executed ${result.protocols.length} response protocol(s)`);
        
        // Log protocol execution (CWE-778)
        logAggregator.aggregate('alert_system', 'warn', 'security', 'Response protocols executed', {
          triggerType,
          protocolsExecuted: result.protocols.length,
          protocols: result.protocols.map(p => ({
            protocolId: p.protocolId,
            protocolName: p.protocolName,
            status: p.status,
            actionsExecuted: p.actionsExecuted
          })),
          context
        });
      }

      return result;
    } catch (error) {
      logger.error('[AlertSystem] Response protocol execution failed:', error);
      
      // Log execution failure (CWE-778)
      logAggregator.aggregate('alert_system', 'error', 'security', 'Response protocol execution failed', {
        triggerType,
        error: error.message,
        context
      });
    }
  }

  /**
   * Trigger alert based on threshold
   */
  async triggerThresholdAlert(metric, value) {
    for (const [ruleId, rule] of this.alertRules.entries()) {
      if (rule.rule_type !== 'threshold' || rule.condition.metric !== metric) {
        continue;
      }

      // Evaluate condition
      const passed = this.evaluateCondition(rule.condition, value);
      if (!passed) continue;

      // Check cooldown
      if (await this.isInCooldown(rule.rule_id, metric)) {
        continue;
      }

      const alertId = await this.createAlert({
        rule_id: rule.rule_id,
        severity: rule.severity,
        title: rule.name,
        description: `${rule.description}: ${metric} = ${value}`,
        affected_entity: metric,
        source_data: JSON.stringify({ metric, value, threshold: rule.condition.value })
      });

      await this.sendNotifications(alertId, rule.notification_channels);
    }
  }

  /**
   * Evaluate threshold condition
   */
  evaluateCondition(condition, value) {
    const { operator, value: threshold } = condition;

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

  /**
   * Check if alert is in cooldown period
   */
  async isInCooldown(ruleId, affectedEntity) {
    const rule = this.alertRules.get(ruleId);
    if (!rule) return false;

    const cooldownMinutes = rule.cooldown_minutes || 10;
    const cooldownTime = new Date(Date.now() - cooldownMinutes * 60 * 1000).toISOString();

    return new Promise((resolve, reject) => {
      db.get(
        `SELECT COUNT(*) as count FROM security_alerts
         WHERE rule_id = ?
           AND affected_entity = ?
           AND created_at >= ?`,
        [ruleId, affectedEntity, cooldownTime],
        (err, row) => {
          if (err) reject(err);
          else resolve(row.count > 0);
        }
      );
    });
  }

  /**
   * Create alert
   */
  async createAlert(details) {
    const alertId = `ALERT-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    return new Promise((resolve, reject) => {
      db.run(
        `INSERT INTO security_alerts
         (alert_id, rule_id, severity, title, description, affected_entity, source_data)
         VALUES (?, ?, ?, ?, ?, ?, ?)`,
        [
          alertId,
          details.rule_id,
          details.severity,
          details.title,
          details.description,
          details.affected_entity,
          details.source_data
        ],
        (err) => {
          if (err) {
            reject(err);
          } else {
            logger.warn(`[AlertSystem] Alert triggered: ${alertId} - ${details.title} (${details.severity})`);
            
            // Log to aggregated logs
            logAggregator.aggregate('alert_system', 'warn', 'security', details.title, {
              alertId,
              ruleId: details.rule_id,
              severity: details.severity,
              affectedEntity: details.affected_entity
            });

            // Emit event for real-time notifications
            this.emit('alert', {
              alertId,
              ...details,
              timestamp: new Date().toISOString()
            });

            this.activeAlerts.set(alertId, details);
            resolve(alertId);
          }
        }
      );
    });
  }

  /**
   * Send notifications through configured channels
   */
  async sendNotifications(alertId, channels) {
    for (const channel of channels) {
      try {
        switch (channel) {
          case 'in_app':
            await this.sendInAppNotification(alertId);
            break;
          case 'email':
            await this.sendEmailNotification(alertId);
            break;
          case 'webhook':
            await this.sendWebhookNotification(alertId);
            break;
          default:
            logger.debug(`[AlertSystem] Unknown notification channel: ${channel}`);
        }
      } catch (error) {
        logger.error(`[AlertSystem] Failed to send ${channel} notification:`, error);
      }
    }

    // Mark notification as sent
    await new Promise((resolve) => {
      db.run(
        'UPDATE security_alerts SET notification_sent = 1 WHERE alert_id = ?',
        [alertId],
        () => resolve()
      );
    });
  }

  /**
   * Send in-app notification (emit event)
   */
  async sendInAppNotification(alertId) {
    const alert = await this.getAlert(alertId);
    if (!alert) return;

    this.emit('notification', {
      type: 'security_alert',
      alertId: alert.alert_id,
      severity: alert.severity,
      title: alert.title,
      description: alert.description,
      timestamp: alert.created_at
    });

    logger.debug(`[AlertSystem] In-app notification sent: ${alertId}`);
  }

  /**
   * Send email notification (placeholder)
   */
  async sendEmailNotification(alertId) {
    const alert = await this.getAlert(alertId);
    if (!alert) return;

    // TODO: Implement email sending (nodemailer)
    logger.info(`[AlertSystem] Email notification (stub): ${alertId}`);
    
    // For now, just log it
    logger.info(`[AlertSystem] Email would be sent for alert ${alertId}: ${alert.title}`);
  }

  /**
   * Send webhook notification (placeholder)
   */
  async sendWebhookNotification(alertId) {
    const alert = await this.getAlert(alertId);
    if (!alert) return;

    // TODO: Implement webhook HTTP POST
    logger.info(`[AlertSystem] Webhook notification (stub): ${alertId}`);
  }

  /**
   * Get alert by ID
   */
  async getAlert(alertId) {
    return new Promise((resolve, reject) => {
      db.get(
        'SELECT * FROM security_alerts WHERE alert_id = ?',
        [alertId],
        (err, row) => {
          if (err) reject(err);
          else resolve(row);
        }
      );
    });
  }

  /**
   * Get active alerts
   */
  async getAlerts(filters = {}) {
    const {
      status = 'active',
      severity,
      limit = 100,
      offset = 0
    } = filters;

    let whereClause = ['status = ?'];
    let params = [status];

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

    params.push(limit, offset);

    return new Promise((resolve, reject) => {
      db.all(
        `SELECT * FROM security_alerts
         WHERE ${whereClause.join(' AND ')}
         ORDER BY created_at DESC
         LIMIT ? OFFSET ?`,
        params,
        (err, rows) => {
          if (err) reject(err);
          else resolve(rows);
        }
      );
    });
  }

  /**
   * Acknowledge alert
   */
  async acknowledgeAlert(alertId, userId, notes = '') {
    return new Promise((resolve, reject) => {
      db.run(
        `UPDATE security_alerts
         SET status = 'acknowledged',
             acknowledged_at = CURRENT_TIMESTAMP,
             acknowledged_by = ?
         WHERE alert_id = ?`,
        [userId, alertId],
        (err) => {
          if (err) {
            reject(err);
          } else {
            logger.info(`[AlertSystem] Alert acknowledged: ${alertId} by user ${userId}`);
            
            logAggregator.aggregate('alert_system', 'info', 'security', 'Alert acknowledged', {
              alertId,
              userId,
              notes
            });

            this.emit('alert_acknowledged', { alertId, userId });
            resolve();
          }
        }
      );
    });
  }

  /**
   * Resolve alert
   */
  async resolveAlert(alertId, userId, notes) {
    return new Promise((resolve, reject) => {
      db.run(
        `UPDATE security_alerts
         SET status = 'resolved',
             resolved_at = CURRENT_TIMESTAMP,
             resolved_by = ?,
             resolution_notes = ?
         WHERE alert_id = ?`,
        [userId, notes, alertId],
        (err) => {
          if (err) {
            reject(err);
          } else {
            logger.info(`[AlertSystem] Alert resolved: ${alertId} by user ${userId}`);
            
            logAggregator.aggregate('alert_system', 'info', 'security', 'Alert resolved', {
              alertId,
              userId,
              notes
            });

            this.activeAlerts.delete(alertId);
            this.emit('alert_resolved', { alertId, userId });
            resolve();
          }
        }
      );
    });
  }

  /**
   * Get alert statistics
   */
  async getStatistics() {
    return new Promise((resolve, reject) => {
      db.all(
        `SELECT 
          status,
          severity,
          COUNT(*) as count
         FROM security_alerts
         WHERE created_at >= datetime('now', '-24 hours')
         GROUP BY status, severity`,
        [],
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }

          const stats = {
            total: 0,
            byStatus: {},
            bySeverity: {}
          };

          for (const row of rows) {
            stats.total += row.count;
            
            if (!stats.byStatus[row.status]) {
              stats.byStatus[row.status] = 0;
            }
            stats.byStatus[row.status] += row.count;

            if (!stats.bySeverity[row.severity]) {
              stats.bySeverity[row.severity] = 0;
            }
            stats.bySeverity[row.severity] += row.count;
          }

          resolve(stats);
        }
      );
    });
  }
}

// Create singleton instance
const alertSystem = new AlertSystem();

// Connect security intelligence to alert system
const securityIntelligence = require('./securityIntelligence');

// Listen for anomalies and trigger alerts
setInterval(async () => {
  try {
    const anomalies = await securityIntelligence.getAnomalies({ status: 'open', limit: 50 });
    for (const anomaly of anomalies) {
      await alertSystem.triggerAnomalyAlert(anomaly);
    }

    // Check threat score
    const threatScore = securityIntelligence.threatScore;
    if (threatScore >= 80) {
      await alertSystem.triggerThresholdAlert('threat_score', threatScore);
    }
  } catch (error) {
    logger.error('[AlertSystem] Error checking for alerts:', error);
  }
}, 60000); // Check every minute

module.exports = alertSystem;
