const express = require('express');
const router = express.Router();
const { authenticate } = require('../middleware/auth');
const { requirePermission } = require('../middleware/rbac');
const { readLimiter } = require('../middleware/rateLimiter');
const { db } = require('../database/db');
const logger = require('../utils/logger');
const fs = require('fs').promises;
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');

const execPromise = promisify(exec);

/**
 * Security Monitoring & Dependency Management
 * Provides comprehensive security status and vulnerability tracking
 */

// Get comprehensive security status
router.get('/status', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
  try {
    const securityStatus = {
      timestamp: new Date().toISOString(),
      dependencies: await checkDependencies(),
      vulnerabilities: await checkVulnerabilities(),
      securityHeaders: await checkSecurityHeaders(),
      auditSummary: await getAuditSummary(),
      systemHealth: await getSystemHealth()
    };

    res.json(securityStatus);
  } catch (error) {
    logger.error('Error fetching security status:', error);
    res.status(500).json({ error: 'Failed to fetch security status' });
  }
});

// Check dependencies for updates
async function checkDependencies() {
  try {
    const backendPackage = JSON.parse(
      await fs.readFile(path.join(__dirname, '../package.json'), 'utf8')
    );

    const frontendPackage = JSON.parse(
      await fs.readFile(path.join(__dirname, '../../frontend/package.json'), 'utf8')
    );

    return {
      backend: {
        dependencies: Object.keys(backendPackage.dependencies || {}).length,
        devDependencies: Object.keys(backendPackage.devDependencies || {}).length,
        lastChecked: new Date().toISOString()
      },
      frontend: {
        dependencies: Object.keys(frontendPackage.dependencies || {}).length,
        devDependencies: Object.keys(frontendPackage.devDependencies || {}).length,
        lastChecked: new Date().toISOString()
      }
    };
  } catch (error) {
    logger.error('Error checking dependencies:', error);
    return { error: 'Unable to check dependencies' };
  }
}

// Check for known vulnerabilities
async function checkVulnerabilities() {
  try {
    // Run npm audit in both backend and frontend
    const backendAudit = await runNpmAudit('backend');
    const frontendAudit = await runNpmAudit('frontend');

    return {
      backend: backendAudit,
      frontend: frontendAudit,
      lastScanned: new Date().toISOString()
    };
  } catch (error) {
    logger.error('Error checking vulnerabilities:', error);
    return { error: 'Unable to scan for vulnerabilities' };
  }
}

async function runNpmAudit(project) {
  try {
    const projectPath = project === 'backend' 
      ? path.join(__dirname, '..')
      : path.join(__dirname, '../../frontend');

    const { stdout } = await execPromise(`cd ${projectPath} && npm audit --json`, {
      timeout: 30000
    });

    const auditData = JSON.parse(stdout);
    
    return {
      total: auditData.metadata?.vulnerabilities?.total || 0,
      critical: auditData.metadata?.vulnerabilities?.critical || 0,
      high: auditData.metadata?.vulnerabilities?.high || 0,
      moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
      low: auditData.metadata?.vulnerabilities?.low || 0,
      info: auditData.metadata?.vulnerabilities?.info || 0
    };
  } catch (error) {
    // npm audit returns non-zero exit code when vulnerabilities are found
    if (error.stdout) {
      try {
        const auditData = JSON.parse(error.stdout);
        return {
          total: auditData.metadata?.vulnerabilities?.total || 0,
          critical: auditData.metadata?.vulnerabilities?.critical || 0,
          high: auditData.metadata?.vulnerabilities?.high || 0,
          moderate: auditData.metadata?.vulnerabilities?.moderate || 0,
          low: auditData.metadata?.vulnerabilities?.low || 0,
          info: auditData.metadata?.vulnerabilities?.info || 0
        };
      } catch {
        return { error: 'Unable to parse audit results' };
      }
    }
    return { error: error.message };
  }
}

// Check security headers configuration
async function checkSecurityHeaders() {
  return {
    helmet: {
      enabled: true,
      features: [
        'Content-Security-Policy',
        'X-Content-Type-Options',
        'X-Frame-Options',
        'X-XSS-Protection',
        'Strict-Transport-Security',
        'Referrer-Policy'
      ]
    },
    csp: {
      enabled: true,
      mode: process.env.NODE_ENV === 'production' ? 'enforcing' : 'report-only'
    },
    cors: {
      enabled: true
    }
  };
}

