const express = require('express');
const router = express.Router();
const { authenticate } = require('../middleware/auth');
const { db } = require('../database/db');
const { spawn } = require('child_process');
const fs = require('fs').promises;
const path = require('path');
const { modifyLimiter, readLimiter } = require('../middleware/rateLimiter');
const encryption = require('../utils/encryption');

// Use centralized encryption module for CWE-311 compliance
const encrypt = (data) => encryption.encryptVPN(data);
const decrypt = (data) => encryption.decryptVPN(data);

// Parse WireGuard config
function parseWireGuardConfig(content) {
  // Security: Limit content size
  if (content.length > 100000) {
    throw new Error('Configuration file too large');
  }

  // Security: Check for script injection attempts
  if (content.includes('<script') || content.includes('${') || content.includes('eval(')) {
    throw new Error('Invalid configuration content detected');
  }

  const lines = content.split('\n');
  const parsed = {
    type: 'wireguard',
    interface: {},
    peer: {}
  };

  let section = null;

  for (const line of lines) {
    const trimmed = line.trim();
    
    if (trimmed === '[Interface]') {
      section = 'interface';
    } else if (trimmed === '[Peer]') {
      section = 'peer';
    } else if (trimmed && !trimmed.startsWith('#') && section) {
      const [key, ...valueParts] = trimmed.split('=');
      if (key && valueParts.length > 0) {
        const value = valueParts.join('=').trim();
        // Security: Sanitize values, no shell commands
        if (value.includes('$(') || value.includes('`') || value.includes(';')) {
          throw new Error('Invalid configuration value detected');
        }
        parsed[section][key.trim()] = value;
      }
    }
  }

  // Validate required fields
  console.log('[VPN-CONFIG] Parsed interface:', Object.keys(parsed.interface));
  console.log('[VPN-CONFIG] Parsed peer:', Object.keys(parsed.peer));
  
  if (!parsed.interface.PrivateKey || !parsed.peer.PublicKey || !parsed.peer.Endpoint) {
    console.log('[VPN-CONFIG] Missing fields - PrivateKey:', !!parsed.interface.PrivateKey, 'PublicKey:', !!parsed.peer.PublicKey, 'Endpoint:', !!parsed.peer.Endpoint);
    throw new Error('Missing required WireGuard configuration fields');
  }

  // Extract metadata
  const endpoint = parsed.peer.Endpoint || '';
  const country = extractCountryFromEndpoint(endpoint);
  const serverName = endpoint.split(':')[0];

  return {
    type: 'wireguard',
    country,
    serverName,
    endpoint,
    data: parsed
  };
}

// Parse OpenVPN config
function parseOpenVPNConfig(content) {
  // Security: Limit content size
  if (content.length > 100000) {
    throw new Error('Configuration file too large');
  }

  // Security: Check for script injection attempts
  if (content.includes('<script') || content.includes('${') || content.includes('eval(')) {
    throw new Error('Invalid configuration content detected');
  }

  // Security: Check for dangerous directives
  const dangerousDirectives = ['script-security 3', 'up /bin/sh', 'down /bin/sh', 'route-up /bin/sh'];
  for (const directive of dangerousDirectives) {
    if (content.includes(directive)) {
      throw new Error('Configuration contains potentially dangerous directives');
    }
  }

  const lines = content.split('\n');
  const parsed = {
    type: 'openvpn',
    remote: null,
    port: null,
    proto: null
  };

  for (const line of lines) {
    const trimmed = line.trim();
    
    if (trimmed.startsWith('remote ')) {
      const parts = trimmed.split(/\s+/);
      parsed.remote = parts[1];
      parsed.port = parts[2] || '1194';
      parsed.proto = parts[3] || 'udp';
    }
  }

  // Validate required fields
  if (!parsed.remote) {
    throw new Error('Missing required OpenVPN remote server');
  }

  // Extract metadata
  const country = extractCountryFromEndpoint(parsed.remote);
  const serverName = parsed.remote || 'Unknown';

  return {
    type: 'openvpn',
    country,
    serverName,
    endpoint: `${parsed.remote}:${parsed.port}`,
    proto: parsed.proto,
    data: { config: content }
  };
}

