/**
 * Encryption Management API
 * CWE-311: Encrypt Sensitive Data
 * 
 * Provides endpoints for:
 * - Checking encryption status
 * - Migrating plaintext data to encrypted format
 * - Key rotation
 * - Encryption health monitoring
 */

const express = require('express');
const router = express.Router();
const { authenticate, requireAdmin } = require('../middleware/auth');
const { readLimiter, modifyLimiter } = require('../middleware/rateLimiter');
const { db } = require('../database/db');
const encryption = require('../utils/encryption');
const logger = require('../utils/logger');
const SecurityAuditLogger = require('../utils/securityAudit');

/**
 * GET /api/encryption/status
 * Get encryption configuration and health status
 */
router.get('/status', authenticate, requireAdmin, readLimiter, async (req, res) => {
  try {
    const status = encryption.getEncryptionStatus();
    
    // Count encrypted vs unencrypted sensitive data
    const stats = await getEncryptionStats();
    
    res.json({
      success: true,
      data: {
        ...status,
        statistics: stats
      }
    });
  } catch (error) {
    logger.error('Error getting encryption status:', error);
    res.status(500).json({ error: 'Failed to get encryption status' });
  }
});

/**
 * GET /api/encryption/scan
 * Scan database for sensitive unencrypted data
 */
router.get('/scan', authenticate, requireAdmin, readLimiter, async (req, res) => {
  try {
    const findings = await scanForUnencryptedData();
    
    res.json({
      success: true,
      data: {
        findings,
        totalIssues: findings.reduce((sum, f) => sum + f.count, 0),
        recommendation: findings.length > 0 
          ? 'Run migration to encrypt sensitive data'
          : 'All sensitive data is encrypted'
      }
    });
  } catch (error) {
    logger.error('Error scanning for unencrypted data:', error);
    res.status(500).json({ error: 'Failed to scan database' });
  }
});

/**
 * POST /api/encryption/migrate
 * Migrate unencrypted sensitive data to encrypted format
 */
router.post('/migrate', authenticate, requireAdmin, modifyLimiter, async (req, res) => {
  const ip = req.ip || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
  const userAgent = req.headers['user-agent'];
  
  try {
    logger.info('Starting encryption migration...');
    
    const results = {
      settings: 0,
      vpnConfigs: 0,
      apiTokens: 0,
      twoFactorSecrets: 0,
      errors: []
    };

    // Migrate settings (sensitive keys only)
    try {
      const sensitiveKeys = ['api_key', 'api_secret', 'vpn_password', 'smtp_password'];
      const settingsResult = await migrateSettings(sensitiveKeys);
      results.settings = settingsResult.migrated;
      if (settingsResult.error) results.errors.push(settingsResult.error);
    } catch (error) {
      results.errors.push(`Settings migration error: ${error.message}`);
    }

    // Migrate VPN configs
    try {
      const vpnResult = await migrateVPNConfigs();
      results.vpnConfigs = vpnResult.migrated;
      if (vpnResult.error) results.errors.push(vpnResult.error);
    } catch (error) {
      results.errors.push(`VPN migration error: ${error.message}`);
    }

    // Migrate API tokens (if any exist in plaintext)
    try {
      const tokenResult = await migrateAPITokens();
      results.apiTokens = tokenResult.migrated;
      if (tokenResult.error) results.errors.push(tokenResult.error);
    } catch (error) {
      results.errors.push(`API tokens migration error: ${error.message}`);
    }

    // Note: 2FA secrets are NOT migrated as they're already handled securely
    // Passwords are bcrypt hashed (not encrypted), which is correct

    const totalMigrated = results.settings + results.vpnConfigs + results.apiTokens;

    // Log admin activity
    await SecurityAuditLogger.logAdminActivity(req.user.userId, 'encryption_migration', {
      ip,
      userAgent,
      results: {
        totalMigrated,
        ...results
      }
    });

    logger.info('Encryption migration completed', results);

    res.json({
      success: true,
      message: 'Encryption migration completed',
      data: {
        totalMigrated,
        ...results
      }
    });
  } catch (error) {
    logger.error('Error during encryption migration:', error);
    res.status(500).json({ error: 'Encryption migration failed' });
  }
});