// Get security audit summary
async function getAuditSummary() {
  return new Promise((resolve, reject) => {
    db.all(
      `SELECT 
        action,
        result,
        COUNT(*) as count,
        MAX(timestamp) as last_occurrence
       FROM security_audit_log
       WHERE timestamp > datetime('now', '-7 days')
       GROUP BY action, result
       ORDER BY count DESC
       LIMIT 20`,
      [],
      (err, rows) => {
        if (err) {
          logger.error('Error fetching audit summary:', err);
          resolve({ error: 'Unable to fetch audit summary' });
        } else {
          resolve(rows || []);
        }
      }
    );
  });
}

// Get system health metrics
async function getSystemHealth() {
  return new Promise((resolve, reject) => {
    Promise.all([
      // Active sessions count
      new Promise((res) => {
        db.get('SELECT COUNT(*) as count FROM sessions WHERE expires_at > ?', 
          [new Date().toISOString()], 
          (err, row) => res(row?.count || 0)
        );
      }),
      // Failed login attempts in last hour
      new Promise((res) => {
        db.get(
          `SELECT COUNT(*) as count FROM security_audit_log 
           WHERE action = 'login' AND result = 'failed' 
           AND timestamp > datetime('now', '-1 hour')`,
          [],
          (err, row) => res(row?.count || 0)
        );
      }),
      // Locked accounts
      new Promise((res) => {
        db.get(
          'SELECT COUNT(*) as count FROM users WHERE locked_until > ?',
          [new Date().toISOString()],
          (err, row) => res(row?.count || 0)
        );
      }),
      // Total users
      new Promise((res) => {
        db.get('SELECT COUNT(*) as count FROM users', [], (err, row) => res(row?.count || 0));
      })
    ]).then(([activeSessions, failedLogins, lockedAccounts, totalUsers]) => {
      resolve({
        activeSessions,
        failedLogins,
        lockedAccounts,
        totalUsers,
        timestamp: new Date().toISOString()
      });
    });
  });
}

// Get detailed vulnerability report
router.get('/vulnerabilities/detailed', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
  try {
    const backendPath = path.join(__dirname, '..');
    const frontendPath = path.join(__dirname, '../../frontend');

    const backendAudit = await execPromise(`cd ${backendPath} && npm audit --json`, {
      timeout: 30000
    }).catch(e => ({ stdout: e.stdout }));

    const frontendAudit = await execPromise(`cd ${frontendPath} && npm audit --json`, {
      timeout: 30000
    }).catch(e => ({ stdout: e.stdout }));

    const backendData = JSON.parse(backendAudit.stdout || '{}');
    const frontendData = JSON.parse(frontendAudit.stdout || '{}');

    res.json({
      backend: {
        vulnerabilities: backendData.vulnerabilities || {},
        metadata: backendData.metadata || {}
      },
      frontend: {
        vulnerabilities: frontendData.vulnerabilities || {},
        metadata: frontendData.metadata || {}
      },
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    logger.error('Error fetching detailed vulnerabilities:', error);
    res.status(500).json({ error: 'Failed to fetch vulnerability details' });
  }
});

// Get security audit log with filtering
router.get('/audit-log', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
  try {
    const { action, result, userId, startDate, endDate, limit = 100, offset = 0 } = req.query;

    let query = 'SELECT * FROM security_audit_log WHERE 1=1';
    const params = [];

    if (action) {
      query += ' AND action = ?';
      params.push(action);
    }

    if (result) {
      query += ' AND result = ?';
      params.push(result);
    }

    if (userId) {
      query += ' AND user_id = ?';
      params.push(userId);
    }

    if (startDate) {
      query += ' AND timestamp >= ?';
      params.push(startDate);
    }

    if (endDate) {
      query += ' AND timestamp <= ?';
      params.push(endDate);
    }

    query += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?';
    params.push(parseInt(limit), parseInt(offset));

    db.all(query, params, (err, rows) => {
      if (err) {
        logger.error('Error fetching audit log:', err);
        return res.status(500).json({ error: 'Failed to fetch audit log' });
      }

      // Get total count for pagination
      let countQuery = 'SELECT COUNT(*) as total FROM security_audit_log WHERE 1=1';
      const countParams = params.slice(0, -2); // Remove limit and offset

      db.get(countQuery, countParams, (err, countRow) => {
        if (err) {
          logger.error('Error counting audit log:', err);
          return res.json({ logs: rows || [], total: 0 });
        }

        res.json({
          logs: rows || [],
          total: countRow?.total || 0,
          limit: parseInt(limit),
          offset: parseInt(offset)
        });
      });
    });
  } catch (error) {
    logger.error('Error fetching audit log:', error);
    res.status(500).json({ error: 'Failed to fetch audit log' });
  }
});