// Extract country code from endpoint/hostname
function extractCountryFromEndpoint(endpoint) {
  if (!endpoint) return null;
  
  // Common patterns: us-01.server.com, node-us-01, 185.107.57.98 (Romania)
  const countryMatch = endpoint.match(/[-_]([a-z]{2})[-_\d]/i);
  if (countryMatch) {
    return countryMatch[1].toUpperCase();
  }

  // Known IP ranges (basic lookup)
  const ipRanges = {
    '185.107.57': 'RO',
    '185.163.110': 'RO',
    '169.150': 'US',
    '103.107': 'JP'
  };

  for (const [range, country] of Object.entries(ipRanges)) {
    if (endpoint.startsWith(range)) {
      return country;
    }
  }

  return null;
}

// Get all VPN configs for user
router.get('/configs', authenticate, readLimiter, async (req, res) => {
  try {
    const configs = await new Promise((resolve, reject) => {
      db.all(
        `SELECT id, name, config_type, country, server_name, endpoint, is_active, created_at
         FROM vpn_configs
         WHERE user_id = ?
         ORDER BY is_active DESC, created_at DESC`,
        [req.user.userId],
        (err, rows) => {
          if (err) reject(err);
          else resolve(rows || []);
        }
      );
    });

    // Verify actual VPN connection status for active configs
    for (const config of configs) {
      if (config.is_active) {
        const actuallyConnected = await checkVPNConnection(config.config_type, req.user.userId);
        if (!actuallyConnected) {
          // Update database to reflect actual state
          await new Promise((resolve, reject) => {
            db.run(
              'UPDATE vpn_configs SET is_active = 0 WHERE id = ?',
              [config.id],
              (err) => (err ? reject(err) : resolve())
            );
          });
          config.is_active = 0;
          console.log(`[VPN-CONFIG] Reset stale active state for config ${config.id}`);
        }
      }
    }

    res.json({ configs });
  } catch (err) {
    console.error('Error fetching VPN configs:', err);
    res.status(500).json({ error: 'Failed to fetch VPN configurations' });
  }
});

// Get specific config
router.get('/configs/:id', authenticate, readLimiter, (req, res) => {
  db.get(
    `SELECT id, name, config_type, config_data, country, server_name, endpoint, is_active, created_at
     FROM vpn_configs
     WHERE id = ? AND user_id = ?`,
    [req.params.id, req.user.userId],
    (err, config) => {
      if (err) {
        console.error('Error fetching VPN config:', err);
        return res.status(500).json({ error: 'Failed to fetch configuration' });
      }

      if (!config) {
        return res.status(404).json({ error: 'Configuration not found' });
      }

      // Decrypt config data
      try {
        config.config_data = JSON.stringify(decrypt(config.config_data));
      } catch (error) {
        console.error('Error decrypting config:', error);
        return res.status(500).json({ error: 'Failed to decrypt configuration' });
      }

      res.json({ config });
    }
  );
});