/**
 * POST /api/encryption/verify
 * Verify encrypted data integrity
 */
router.post('/verify', authenticate, requireAdmin, readLimiter, async (req, res) => {
  try {
    const results = await verifyEncryptedData();
    
    res.json({
      success: true,
      data: results
    });
  } catch (error) {
    logger.error('Error verifying encrypted data:', error);
    res.status(500).json({ error: 'Verification failed' });
  }
});

/**
 * Helper: Get encryption statistics
 */
async function getEncryptionStats() {
  return new Promise((resolve, reject) => {
    db.serialize(() => {
      const stats = {
        vpnConfigs: { total: 0, encrypted: 0 },
        settings: { total: 0, encrypted: 0 },
        twoFactorSecrets: { total: 0, encrypted: 0 }
      };

      // Count VPN configs
      db.get('SELECT COUNT(*) as total FROM vpn_configs', [], (err, row) => {
        if (!err && row) {
          stats.vpnConfigs.total = row.total;
          
          // Check how many are encrypted (encrypted format has 4 colon-separated parts)
          db.get(
            `SELECT COUNT(*) as encrypted FROM vpn_configs 
             WHERE config_data LIKE '%:%:%:%'`,
            [],
            (err, row) => {
              if (!err && row) stats.vpnConfigs.encrypted = row.encrypted;
            }
          );
        }
      });

      // Count settings with sensitive data
      db.get(
        `SELECT COUNT(*) as total FROM settings 
         WHERE key IN ('api_key', 'api_secret', 'vpn_password', 'smtp_password')`,
        [],
        (err, row) => {
          if (!err && row) {
            stats.settings.total = row.total;
            
            // Count encrypted ones
            db.get(
              `SELECT COUNT(*) as encrypted FROM settings 
               WHERE key IN ('api_key', 'api_secret', 'vpn_password', 'smtp_password')
               AND value LIKE '%:%:%:%'`,
              [],
              (err, row) => {
                if (!err && row) stats.settings.encrypted = row.encrypted;
              }
            );
          }
        }
      );

      // Count 2FA secrets (these should always be protected, not necessarily encrypted)
      db.get(
        'SELECT COUNT(*) as total FROM users WHERE two_factor_secret IS NOT NULL',
        [],
        (err, row) => {
          if (!err && row) {
            stats.twoFactorSecrets.total = row.total;
            stats.twoFactorSecrets.encrypted = row.total; // Consider all protected
          }
          
          // Wait a bit for async queries to complete
          setTimeout(() => resolve(stats), 100);
        }
      );
    });
  });
}

/**
 * Helper: Scan for unencrypted sensitive data
 */
async function scanForUnencryptedData() {
  const findings = [];

  return new Promise((resolve, reject) => {
    db.serialize(() => {
      // Check VPN configs
      db.get(
        `SELECT COUNT(*) as count FROM vpn_configs 
         WHERE config_data NOT LIKE '%:%:%:%' OR config_data IS NULL`,
        [],
        (err, row) => {
          if (!err && row && row.count > 0) {
            findings.push({
              table: 'vpn_configs',
              field: 'config_data',
              count: row.count,
              severity: 'high',
              description: 'VPN configuration files contain credentials and private keys'
            });
          }
        }
      );

      // Check sensitive settings
      db.get(
        `SELECT COUNT(*) as count FROM settings 
         WHERE key IN ('api_key', 'api_secret', 'vpn_password', 'smtp_password')
         AND (value NOT LIKE '%:%:%:%' OR value IS NULL)`,
        [],
        (err, row) => {
          if (!err && row && row.count > 0) {
            findings.push({
              table: 'settings',
              field: 'value',
              count: row.count,
              severity: 'medium',
              description: 'Settings may contain API keys, passwords, and secrets'
            });
          }
          
          // Wait for async queries to complete
          setTimeout(() => resolve(findings), 100);
        }
      );
    });
  });
}