// Export audit log
router.get('/audit-log/export', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
  try {
    const { format = 'json', startDate, endDate } = req.query;

    let query = 'SELECT * FROM security_audit_log WHERE 1=1';
    const params = [];

    if (startDate) {
      query += ' AND timestamp >= ?';
      params.push(startDate);
    }

    if (endDate) {
      query += ' AND timestamp <= ?';
      params.push(endDate);
    }

    query += ' ORDER BY timestamp DESC';

    db.all(query, params, (err, rows) => {
      if (err) {
        logger.error('Error exporting audit log:', err);
        return res.status(500).json({ error: 'Failed to export audit log' });
      }

      if (format === 'csv') {
        const csv = convertToCSV(rows);
        res.setHeader('Content-Type', 'text/csv');
        res.setHeader('Content-Disposition', `attachment; filename=security-audit-${Date.now()}.csv`);
        res.send(csv);
      } else {
        res.setHeader('Content-Type', 'application/json');
        res.setHeader('Content-Disposition', `attachment; filename=security-audit-${Date.now()}.json`);
        res.json(rows);
      }
    });
  } catch (error) {
    logger.error('Error exporting audit log:', error);
    res.status(500).json({ error: 'Failed to export audit log' });
  }
});

function convertToCSV(data) {
  if (!data || data.length === 0) return '';

  const headers = Object.keys(data[0]);
  const csvRows = [headers.join(',')];

  for (const row of data) {
    const values = headers.map(header => {
      const value = row[header];
      return typeof value === 'string' && value.includes(',') 
        ? `"${value}"` 
        : value;
    });
    csvRows.push(values.join(','));
  }

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

// Get security recommendations
router.get('/recommendations', authenticate, requirePermission('security.view_audit'), readLimiter, async (req, res) => {
  try {
    const recommendations = [];

    // Check for locked accounts
    const lockedAccounts = await new Promise((resolve) => {
      db.get(
        'SELECT COUNT(*) as count FROM users WHERE locked_until > ?',
        [new Date().toISOString()],
        (err, row) => resolve(row?.count || 0)
      );
    });

    if (lockedAccounts > 0) {
      recommendations.push({
        severity: 'warning',
        category: 'account_security',
        title: 'Locked Accounts',
        description: `${lockedAccounts} account(s) are currently locked due to failed login attempts`,
        action: 'Review locked accounts and consider unlocking legitimate users'
      });
    }

    // Check for users with old passwords
    const oldPasswords = await new Promise((resolve) => {
      db.all(
        `SELECT username, password_changed_at FROM users 
         WHERE password_changed_at < datetime('now', '-90 days')`,
        [],
        (err, rows) => resolve(rows || [])
      );
    });

    if (oldPasswords.length > 0) {
      recommendations.push({
        severity: 'info',
        category: 'password_policy',
        title: 'Old Passwords',
        description: `${oldPasswords.length} user(s) haven't changed their password in over 90 days`,
        action: 'Encourage users to update their passwords regularly'
      });
    }

    // Check for recent failed logins
    const recentFailures = await new Promise((resolve) => {
      db.get(
        `SELECT COUNT(*) as count FROM security_audit_log 
         WHERE action = 'login' AND result = 'failed' 
         AND timestamp > datetime('now', '-1 hour')`,
        [],
        (err, row) => resolve(row?.count || 0)
      );
    });

    if (recentFailures > 10) {
      recommendations.push({
        severity: 'high',
        category: 'threat_detection',
        title: 'High Failed Login Rate',
        description: `${recentFailures} failed login attempts in the last hour`,
        action: 'Investigate potential brute-force attack'
      });
    }

    // Check for users without 2FA
    const no2FA = await new Promise((resolve) => {
      db.get(
        'SELECT COUNT(*) as count FROM users WHERE two_factor_secret IS NULL',
        [],
        (err, row) => resolve(row?.count || 0)
      );
    });

    if (no2FA > 0) {
      recommendations.push({
        severity: 'warning',
        category: 'authentication',
        title: 'Two-Factor Authentication',
        description: `${no2FA} user(s) don't have 2FA enabled`,
        action: 'Encourage users to enable two-factor authentication'
      });
    }

    res.json({
      recommendations,
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    logger.error('Error generating recommendations:', error);
    res.status(500).json({ error: 'Failed to generate recommendations' });
  }
});

module.exports = router;
