/**
 * Security Audit Logger
 * Tracks security-related events for compliance and forensics
 * Integrated with SIEM for centralized log aggregation
 */

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

class SecurityAuditLogger {
  /**
   * Log authentication events
   */
  static async logAuthEvent(eventType, userId, details = {}) {
    const event = {
      event_type: eventType,
      user_id: userId,
      ip_address: details.ip || 'unknown',
      user_agent: details.userAgent || 'unknown',
      success: details.success !== false,
      failure_reason: details.failureReason || null,
      metadata: JSON.stringify(details.metadata || {}),
      timestamp: new Date().toISOString()
    };

    try {
      await new Promise((resolve, reject) => {
        db.run(
          `INSERT INTO security_audit_log (event_type, user_id, ip_address, user_agent, success, failure_reason, metadata, timestamp)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
          [event.event_type, event.user_id, event.ip_address, event.user_agent, event.success ? 1 : 0, event.failure_reason, event.metadata, event.timestamp],
          (err) => err ? reject(err) : resolve()
        );
      });

      logger.info(`[SECURITY] ${eventType}: user=${userId}, ip=${event.ip_address}, success=${event.success}`);
      
      // Aggregate to SIEM
      const level = event.success ? 'info' : 'warn';
      const source = eventType.startsWith('LOGIN') || eventType.includes('PASSWORD') ? 'authentication' : 'security_audit';
      logAggregator.aggregate(source, level, 'authentication', `${eventType}: ${event.success ? 'success' : 'failure'}`, {
        userId: event.user_id,
        ip: event.ip_address,
        userAgent: event.user_agent,
        failureReason: event.failure_reason,
        metadata: details.metadata || {}
      });
    } catch (error) {
      logger.error('Failed to log security event:', error);
    }
  }

  /**
   * Log login attempt
   */
  static async logLoginAttempt(username, success, details = {}) {
    return this.logAuthEvent('LOGIN_ATTEMPT', null, {
      success,
      failureReason: success ? null : (details.reason || 'Invalid credentials'),
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { username }
    });
  }

  /**
   * Log successful login
   */
  static async logLoginSuccess(userId, details = {}) {
    return this.logAuthEvent('LOGIN_SUCCESS', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { twoFactorUsed: details.twoFactorUsed || false }
    });
  }

  /**
   * Log failed login
   */
  static async logLoginFailure(username, reason, details = {}) {
    return this.logAuthEvent('LOGIN_FAILURE', null, {
      success: false,
      failureReason: reason,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { username }
    });
  }

  /**
   * Log account lockout
   */
  static async logAccountLockout(userId, details = {}) {
    return this.logAuthEvent('ACCOUNT_LOCKOUT', userId, {
      success: false,
      failureReason: 'Too many failed login attempts',
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { failedAttempts: details.failedAttempts }
    });
  }

  /**
   * Log password change
   */
  static async logPasswordChange(userId, details = {}) {
    return this.logAuthEvent('PASSWORD_CHANGE', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { forced: details.forced || false, expired: details.expired || false }
    });
  }

  /**
   * Log 2FA events
   */
  static async log2FAEvent(eventType, userId, success, details = {}) {
    return this.logAuthEvent(eventType, userId, {
      success,
      failureReason: success ? null : (details.reason || 'Invalid code'),
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: details.metadata || {}
    });
  }

  /**
   * Log session events
   */
  static async logSessionEvent(eventType, userId, details = {}) {
    return this.logAuthEvent(eventType, userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: { sessionId: details.sessionId }
    });
  }

  /**
   * Log privilege escalation
   */
  static async logPrivilegeEscalation(userId, details = {}) {
    return this.logAuthEvent('PRIVILEGE_ESCALATION', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        fromRole: details.fromRole,
        toRole: details.toRole,
        grantedBy: details.grantedBy
      }
    });
  }

  /**
   * Get recent security events for a user
   */
  static async getUserSecurityEvents(userId, limit = 50) {
    return new Promise((resolve, reject) => {
      db.all(
        `SELECT * FROM security_audit_log 
         WHERE user_id = ? 
         ORDER BY timestamp DESC 
         LIMIT ?`,
        [userId, limit],
        (err, rows) => err ? reject(err) : resolve(rows)
      );
    });
  }

  /**
   * Get failed login attempts for an IP
   */
  static async getFailedAttemptsForIP(ipAddress, timeWindowMinutes = 30) {
    const cutoffTime = new Date(Date.now() - timeWindowMinutes * 60 * 1000).toISOString();
    
    return new Promise((resolve, reject) => {
      db.get(
        `SELECT COUNT(*) as count 
         FROM security_audit_log 
         WHERE event_type IN ('LOGIN_FAILURE', 'LOGIN_ATTEMPT') 
         AND success = 0 
         AND ip_address = ? 
         AND timestamp > ?`,
        [ipAddress, cutoffTime],
        (err, row) => err ? reject(err) : resolve(row.count || 0)
      );
    });
  }

  /**
   * Get failed login attempts for a user
   */
  static async getFailedAttemptsForUser(username, timeWindowMinutes = 30) {
    const cutoffTime = new Date(Date.now() - timeWindowMinutes * 60 * 1000).toISOString();
    
    return new Promise((resolve, reject) => {
      db.get(
        `SELECT COUNT(*) as count 
         FROM security_audit_log 
         WHERE event_type IN ('LOGIN_FAILURE', 'LOGIN_ATTEMPT') 
         AND success = 0 
         AND metadata LIKE ? 
         AND timestamp > ?`,
        [`%"username":"${username}"%`, cutoffTime],
        (err, row) => err ? reject(err) : resolve(row.count || 0)
      );
    });
  }

  /**
   * Clear old audit logs (data retention)
   */
  static async cleanupOldLogs(retentionDays = 90) {
    const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000).toISOString();
    
    return new Promise((resolve, reject) => {
      db.run(
        'DELETE FROM security_audit_log WHERE timestamp < ?',
        [cutoffDate],
        function(err) {
          if (err) reject(err);
          else {
            logger.info(`Cleaned up ${this.changes} old security audit logs`);
            resolve(this.changes);
          }
        }
      );
    });
  }

  /**
   * CWE-778: Log token issuance (JWT, OAuth, etc.)
   * Includes relevant metadata such as client ID, IP address, device info
   */
  static async logTokenIssuance(userId, tokenType, details = {}) {
    return this.logAuthEvent('TOKEN_ISSUED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        tokenType: tokenType, // 'JWT', 'REFRESH', 'TEMP_2FA', 'OAUTH'
        clientId: details.clientId,
        deviceInfo: details.deviceInfo || this.extractDeviceInfo(details.userAgent),
        expiresIn: details.expiresIn,
        purpose: details.purpose, // 'login', '2fa', 'registration', 'password_reset'
        scope: details.scope,
        grantType: details.grantType
      }
    });
  }

  /**
   * CWE-778: Log token refresh
   */
  static async logTokenRefresh(userId, details = {}) {
    return this.logAuthEvent('TOKEN_REFRESHED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        tokenType: details.tokenType || 'JWT',
        clientId: details.clientId,
        deviceInfo: details.deviceInfo || this.extractDeviceInfo(details.userAgent),
        oldTokenExpiry: details.oldTokenExpiry,
        newTokenExpiry: details.newTokenExpiry
      }
    });
  }

  /**
   * CWE-778: Log token revocation
   */
  static async logTokenRevocation(userId, reason, details = {}) {
    return this.logAuthEvent('TOKEN_REVOKED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        reason: reason, // 'logout', 'password_change', 'security_breach', 'admin_action'
        tokenType: details.tokenType || 'JWT',
        sessionId: details.sessionId,
        revokedBy: details.revokedBy // user_id of admin who revoked it
      }
    });
  }

  /**
   * CWE-778: Log privilege changes
   * Any activities where user's privilege level changes
   */
  static async logPrivilegeChange(userId, action, details = {}) {
    return this.logAuthEvent('PRIVILEGE_CHANGE', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        action: action, // 'role_assigned', 'role_removed', 'permission_granted', 'permission_revoked'
        previousRole: details.previousRole,
        newRole: details.newRole,
        previousPermissions: details.previousPermissions,
        newPermissions: details.newPermissions,
        changedBy: details.changedBy, // user_id of admin who made the change
        changedByUsername: details.changedByUsername,
        reason: details.reason,
        affectedUser: details.affectedUser // username of the user being changed
      }
    });
  }

  /**
   * CWE-778: Log permission grant
   */
  static async logPermissionGrant(userId, permission, details = {}) {
    return this.logAuthEvent('PERMISSION_GRANTED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        permission: permission,
        grantedBy: details.grantedBy,
        grantedByUsername: details.grantedByUsername,
        scope: details.scope,
        expiresAt: details.expiresAt
      }
    });
  }

  /**
   * CWE-778: Log permission revocation
   */
  static async logPermissionRevocation(userId, permission, details = {}) {
    return this.logAuthEvent('PERMISSION_REVOKED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        permission: permission,
        revokedBy: details.revokedBy,
        revokedByUsername: details.revokedByUsername,
        reason: details.reason
      }
    });
  }

  /**
   * CWE-778: Log user activation/deactivation
   */
  static async logAccountStatusChange(userId, newStatus, details = {}) {
    return this.logAuthEvent('ACCOUNT_STATUS_CHANGED', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        previousStatus: details.previousStatus,
        newStatus: newStatus, // 'active', 'inactive', 'suspended', 'locked'
        changedBy: details.changedBy,
        changedByUsername: details.changedByUsername,
        reason: details.reason
      }
    });
  }

  /**
   * CWE-778: Log administrative activities
   * For admin actions like user creation, deletion, unlock, force logout
   */
  static async logAdminActivity(adminId, action, details = {}) {
    return this.logAuthEvent('ADMIN_ACTIVITY', adminId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        action: action, // 'user_created', 'user_deleted', 'account_unlocked', 'password_reset', 'force_logout'
        targetUserId: details.targetUserId,
        targetUsername: details.targetUsername,
        adminUsername: details.adminUsername,
        changes: details.changes,
        reason: details.reason,
        deviceInfo: details.deviceInfo || this.extractDeviceInfo(details.userAgent)
      }
    });
  }

  /**
   * CWE-778: Log sensitive data access
   * For accessing user lists, settings, VPN configs, backups, etc.
   */
  static async logSensitiveDataAccess(userId, dataType, details = {}) {
    return this.logAuthEvent('SENSITIVE_DATA_ACCESS', userId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        dataType: dataType, // 'user_list', 'user_details', 'settings', 'vpn_configs', 'backups', 'audit_logs'
        accessMethod: details.accessMethod || 'view', // 'view', 'export', 'download'
        recordCount: details.recordCount,
        filters: details.filters,
        scope: details.scope, // 'own', 'all', 'specific'
        deviceInfo: details.deviceInfo || this.extractDeviceInfo(details.userAgent)
      }
    });
  }

  /**
   * Extract device info from user agent
   */
  static extractDeviceInfo(userAgent = '') {
    if (!userAgent) return { deviceType: 'unknown', os: 'unknown', browser: 'unknown' };

    const ua = userAgent.toLowerCase();
    
    // Device type
    let deviceType = 'desktop';
    if (/bot|crawler|spider/.test(ua)) deviceType = 'bot';
    else if (/mobile|android|iphone|ipod/.test(ua)) deviceType = 'mobile';
    else if (/tablet|ipad/.test(ua)) deviceType = 'tablet';

    // Operating System
    let os = 'unknown';
    if (/windows/.test(ua)) os = 'Windows';
    else if (/mac os|macos/.test(ua)) os = 'macOS';
    else if (/linux/.test(ua)) os = 'Linux';
    else if (/android/.test(ua)) os = 'Android';
    else if (/ios|iphone|ipad/.test(ua)) os = 'iOS';

    // Browser
    let browser = 'unknown';
    if (/firefox/.test(ua)) browser = 'Firefox';
    else if (/chrome/.test(ua) && !/edge|edg/.test(ua)) browser = 'Chrome';
    else if (/safari/.test(ua) && !/chrome/.test(ua)) browser = 'Safari';
    else if (/edge|edg/.test(ua)) browser = 'Edge';
    else if (/opera|opr/.test(ua)) browser = 'Opera';

    return { deviceType, os, browser };
  }

  /**
   * Get comprehensive audit statistics
   */
  static async getAuditStatistics(timeRangeDays = 30) {
    const cutoffDate = new Date(Date.now() - timeRangeDays * 24 * 60 * 60 * 1000).toISOString();
    
    return new Promise((resolve, reject) => {
      db.all(
        `SELECT 
          event_type,
          success,
          COUNT(*) as count,
          COUNT(DISTINCT user_id) as unique_users,
          COUNT(DISTINCT ip_address) as unique_ips
        FROM security_audit_log
        WHERE timestamp > ?
        GROUP BY event_type, success
        ORDER BY count DESC`,
        [cutoffDate],
        (err, rows) => err ? reject(err) : resolve(rows)
      );
    });
  }

  /**
   * Log system events (startup, shutdown, cleanup, etc.)
   */
  static async logSystemEvent(eventType, success, details = {}) {
    return this.logAuthEvent(`SYSTEM_${eventType.toUpperCase()}`, null, {
      success,
      failureReason: success ? null : (details.error || 'System event failed'),
      metadata: details
    });
  }

  /**
   * Log security incidents (tampering, breaches, etc.)
   */
  static async logSecurityIncident(incidentType, details = {}) {
    const event = {
      event_type: `SECURITY_INCIDENT_${incidentType.toUpperCase()}`,
      user_id: details.userId || null,
      ip_address: details.ip || 'system',
      user_agent: details.userAgent || 'system',
      success: false,
      failure_reason: `Security incident: ${incidentType}`,
      metadata: JSON.stringify(details),
      timestamp: new Date().toISOString()
    };

    try {
      await new Promise((resolve, reject) => {
        db.run(
          `INSERT INTO security_audit_log (event_type, user_id, ip_address, user_agent, success, failure_reason, metadata, timestamp)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
          [event.event_type, event.user_id, event.ip_address, event.user_agent, 0, event.failure_reason, event.metadata, event.timestamp],
          (err) => err ? reject(err) : resolve()
        );
      });

      logger.error(`[SECURITY INCIDENT] ${incidentType}: ${JSON.stringify(details)}`);
      
      // Aggregate to SIEM with CRITICAL level
      logAggregator.aggregate('security_audit', 'critical', 'security_incident', `Security incident: ${incidentType}`, {
        incidentType,
        ...details
      });
    } catch (error) {
      logger.error('Failed to log security incident:', error);
    }
  }

  /**
   * Log admin activities (user management, config changes, etc.)
   */
  static async logAdminActivity(adminId, action, details = {}) {
    return this.logAuthEvent(`ADMIN_${action.toUpperCase()}`, adminId, {
      success: true,
      ip: details.ip,
      userAgent: details.userAgent,
      metadata: {
        action,
        target: details.target || details.targetUserId,
        changes: details.changes,
        ...details
      }
    });
  }
}

module.exports = SecurityAuditLogger;