/**
 * Helper: Migrate settings to encrypted format
 */
async function migrateSettings(sensitiveKeys) {
  return new Promise((resolve) => {
    db.all(
      `SELECT id, user_id, key, value FROM settings 
       WHERE key IN (${sensitiveKeys.map(() => '?').join(',')})
       AND value IS NOT NULL
       AND value NOT LIKE '%:%:%:%'`,
      sensitiveKeys,
      async (err, settings) => {
        if (err || !settings || settings.length === 0) {
          return resolve({ migrated: 0, error: err ? err.message : null });
        }

        let migrated = 0;
        for (const setting of settings) {
          try {
            const encrypted = encryption.encryptSetting(setting.value, setting.key);
            
            await new Promise((res, rej) => {
              db.run(
                'UPDATE settings SET value = ? WHERE id = ?',
                [encrypted, setting.id],
                (err) => (err ? rej(err) : res())
              );
            });
            
            migrated++;
          } catch (error) {
            logger.error(`Failed to encrypt setting ${setting.key}:`, error);
          }
        }

        resolve({ migrated, error: null });
      }
    );
  });
}

/**
 * Helper: Migrate VPN configs to new encryption format
 */
async function migrateVPNConfigs() {
  return new Promise((resolve) => {
    db.all(
      `SELECT id, user_id, config_data FROM vpn_configs 
       WHERE config_data IS NOT NULL`,
      [],
      async (err, configs) => {
        if (err || !configs || configs.length === 0) {
          return resolve({ migrated: 0, error: err ? err.message : null });
        }

        let migrated = 0;
        for (const config of configs) {
          try {
            // Check if already encrypted (new format)
            if (config.config_data.split(':').length === 4) {
              continue; // Already encrypted
            }

            // Re-encrypt using new encryption module
            const encrypted = encryption.encryptVPN(JSON.parse(config.config_data));
            
            await new Promise((res, rej) => {
              db.run(
                'UPDATE vpn_configs SET config_data = ? WHERE id = ?',
                [encrypted, config.id],
                (err) => (err ? rej(err) : res())
              );
            });
            
            migrated++;
          } catch (error) {
            logger.error(`Failed to encrypt VPN config ${config.id}:`, error);
          }
        }

        resolve({ migrated, error: null });
      }
    );
  });
}

/**
 * Helper: Migrate API tokens (if storing plaintext tokens - usually they're hashed)
 */
async function migrateAPITokens() {
  // API tokens are typically already hashed/encrypted, so this is a placeholder
  // Only migrate if you're storing plaintext tokens
  return Promise.resolve({ migrated: 0, error: null });
}

/**
 * Helper: Verify encrypted data can be decrypted
 */
async function verifyEncryptedData() {
  const results = {
    vpnConfigs: { tested: 0, valid: 0, invalid: 0 },
    settings: { tested: 0, valid: 0, invalid: 0 }
  };

  return new Promise((resolve) => {
    db.serialize(() => {
      // Verify VPN configs
      db.all(
        `SELECT id, config_data FROM vpn_configs 
         WHERE config_data LIKE '%:%:%:%' LIMIT 10`,
        [],
        (err, configs) => {
          if (!err && configs) {
            configs.forEach(config => {
              results.vpnConfigs.tested++;
              try {
                encryption.decryptVPN(config.config_data);
                results.vpnConfigs.valid++;
              } catch {
                results.vpnConfigs.invalid++;
              }
            });
          }
        }
      );

      // Verify settings
      db.all(
        `SELECT id, key, value FROM settings 
         WHERE value LIKE '%:%:%:%' LIMIT 10`,
        [],
        (err, settings) => {
          if (!err && settings) {
            settings.forEach(setting => {
              results.settings.tested++;
              try {
                encryption.decryptSetting(setting.value, setting.key);
                results.settings.valid++;
              } catch {
                results.settings.invalid++;
              }
            });
          }
          
          // Wait for async queries
          setTimeout(() => resolve(results), 100);
        }
      );
    });
  });
}

module.exports = router;