// Upload VPN config file
router.post('/configs/upload', authenticate, modifyLimiter, async (req, res) => {
  try {
    console.log('[VPN-CONFIG] Upload request received');
    console.log('[VPN-CONFIG] Files:', req.files ? Object.keys(req.files) : 'none');
    // CWE-532: Do not log request body - may contain sensitive VPN credentials
    
    if (!req.files || !req.files.config) {
      return res.status(400).json({ error: 'No file uploaded' });
    }

    const uploadedFile = req.files.config;
    
    // Validate file size (1MB max)
    if (uploadedFile.size > 1024 * 1024) {
      return res.status(400).json({ error: 'File too large (max 1MB)' });
    }
    
    // Validate file extension
    const ext = path.extname(uploadedFile.name).toLowerCase();
    if (ext !== '.conf' && ext !== '.ovpn') {
      return res.status(400).json({ error: 'Only .conf and .ovpn files are allowed' });
    }

    const { name } = req.body;
    if (!name || name.trim().length === 0) {
      return res.status(400).json({ error: 'Configuration name is required' });
    }

    // Validate name
    if (!/^[a-zA-Z0-9\s\-_.()]+$/.test(name)) {
      return res.status(400).json({ error: 'Invalid configuration name. Use only letters, numbers, spaces, and common punctuation.' });
    }

    if (name.length > 100) {
      return res.status(400).json({ error: 'Configuration name too long (max 100 characters)' });
    }

    // Read file content from temp file
    const content = await fs.readFile(uploadedFile.tempFilePath, 'utf8');
    console.log('[VPN-CONFIG] File extension:', ext);
    console.log('[VPN-CONFIG] Content length:', content.length);
    console.log('[VPN-CONFIG] First 200 chars:', content.substring(0, 200));

    // Parse based on file type
    let parsed;
    if (ext === '.conf') {
      parsed = parseWireGuardConfig(content);
    } else if (ext === '.ovpn') {
      parsed = parseOpenVPNConfig(content);
    } else {
      return res.status(400).json({ error: 'Unsupported file format' });
    }

    // Encrypt config data using centralized encryption (CWE-311)
    const encryptedData = encrypt(parsed.data);

    // Save to database
    db.run(
      `INSERT INTO vpn_configs (user_id, name, config_type, config_data, country, server_name, endpoint, is_active)
       VALUES (?, ?, ?, ?, ?, ?, ?, 0)`,
      [req.user.userId, name.trim(), parsed.type, encryptedData, parsed.country, parsed.serverName, parsed.endpoint],
      function(err) {
        if (err) {
          console.error('Error saving VPN config:', err);
          return res.status(500).json({ error: 'Failed to save configuration' });
        }

        res.json({
          message: 'Configuration uploaded successfully',
          config: {
            id: this.lastID,
            name: name.trim(),
            type: parsed.type,
            country: parsed.country,
            serverName: parsed.serverName,
            endpoint: parsed.endpoint
          }
        });
      }
    );
  } catch (error) {
    console.error('[VPN-CONFIG] Error processing config upload:', error);
    console.error('[VPN-CONFIG] Error stack:', error.stack);
    res.status(500).json({ error: error.message || 'Failed to process configuration file' });
  }
});

// Delete config
router.delete('/configs/:id', authenticate, modifyLimiter, (req, res) => {
  // First check if config is active
  db.get(
    'SELECT is_active FROM vpn_configs WHERE id = ? AND user_id = ?',
    [req.params.id, req.user.userId],
    (err, config) => {
      if (err) {
        console.error('Error checking config:', err);
        return res.status(500).json({ error: 'Failed to delete configuration' });
      }

      if (!config) {
        return res.status(404).json({ error: 'Configuration not found' });
      }

      if (config.is_active) {
        return res.status(400).json({ error: 'Cannot delete active configuration. Disconnect first.' });
      }

      // Delete the config
      db.run(
        'DELETE FROM vpn_configs WHERE id = ? AND user_id = ?',
        [req.params.id, req.user.userId],
        function(err) {
          if (err) {
            console.error('Error deleting config:', err);
            return res.status(500).json({ error: 'Failed to delete configuration' });
          }

          res.json({ message: 'Configuration deleted successfully' });
        }
      );
    }
  );
});

// Set active config
router.post('/configs/:id/activate', authenticate, modifyLimiter, (req, res) => {
  db.serialize(() => {
    // Deactivate all configs for user
    db.run(
      'UPDATE vpn_configs SET is_active = 0 WHERE user_id = ?',
      [req.user.userId],
      (err) => {
        if (err) {
          console.error('Error deactivating configs:', err);
          return res.status(500).json({ error: 'Failed to activate configuration' });
        }

        // Activate the selected config
        db.run(
          'UPDATE vpn_configs SET is_active = 1 WHERE id = ? AND user_id = ?',
          [req.params.id, req.user.userId],
          function(err) {
            if (err) {
              console.error('Error activating config:', err);
              return res.status(500).json({ error: 'Failed to activate configuration' });
            }

            if (this.changes === 0) {
              return res.status(404).json({ error: 'Configuration not found' });
            }

            res.json({ message: 'Configuration activated successfully' });
          }
        );
      }
    );
  });
});

// Connect using config
router.post('/configs/:id/connect', authenticate, modifyLimiter, async (req, res) => {
  try {
    // Get config
    const config = await new Promise((resolve, reject) => {
      db.get(
        'SELECT config_type, config_data FROM vpn_configs WHERE id = ? AND user_id = ?',
        [req.params.id, req.user.userId],
        (err, row) => {
          if (err) reject(err);
          else resolve(row);
        }
      );
    });

    if (!config) {
      return res.status(404).json({ error: 'Configuration not found' });
    }

    // Decrypt config
    const decryptedData = decrypt(config.config_data);

    console.log(`[VPN-CONFIG] Connecting config ${req.params.id} for user ${req.user.userId}`);

    // Connect based on type
    if (config.config_type === 'wireguard') {
      await connectWireGuard(decryptedData, req.user.userId);
    } else if (config.config_type === 'openvpn') {
      await connectOpenVPN(decryptedData, req.user.userId);
    }

    console.log(`[VPN-CONFIG] Successfully connected, updating database`);

    // Deactivate all other configs for this user first
    await new Promise((resolve, reject) => {
      db.run(
        'UPDATE vpn_configs SET is_active = 0 WHERE user_id = ?',
        [req.user.userId],
        (err) => (err ? reject(err) : resolve())
      );
    });

    // Mark this config as active
    await new Promise((resolve, reject) => {
      db.run(
        'UPDATE vpn_configs SET is_active = 1 WHERE id = ? AND user_id = ?',
        [req.params.id, req.user.userId],
        (err) => (err ? reject(err) : resolve())
      );
    });

    // CWE-532: Logged without exposing sensitive config ID
    console.log(`[VPN-CONFIG] Configuration marked as active for user ${req.user.userId}`);
    res.json({ message: 'Connected to VPN successfully', success: true });
  } catch (error) {
    console.error('Error connecting to VPN:', error);
    res.status(500).json({ error: error.message || 'Failed to connect to VPN' });
  }
});

// Disconnect VPN
router.post('/configs/:id/disconnect', authenticate, modifyLimiter, async (req, res) => {
  try {
    // Get config
    const config = await new Promise((resolve, reject) => {
      db.get(
        'SELECT config_type FROM vpn_configs WHERE id = ? AND user_id = ? AND is_active = 1',
        [req.params.id, req.user.userId],
        (err, row) => {
          if (err) reject(err);
          else resolve(row);
        }
      );
    });

    if (!config) {
      // No active config found, but still try to clean up any interfaces
      console.log('[VPN] No active config found, attempting cleanup anyway');
      try {
        await disconnectWireGuard(req.user.userId);
      } catch (e) {
        // Ignore errors during cleanup
      }
      // Mark as inactive regardless
      await new Promise((resolve, reject) => {
        db.run(
          'UPDATE vpn_configs SET is_active = 0 WHERE id = ? AND user_id = ?',
          [req.params.id, req.user.userId],
          (err) => (err ? reject(err) : resolve())
        );
      });
      return res.json({ message: 'VPN state cleaned up' });
    }

    // Disconnect based on type
    if (config.config_type === 'wireguard') {
      await disconnectWireGuard(req.user.userId);
    } else if (config.config_type === 'openvpn') {
      await disconnectOpenVPN(req.user.userId);
    }

    // Mark as inactive
    await new Promise((resolve, reject) => {
      db.run(
        'UPDATE vpn_configs SET is_active = 0 WHERE id = ? AND user_id = ?',
        [req.params.id, req.user.userId],
        (err) => (err ? reject(err) : resolve())
      );
    });

    res.json({ message: 'Disconnected from VPN successfully' });
  } catch (error) {
    console.error('Error disconnecting from VPN:', error);
    res.status(500).json({ error: error.message || 'Failed to disconnect from VPN' });
  }
});

// Helper: Connect WireGuard
async function connectWireGuard(config, userId) {
  const interfaceName = `wg${userId}`;
  const confPath = `/etc/wireguard/${interfaceName}.conf`;
  
  // Build WireGuard config with split-tunnel for local network access
  // Note: DNS is not set in config to avoid conflicts with Docker's DNS
  // Extract VPN endpoint IP to exclude from tunnel (prevent routing loop)
  const vpnEndpointIP = config.peer.Endpoint ? config.peer.Endpoint.split(':')[0] : null;
  
  // Use DNS from config (VPN provider's DNS) with public DNS fallbacks
  const dnsServers = config.interface.DNS || '1.1.1.1';
  const primaryDNS = dnsServers.split(',')[0].trim();
  
  const wgConfig = `[Interface]
PrivateKey = ${config.interface.PrivateKey}
Address = ${config.interface.Address}
Table = off
FwMark = 0xca6c
PostUp = ip route add default dev %i table 51820${vpnEndpointIP ? `
PostUp = ip route add ${vpnEndpointIP}/32 via 172.20.0.1 dev eth0` : ''}
PostUp = ip rule add to 172.20.0.0/16 table main priority 50
PostUp = ip rule add to 192.168.0.0/16 table main priority 51
PostUp = ip rule add not fwmark 0xca6c table 51820 priority 100
PostUp = ip route replace default via 172.20.0.1 dev eth0 metric 200
PostUp = ip route add default dev %i metric 50
PostUp = cp /etc/resolv.conf /etc/resolv.conf.vpn-backup
PostUp = echo 'nameserver ${primaryDNS}' > /etc/resolv.conf
PostUp = echo 'nameserver 1.1.1.1' >> /etc/resolv.conf
PostUp = echo 'nameserver 8.8.8.8' >> /etc/resolv.conf
PreDown = ip route del default dev %i metric 50
PreDown = ip route replace default via 172.20.0.1 dev eth0 metric 0
PreDown = ip rule del to 172.20.0.0/16 table main priority 50
PreDown = ip rule del to 192.168.0.0/16 table main priority 51
PreDown = ip rule del to 10.0.0.0/8 table main priority 52
PreDown = ip rule del not fwmark 0xca6c table 51820 priority 100
PreDown = ip route del default dev %i table 51820${vpnEndpointIP ? `
PreDown = ip route del ${vpnEndpointIP}/32 via 172.20.0.1 dev eth0` : ''}
PreDown = mv /etc/resolv.conf.vpn-backup /etc/resolv.conf 2>/dev/null || true

[Peer]
PublicKey = ${config.peer.PublicKey}
AllowedIPs = ${config.peer.AllowedIPs || '0.0.0.0/0, ::/0'}
Endpoint = ${config.peer.Endpoint}
${config.peer.PersistentKeepalive ? `PersistentKeepalive = ${config.peer.PersistentKeepalive}` : ''}
`;

  console.log('[WireGuard] Creating config file at', confPath);
  console.log('[WireGuard] Current user:', process.getuid ? process.getuid() : 'unknown');
  
  // Write config file (this will work as root or with proper permissions)
  try {
    await fs.writeFile(confPath, wgConfig, { mode: 0o600 });
    console.log('[WireGuard] Config file created successfully');
  } catch (err) {
    console.error('[WireGuard] Failed to create config file:', err.message);
    throw new Error(`Failed to create WireGuard config: ${err.message}`);
  }

  return new Promise((resolve, reject) => {
    // Use shell to ensure root context
    const wg = spawn('wg-quick', ['up', interfaceName], {
      uid: 0, // Run as root
      gid: 0
    });
    
    let output = '';
    wg.stdout.on('data', (data) => { 
      output += data; 
      console.log('[WireGuard]', data.toString().trim());
    });
    wg.stderr.on('data', (data) => { 
      output += data;
      console.log('[WireGuard]', data.toString().trim());
    });
    
    wg.on('close', (code) => {
      if (code === 0) {
        console.log('[WireGuard] Connected successfully to', config.peer.Endpoint);
        resolve();
      } else {
        console.error('[WireGuard] Connection failed (code', code, '):', output);
        // Check for Docker networking limitation
        if (output.includes('Nexthop has invalid gateway') || output.includes('Error: Nexthop')) {
          reject(new Error('VPN connection requires host network mode. Docker containers have limited network access. Please use the desktop app for VPN connections.'));
        } else {
          reject(new Error('WireGuard connection failed'));
        }
      }
    });
    
    wg.on('error', (err) => {
      console.error('[WireGuard] Spawn error:', err.message);
      reject(new Error(`Failed to start wg-quick: ${err.message}`));
    });
  });
}

// Helper: Connect OpenVPN
async function connectOpenVPN(config, userId) {
  const confPath = `/tmp/ovpn_${userId}.conf`;
  
  await fs.writeFile(confPath, config.config);

  return new Promise((resolve, reject) => {
    const ovpn = spawn('openvpn', [
      '--config', confPath,
      '--daemon',
      '--log', `/tmp/ovpn_${userId}.log`
    ]);
    
    ovpn.on('close', (code) => {
      if (code === 0) {
        console.log('[OpenVPN] Connected successfully');
        resolve();
      } else {
        reject(new Error('OpenVPN connection failed. VPN requires host network mode. Please use the desktop app for VPN connections.'));
      }
    });
  });
}

// Helper: Disconnect WireGuard
async function disconnectWireGuard(userId) {
  const interfaceName = `wg${userId}`;
  
  console.log('[WireGuard] Disconnecting interface:', interfaceName);

  return new Promise((resolve, reject) => {
    const wg = spawn('wg-quick', ['down', interfaceName], {
      uid: 0,
      gid: 0
    });
    
    let output = '';
    wg.stdout.on('data', (data) => { 
      output += data; 
      console.log('[WireGuard]', data.toString().trim());
    });
    wg.stderr.on('data', (data) => { 
      output += data;
      console.log('[WireGuard]', data.toString().trim());
    });
    
    wg.on('close', (code) => {
      if (code === 0) {
        console.log('[WireGuard] Disconnected successfully');
        resolve();
      } else if (output.includes('is not a WireGuard interface') || output.includes('does not exist')) {
        // Interface doesn't exist - already disconnected
        console.log('[WireGuard] Interface already disconnected or config file missing');
        resolve();
      } else {
        console.error('[WireGuard] Disconnect failed (code', code, '):', output);
        reject(new Error('WireGuard disconnect failed'));
      }
    });
    
    wg.on('error', (err) => {
      console.error('[WireGuard] Spawn error:', err.message);
      reject(new Error(`Failed to stop wg-quick: ${err.message}`));
    });
  });
}

// Helper: Disconnect OpenVPN
async function disconnectOpenVPN(userId) {
  console.log('[OpenVPN] Disconnecting...');
  
  return new Promise((resolve, reject) => {
    // Kill OpenVPN process
    const pkill = spawn('pkill', ['-f', `openvpn.*ovpn_${userId}`]);
    
    pkill.on('close', (code) => {
      // pkill returns 0 if processes were killed, 1 if none were found
      if (code === 0 || code === 1) {
        console.log('[OpenVPN] Disconnected successfully');
        resolve();
      } else {
        console.error('[OpenVPN] Disconnect failed with code:', code);
        reject(new Error('OpenVPN disconnect failed'));
      }
    });
    
    pkill.on('error', (err) => {
      console.error('[OpenVPN] Spawn error:', err.message);
      reject(new Error(`Failed to stop OpenVPN: ${err.message}`));
    });
  });
}

// Helper: Check if VPN is actually connected
async function checkVPNConnection(configType, userId) {
  try {
    if (configType === 'wireguard') {
      const interfaceName = `wg${userId}`;
      return new Promise((resolve) => {
        const wg = spawn('wg', ['show', interfaceName]);
        let hasOutput = false;
        
        wg.stdout.on('data', () => { hasOutput = true; });
        wg.on('close', (code) => {
          resolve(code === 0 && hasOutput);
        });
        wg.on('error', () => resolve(false));
      });
    } else if (configType === 'openvpn') {
      return new Promise((resolve) => {
        const pgrep = spawn('pgrep', ['-f', `openvpn.*ovpn_${userId}`]);
        let hasOutput = false;
        
        pgrep.stdout.on('data', () => { hasOutput = true; });
        pgrep.on('close', (code) => {
          resolve(code === 0 && hasOutput);
        });
        pgrep.on('error', () => resolve(false));
      });
    }
    return false;
  } catch (error) {
    return false;
  }
}

module.exports = router;
