const express = require('express');
const cors = require('cors');
const { exec } = require('child_process');
const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const http = require('http');
const WebSocket = require('ws');

// Импорт утилит
const log = require('./utils/logger');
const { execSshCommand, execScpCommand } = require('./utils/sshUtils');
const { checkSystemRequirements, SYSTEM_REQUIREMENTS } = require('./utils/systemUtils');
const { exportDockerImages, transferDockerImages, importDockerImages, cleanupLocalFiles } = require('./utils/dockerUtils');
const { createAllUsers } = require('./utils/userUtils');
const { cleanupVdsServer, setupRootSshKeys, disablePasswordAuth, setupFirewall } = require('./utils/cleanupUtils');
const { createSshKeys } = require('./utils/localUtils');

const PUBLIC_KEY_PATH = path.join(os.homedir(), '.ssh', 'id_rsa.pub');

const app = express();
const server = http.createServer(app);
const PORT = process.env.PORT || 3000;

// WebSocket сервер
const wss = new WebSocket.Server({ 
  server,
  cors: {
    origin: [
      'http://localhost:5173',
      'http://localhost:8000',
      'http://localhost:9000',
      'https://localhost:9443',
      'http://127.0.0.1:5173',
      'http://127.0.0.1:8000'
    ],
    credentials: true
  }
});

// Middleware
app.use(cors({
  origin: [
    'http://localhost:5173',
    'http://localhost:8000',
    'http://localhost:9000',
    'https://localhost:9443',
    'http://127.0.0.1:5173',
    'http://127.0.0.1:8000'
  ],
  credentials: true
}));
app.use(express.json({ limit: '10mb' }));

// Middleware для логирования запросов
const logRequest = (req, res, next) => {
  const timestamp = new Date().toISOString();
  const ip = req.ip || req.connection.remoteAddress;
  log.info(`[${timestamp}] ${req.method} ${req.url} from ${ip}`);
  next();
};

app.use(logRequest);

// Состояние VDS
let vdsState = {
  configured: false,
  domain: null,
  vdsIp: null
};

// Функция для отправки логов через WebSocket
const sendWebSocketLog = (type, message, stage = null, percentage = null) => {
  const logData = {
    type: 'webssh_log',
    logType: type,
    message,
    timestamp: new Date().toISOString()
  };
  
  if (stage) {
    logData.stage = stage;
  }
  
  if (percentage !== null) {
    logData.percentage = percentage;
  }
  
  broadcastToWebSocket(logData);
  
  // Также отправляем как прогресс, если есть stage
  if (stage) {
    broadcastToWebSocket({
      type: 'webssh_progress',
      stage,
      message,
      percentage,
      timestamp: new Date().toISOString()
    });
  }
};

// Функция для отправки статуса через WebSocket
const sendWebSocketStatus = (connected, message = null) => {
  broadcastToWebSocket({
    type: 'webssh_status',
    connected,
    status: connected ? 'connected' : 'disconnected',
    message,
    timestamp: new Date().toISOString()
  });
};

// Функция для отправки сообщения всем подключенным WebSocket клиентам
const broadcastToWebSocket = (data) => {
  const message = JSON.stringify(data);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
};

// Проверка здоровья агента
app.get('/health', (req, res) => {
  log.info('Health check requested');
  res.json({ 
    status: 'ok', 
    timestamp: new Date().toISOString(),
    version: '1.0.0',
    vdsConfigured: vdsState.configured,
    vdsDomain: vdsState.domain
  });
});

// Предварительная проверка системных требований VDS
app.post('/vds/check-requirements', logRequest, async (req, res) => {
  try {
    const { 
      vdsIp, 
      ubuntuUser, 
      sshUser,
      sshHost,
      sshPort = 22,
      sshConnectUser,
      sshConnectPassword
    } = req.body;
    
    // Нормализуем значения (удаляем пробелы)
    const normalizedVdsIp = String(vdsIp || '').trim();
    const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
    const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
    const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
    
    if (!normalizedVdsIp || !ubuntuUser || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
      return res.status(400).json({
        success: false,
        message: 'Необходимы параметры: vdsIp, ubuntuUser, sshConnectUser, sshConnectPassword'
      });
    }
    
    log.info(`Проверка системных требований VDS: ${normalizedVdsIp}`);
    
    const options = {
      vdsIp: normalizedVdsIp,
      sshHost: normalizedSshHost,
      sshPort,
      sshConnectUser: normalizedSshConnectUser,
      sshConnectPassword: normalizedSshConnectPassword
    };
    
    const result = await checkSystemRequirements(options);
    
    res.json({
      success: result.isCompatible,
      message: result.isCompatible 
        ? (result.hasWarnings ? 'VDS соответствует минимальным требованиям, но есть предупреждения' : 'VDS полностью соответствует системным требованиям')
        : 'VDS не соответствует минимальным требованиям',
      systemInfo: result.systemInfo,
      requirements: SYSTEM_REQUIREMENTS,
      warnings: result.warnings,
      errors: result.errors,
      isCompatible: result.isCompatible,
      hasWarnings: result.hasWarnings
    });
    
  } catch (error) {
    log.error('Ошибка проверки системных требований: ' + error.message);
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

// Передача ключа шифрования на VDS
app.post('/vds/transfer-encryption-key', logRequest, async (req, res) => {
  try {
    const { 
      vdsIp, 
      dockerUser, 
      sshUser, 
      sshHost,
      sshPort = 22,
      sshConnectUser,
      sshConnectPassword
    } = req.body;
    
    // Нормализуем значения (удаляем пробелы)
    const normalizedVdsIp = String(vdsIp || '').trim();
    const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
    const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
    const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
    
    if (!normalizedVdsIp || !dockerUser || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
      return res.status(400).json({
        success: false,
        message: 'Необходимы параметры: vdsIp, dockerUser, sshConnectUser, sshConnectPassword'
      });
    }
    
    log.info(`🔐 Передача ключа шифрования на VDS: ${normalizedVdsIp}`);
    
    const options = {
      vdsIp: normalizedVdsIp,
      sshHost: normalizedSshHost,
      sshPort,
      sshConnectUser: normalizedSshConnectUser,
      sshConnectPassword: normalizedSshConnectPassword
    };
    
    // 1. Убеждаемся, что директория для ключа существует на VDS
    log.info('🔍 Подготовка директории для ключа шифрования на VDS...');
    const ensureDirResult = await execSshCommand(`mkdir -p /home/${dockerUser}/dapp/ssl/keys`, options);
    if (ensureDirResult.code !== 0) {
      log.error('❌ Не удалось подготовить директорию для ключа шифрования на VDS');
      return res.status(500).json({
        success: false,
        message: 'Не удалось подготовить директорию для ключа шифрования на VDS'
      });
    }
    
    // 2. Определяем путь к ключу шифрования
    let encryptionKeyPath = process.env.ENCRYPTION_KEY_PATH;
    
    if (!encryptionKeyPath) {
      // Пробуем несколько возможных путей
      const possiblePaths = [
        path.resolve(__dirname, '..', 'ssl', 'keys', 'full_db_encryption.key'),
        path.resolve(__dirname, 'ssl', 'keys', 'full_db_encryption.key'),
        '/app/ssl/keys/full_db_encryption.key',
        path.join(process.cwd(), 'ssl', 'keys', 'full_db_encryption.key')
      ];
      
      log.info(`🔍 Поиск ключа шифрования в возможных путях...`);
      for (const possiblePath of possiblePaths) {
        if (await fs.pathExists(possiblePath)) {
          encryptionKeyPath = possiblePath;
          log.info(`✅ Ключ найден по пути: ${encryptionKeyPath}`);
          break;
        }
      }
    }
    
    if (!encryptionKeyPath) {
      log.error('❌ Путь к ключу шифрования не указан и не найден в стандартных местах');
      return res.status(500).json({
        success: false,
        message: 'Путь к ключу шифрования не указан и не найден в стандартных местах'
      });
    }
    
    // Проверяем существование файла
    const keyExists = await fs.pathExists(encryptionKeyPath);
    if (!keyExists) {
      log.error(`❌ Файл ключа шифрования не найден: ${encryptionKeyPath}`);
      return res.status(500).json({
        success: false,
        message: `Файл ключа шифрования не найден: ${encryptionKeyPath}`
      });
    }
    
    log.info(`📖 Чтение ключа шифрования из: ${encryptionKeyPath}`);
    
    try {
      const encryptionKeyContent = await fs.readFile(encryptionKeyPath, 'utf8');
      
      if (!encryptionKeyContent || encryptionKeyContent.trim().length === 0) {
        throw new Error('Ключ шифрования пуст или не может быть прочитан');
      }
      
      log.success(`✅ Ключ шифрования прочитан с локальной машины (длина: ${encryptionKeyContent.length} символов)`);
      
      // 3. Передаем ключ на VDS через SSH
      log.info('📤 Передача ключа шифрования на VDS...');
      
      // Создаем временный файл с ключом
      const tempKeyPath = `/tmp/encryption_key_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.key`;
      log.info(`📝 Создание временного файла: ${tempKeyPath}`);
      await fs.writeFile(tempKeyPath, encryptionKeyContent, { mode: 0o600 });
      
      // Передаем файл на VDS через SCP в правильную директорию
      log.info(`📤 Передача ключа на VDS через SCP...`);
      const scpResult = await execScpCommand(
        tempKeyPath,
        `/home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`,
        options
      );
      
      // Проверяем результат SCP
      if (scpResult.code !== 0) {
        throw new Error(`SCP команда завершилась с ошибкой (код: ${scpResult.code}): ${scpResult.stderr || scpResult.stdout}`);
      }
      
      log.success('✅ Файл ключа успешно передан на VDS через SCP');
      
      // Удаляем временный файл
      try {
        await fs.remove(tempKeyPath);
        log.info(`🗑️ Временный файл удален: ${tempKeyPath}`);
      } catch (removeError) {
        log.warn(`⚠️ Не удалось удалить временный файл ${tempKeyPath}: ${removeError.message}`);
      }
      
      // 4. Устанавливаем правильные права доступа к ключу на VDS
      log.info('🔒 Настройка прав доступа к ключу шифрования...');
      const chownResult = await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      if (chownResult.code !== 0) {
        log.warn(`⚠️ Не удалось изменить владельца ключа: ${chownResult.stderr}`);
      }
      
      // 🆕 Используем права 644 вместо 600, чтобы контейнеры могли читать ключ
      // Ключ должен быть читаемым для всех процессов в контейнерах
      const chmodResult = await execSshCommand(`chmod 644 /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      if (chmodResult.code !== 0) {
        log.warn(`⚠️ Не удалось изменить права доступа к ключу: ${chmodResult.stderr}`);
      } else {
        log.success('✅ Права доступа к ключу установлены (644 - читаемый для всех)');
      }
      
      // 5. Проверяем, что ключ успешно передан
      log.info('🔍 Проверка передачи ключа...');
      const verifyResult = await execSshCommand(`ls -la /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      
      if (verifyResult.code === 0) {
        log.success('✅ Ключ шифрования успешно передан и проверен на VDS');
        log.info(`📋 Информация о ключе на VDS: ${verifyResult.stdout.trim()}`);
        
        res.json({
          success: true,
          message: 'Ключ шифрования успешно передан на VDS',
          vdsIp: vdsIp,
          keyPath: `/home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`,
          nextSteps: [
            '✅ Ключ шифрования передан на VDS',
            '✅ Права доступа настроены',
            '✅ VDS готова для запуска приложения с шифрованием'
          ]
        });
      } else {
        throw new Error(`Не удалось проверить передачу ключа шифрования: ${verifyResult.stderr || verifyResult.stdout}`);
      }
      
    } catch (error) {
      log.error('❌ Ошибка передачи ключа шифрования: ' + error.message);
      log.error('📋 Детали ошибки:', error.stack);
      return res.status(500).json({
        success: false,
        message: `Ошибка передачи ключа шифрования: ${error.message}`
      });
    }
    
  } catch (error) {
    log.error('❌ Ошибка передачи ключа шифрования на VDS: ' + error.message);
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

// Настройка VDS
app.post('/vds/setup', logRequest, async (req, res) => {
  try {
    const { 
      vdsIp, 
      domain, 
      email, 
      ubuntuUser, 
      dockerUser, 
      sshUser, 
      sshHost,
      sshPort = 22,
      sshConnectUser,
      sshConnectPassword
    } = req.body;
    
    // Нормализуем значения (удаляем пробелы)
    const normalizedVdsIp = String(vdsIp || '').trim();
    const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
    const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
    const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
    
    log.info(`Настройка VDS: ${normalizedVdsIp} для домена: ${domain}`);
    
    // Отправляем начальный статус через WebSocket
    sendWebSocketStatus(false, 'Начинаем настройку VDS...');
    sendWebSocketLog('info', `🚀 Начинаем настройку VDS: ${normalizedVdsIp} для домена: ${domain}`, 'init', 0);
    
    const options = {
      vdsIp: normalizedVdsIp,
      sshHost: normalizedSshHost,
      sshPort,
      sshConnectUser: normalizedSshConnectUser,
      sshConnectPassword: normalizedSshConnectPassword
    };
    
    // 0. Проверка системных требований
    sendWebSocketLog('info', '🔍 Проверка системных требований VDS...', 'requirements', 5);
    const systemResult = await checkSystemRequirements(options);
    const systemInfo = systemResult.systemInfo;
    sendWebSocketLog('success', '✅ Системные требования проверены', 'requirements', 10);
    
    // 1. Создание SSH ключей локально (на хосте)
    sendWebSocketLog('info', '🔑 Создание SSH ключей...', 'ssh_keys', 15);
    await createSshKeys(email);
    sendWebSocketLog('success', '✅ SSH ключи созданы', 'ssh_keys', 20);
    
    // Читаем созданный публичный ключ с хоста
    const publicKeyContent = await fs.readFile(PUBLIC_KEY_PATH, 'utf8');
    const publicKeyLine = publicKeyContent.trim();
    
    // 2. Настройка SSH ключей для root
    await setupRootSshKeys(publicKeyLine, options);
    
    // 3. Очистка VDS сервера
    sendWebSocketLog('info', '🧹 Очистка VDS сервера...', 'cleanup', 30);
    await cleanupVdsServer(options);
    sendWebSocketLog('success', '✅ VDS сервер очищен', 'cleanup', 35);
    
    // 4. Создание пользователей
    sendWebSocketLog('info', '👥 Создание пользователей...', 'users', 40);
    await createAllUsers(ubuntuUser, dockerUser, publicKeyLine, options);
    sendWebSocketLog('success', '✅ Пользователи созданы', 'users', 45);
    
    // 5. Установка Docker
    sendWebSocketLog('info', '🐳 Установка Docker...', 'docker', 50);
    log.info('Установка Docker...');
    await execSshCommand('curl -fsSL https://get.docker.com -o get-docker.sh', options);
    await execSshCommand('sh get-docker.sh', options);
    await execSshCommand(`usermod -aG docker ${dockerUser}`, options);
    sendWebSocketLog('success', '✅ Docker установлен', 'docker', 55);
    
    // 6. Установка Docker Compose
    await execSshCommand('curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose', options);
    await execSshCommand('chmod +x /usr/local/bin/docker-compose', options);
    
    // 7. Отключение парольной аутентификации
    await disablePasswordAuth(options);
    
    // 8. Настройка firewall
    await setupFirewall(options);
    
    // 8.1. Установка fail2ban для защиты от SSH атак
    log.info('Установка fail2ban для защиты от SSH атак...');
    await execSshCommand('apt-get install -y fail2ban', options);
    
    // Создание конфигурации fail2ban для SSH с увеличенными лимитами
    const fail2banConfig = `[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 50
bantime = 3600
findtime = 3600

        [nginx-http-auth]
        enabled = true
        filter = nginx-http-auth
        logpath = /var/lib/docker/containers/*/docker-nginx-*.log
        maxretry = 3
        bantime = 3600`;
    
    await execSshCommand(`echo '${fail2banConfig}' | tee /etc/fail2ban/jail.local`, options);
    await execSshCommand('systemctl enable fail2ban', options);
    await execSshCommand('systemctl start fail2ban', options);
    log.success('fail2ban настроен для защиты от SSH атак');
    
    // 9. Создание директории для ключей шифрования
    await execSshCommand(`mkdir -p /home/${dockerUser}/dapp/ssl/keys`, options);
    await execSshCommand(`chmod 700 /home/${dockerUser}/dapp/ssl/keys`, options);
    await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp/ssl/keys`, options);
    log.success('Директория для ключа шифрования подготовлена');
    
    // 9.1. Передача ключа шифрования на VDS
    // Прогресс после установки Docker (55%), двигаемся вперёд, а не назад
    sendWebSocketLog('info', '🔐 Передача ключа шифрования на VDS...', 'encryption_key', 56);
    log.info('🔐 Передача ключа шифрования на VDS...');
    
    try {
      // Определяем путь к ключу шифрования
      let encryptionKeyPath = process.env.ENCRYPTION_KEY_PATH;
      
      if (!encryptionKeyPath) {
        // Пробуем несколько возможных путей
        const possiblePaths = [
          path.resolve(__dirname, '..', 'ssl', 'keys', 'full_db_encryption.key'),
          path.resolve(__dirname, 'ssl', 'keys', 'full_db_encryption.key'),
          '/app/ssl/keys/full_db_encryption.key',
          path.join(process.cwd(), 'ssl', 'keys', 'full_db_encryption.key')
        ];
        
        log.info(`🔍 Поиск ключа шифрования в возможных путях...`);
        for (const possiblePath of possiblePaths) {
          if (await fs.pathExists(possiblePath)) {
            encryptionKeyPath = possiblePath;
            log.info(`✅ Ключ найден по пути: ${encryptionKeyPath}`);
            break;
          }
        }
      }
      
      if (!encryptionKeyPath) {
        throw new Error('Путь к ключу шифрования не указан и не найден в стандартных местах');
      }
      
      // Проверяем существование файла
      const keyExists = await fs.pathExists(encryptionKeyPath);
      if (!keyExists) {
        throw new Error(`Файл ключа шифрования не найден: ${encryptionKeyPath}`);
      }
      
      log.info(`📖 Чтение ключа шифрования из: ${encryptionKeyPath}`);
      
      // Читаем ключ шифрования с локальной машины
      const encryptionKeyContent = await fs.readFile(encryptionKeyPath, 'utf8');
      
      if (!encryptionKeyContent || encryptionKeyContent.trim().length === 0) {
        throw new Error('Ключ шифрования пуст или не может быть прочитан');
      }
      
      log.success(`✅ Ключ шифрования прочитан с локальной машины (длина: ${encryptionKeyContent.length} символов)`);
      
      // Создаем временный файл с ключом
      const tempKeyPath = `/tmp/encryption_key_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.key`;
      log.info(`📝 Создание временного файла: ${tempKeyPath}`);
      await fs.writeFile(tempKeyPath, encryptionKeyContent, { mode: 0o600 });
      
      // Передаем файл на VDS через SCP
      log.info(`📤 Передача ключа на VDS через SCP...`);
      const scpResult = await execScpCommand(
        tempKeyPath,
        `/home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`,
        options
      );
      
      // Проверяем результат SCP
      if (scpResult.code !== 0) {
        throw new Error(`SCP команда завершилась с ошибкой (код: ${scpResult.code}): ${scpResult.stderr || scpResult.stdout}`);
      }
      
      log.success('✅ Файл ключа успешно передан на VDS через SCP');
      
      // Удаляем временный файл
      try {
        await fs.remove(tempKeyPath);
        log.info(`🗑️ Временный файл удален: ${tempKeyPath}`);
      } catch (removeError) {
        log.warn(`⚠️ Не удалось удалить временный файл ${tempKeyPath}: ${removeError.message}`);
      }
      
      // Устанавливаем правильные права доступа к ключу на VDS
      log.info('🔒 Настройка прав доступа к ключу шифрования на VDS...');
      const chownResult = await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      if (chownResult.code !== 0) {
        log.warn(`⚠️ Не удалось изменить владельца ключа: ${chownResult.stderr}`);
      }
      
      // 🆕 Используем права 644 вместо 600, чтобы контейнеры могли читать ключ
      // Ключ должен быть читаемым для всех процессов в контейнерах
      const chmodResult = await execSshCommand(`chmod 644 /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      if (chmodResult.code !== 0) {
        log.warn(`⚠️ Не удалось изменить права доступа к ключу: ${chmodResult.stderr}`);
      } else {
        log.success('✅ Права доступа к ключу установлены (644 - читаемый для всех)');
      }
      
      // Проверяем, что ключ успешно передан
      log.info('🔍 Проверка передачи ключа...');
      const verifyResult = await execSshCommand(`ls -la /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
      
      if (verifyResult.code === 0) {
        log.success('✅ Ключ шифрования успешно передан на VDS');
        log.info(`📋 Информация о ключе на VDS: ${verifyResult.stdout.trim()}`);
        // Делаем прогресс строго больше предыдущего шага Docker (55%)
        sendWebSocketLog('success', '✅ Ключ шифрования передан на VDS', 'encryption_key', 57);
      } else {
        throw new Error(`Не удалось проверить передачу ключа шифрования: ${verifyResult.stderr || verifyResult.stdout}`);
      }
    } catch (error) {
      log.error('❌ Ошибка передачи ключа шифрования: ' + error.message);
      log.error('📋 Детали ошибки:', error.stack);
      // Даже при ошибке не откатываем прогресс назад относительно предыдущих шагов
      sendWebSocketLog('error', `❌ Ошибка передачи ключа шифрования: ${error.message}`, 'encryption_key', 57);
      // Продолжаем установку, но предупреждаем пользователя
      log.warn('⚠️ Внимание: ключ шифрования не передан. Backend может не запуститься без ключа.');
    }
    
    // 10. Проверка и удаление системного nginx для избежания конфликтов портов
    log.info('🔍 Проверка наличия системного nginx...');
    const nginxCheck = await execSshCommand('systemctl list-units --type=service --state=active,inactive | grep nginx || echo "nginx not found"', options);
    
    if (nginxCheck.stdout.includes('nginx.service')) {
      log.info('⚠️ Обнаружен системный nginx - удаляем для освобождения портов 80/443...');
      
      // Остановка и удаление системного nginx
      await execSshCommand('systemctl stop nginx || true', options);
      await execSshCommand('systemctl disable nginx || true', options);
      await execSshCommand('systemctl mask nginx || true', options);
      await execSshCommand('pkill -f nginx || true', options);
      await execSshCommand('apt-get purge -y nginx nginx-common nginx-full || true', options);
      await execSshCommand('apt-get autoremove -y || true', options);
      
      log.success('✅ Системный nginx полностью удален, порты 80/443 освобождены для Docker nginx');
    } else {
      log.info('ℹ️ Системный nginx не обнаружен - порты 80/443 свободны для Docker nginx');
    }
    
    // 11. Создание временных SSL сертификатов для запуска frontend-nginx
    log.info('🔒 Создание временных SSL сертификатов...');
    await execSshCommand(`mkdir -p /etc/letsencrypt/live/${domain}`, options);
    await execSshCommand(`mkdir -p /var/www/certbot`, options);
    
    // Создаем временный самоподписанный сертификат
    const tempCertCommand = `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/letsencrypt/live/${domain}/privkey.pem -out /etc/letsencrypt/live/${domain}/fullchain.pem -subj '/C=US/ST=State/L=City/O=Organization/CN=${domain}'`;
    await execSshCommand(tempCertCommand, options);
    log.success('Временный SSL сертификат создан');
    // Сообщаем о создании временного сертификата сразу после его генерации,
    // выставляя прогресс между шагами Docker (55%) и экспортом образов (60%),
    // чтобы индикатор прогресса не "откатывался" назад.
    log.info('ℹ️ Временный SSL сертификат создан. Для получения реального SSL сертификата используйте кнопку \"Получить / обновить SSL\" на странице /vds.');
    sendWebSocketLog(
      'info',
      'ℹ️ Временный SSL сертификат установлен. Для получения реального SSL нажмите \"Получить / обновить SSL\" в интерфейсе VDS.',
      'ssl_cert',
      58
    );
    
    // 12. Передача docker-compose.prod.yml на VDS
    log.info('Передача docker-compose.prod.yml на VDS...');
    await execScpCommand('/app/docker-compose.prod.yml', `/home/${dockerUser}/dapp/docker-compose.prod.yml`, options);
    
    // 13.1. 🆕 Nginx конфигурация встроена в Docker образ с валидацией переменных
    log.info('Nginx конфигурация встроена в Docker образ frontend-nginx');
    log.info('Конфигурация будет применена автоматически при запуске контейнера');
    
    if (!domain || !email) {
      log.error('Критическая ошибка: отсутствуют обязательные переменные DOMAIN или EMAIL для nginx');
      throw new Error('Необходимы переменные DOMAIN и EMAIL для настройки nginx');
    }
    log.success(`Nginx будет настроен для домена: ${domain} с email: ${email}`);
    
    // 14. 🆕 Создание полного .env файла со всеми переменными окружения
    const envContent = `# Основные настройки
DOMAIN=${domain}
BACKEND_CONTAINER=dapp-backend
EMAIL=${email}

# Настройки базы данных
DB_NAME=dapp_db
DB_USER=dapp_user
DB_PASSWORD=dapp_password

# Настройки Node.js
NODE_ENV=production
PORT=8000

# Настройки Ollama
OLLAMA_MODEL=qwen2.5:7b
OLLAMA_EMBEDDINGS_MODEL=qwen2.5:7b

# Настройки безопасности
SSL_CERT_PATH=/etc/ssl/certs
SSL_KEY_PATH=/etc/ssl/private

# 🆕 Дополнительные переменные для WebSocket
WS_BACKEND_CONTAINER=dapp-backend`;
    
    // Создаем .env файл локально и передаем через SCP
    await fs.writeFile('/tmp/.env', envContent);
    await execScpCommand('/tmp/.env', `/home/${dockerUser}/dapp/.env`, options);
    await fs.remove('/tmp/.env'); // Очищаем временный файл
    
    // 15. Экспорт и передача Docker образов
    await exportDockerImages(sendWebSocketLog);
    await transferDockerImages({ ...options, dockerUser }, sendWebSocketLog);
    await importDockerImages({ ...options, dockerUser }, sendWebSocketLog);
    await cleanupLocalFiles();
    
    // 16. Запуск приложения
    log.info('Запуск приложения...');
    await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml up -d`, options);
    
    // 16.1. Настройка CORS заголовков в nginx для API
    log.info('🔧 Настройка CORS заголовков в nginx для API...');
    await execSshCommand(
      `cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec frontend-nginx sed -i '/add_header X-XSS-Protection/a\\            add_header Access-Control-Allow-Origin \"https://${domain}\" always;\\            add_header Access-Control-Allow-Methods \"GET, POST, PUT, DELETE, OPTIONS\" always;\\            add_header Access-Control-Allow-Headers \"Content-Type, Authorization, X-Requested-With\" always;\\            add_header Access-Control-Allow-Credentials \"true\" always;' /etc/nginx/nginx.conf`,
      options
    );
    
    // Перезапускаем nginx с новой конфигурацией
    await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml restart frontend-nginx`, options);
    log.success('✅ CORS заголовки настроены в nginx для API');
    
    // 16.0. Получение реального SSL сертификата перенесено в backend (/api/vds/ssl/renew).
    // Здесь агент создает только временный самоподписанный сертификат (см. шаг 11 выше).
    // Для получения/обновления реального сертификата используйте кнопку
    // "Получить / обновить SSL" на странице управления VDS в интерфейсе DLE,
    // которая вызывает /api/vds/ssl/renew на backend.
    
    // 16.2. Ожидание готовности базы данных с повторными попытками
    log.info('Ожидание готовности базы данных...');
    let dbReady = false;
    let attempts = 0;
    const maxAttempts = 30;
    
    while (!dbReady && attempts < maxAttempts) {
      const dbCheckResult = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T postgres pg_isready -U dapp_user -d dapp_db`, options);
      if (dbCheckResult.code === 0) {
        dbReady = true;
        log.success('База данных готова к работе');
      } else {
        attempts++;
        log.info(`Ожидание готовности БД... попытка ${attempts}/${maxAttempts}`);
        await new Promise(resolve => setTimeout(resolve, 2000));
      }
    }
    
    if (!dbReady) {
      log.error('База данных не готова после максимального количества попыток');
    }
    
    // 16.2. 🆕 Проверка целостности переданной базы данных
    log.info('Проверка целостности переданной базы данных...');
    const tableCheckResult = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T postgres psql -U dapp_user -d dapp_db -c "\\dt"`, options);
    
    if (tableCheckResult.code === 0 && tableCheckResult.stdout.includes('email_settings')) {
      log.success('База данных содержит все необходимые таблицы (email_settings найдена)');
      
      // Дополнительная проверка количества таблиц
      const tableCountResult = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T postgres psql -U dapp_user -d dapp_db -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';"`, options);
      if (tableCountResult.code === 0) {
        log.info(`Количество таблиц в базе данных: ${tableCountResult.stdout.trim()}`);
      }
    } else {
      log.warn('Предупреждение: база данных может быть пустой или неполной');
      log.info('Содержимое проверки таблиц: ' + tableCheckResult.stdout);
    }
    
    // 16.3. 🆕 Улучшенная проверка ключа шифрования в контейнере backend
    log.info('Проверка ключа шифрования в backend контейнере...');
    const keyCheckResult = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T backend ls -la /app/ssl/keys/`, options);
    
    if (keyCheckResult.code === 0 && keyCheckResult.stdout.includes('full_db_encryption.key')) {
      log.success('Ключ шифрования найден в backend контейнере');
      
      // Дополнительная проверка содержимого ключа
      const keyContentResult = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T backend head -c 50 /app/ssl/keys/full_db_encryption.key`, options);
      if (keyContentResult.code === 0) {
        log.info('Ключ шифрования доступен для чтения в контейнере');
      }
    } else {
      log.error('Критическая ошибка: ключ шифрования не найден в backend контейнере');
      log.info('Содержимое /app/ssl/keys/: ' + keyCheckResult.stdout);
      log.info('Попытка повторного монтирования ключа...');
      
      // Перезапуск backend контейнера с правильным монтированием
      await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml restart backend`, options);
      await new Promise(resolve => setTimeout(resolve, 5000));
      
      const retryKeyCheck = await execSshCommand(`cd /home/${dockerUser}/dapp && docker compose -f docker-compose.prod.yml exec -T backend ls -la /app/ssl/keys/`, options);
      if (retryKeyCheck.code === 0 && retryKeyCheck.stdout.includes('full_db_encryption.key')) {
        log.success('Ключ шифрования найден после перезапуска backend контейнера');
      } else {
        log.error('Ключ шифрования все еще недоступен после перезапуска');
      }
    }
    
    // 17. Проверка статуса контейнеров
    log.info('Проверка статуса контейнеров...');
    const containersResult = await execSshCommand('docker ps --format "table {{.Names}}\\t{{.Status}}"', options);
    log.info('Статус контейнеров:\\n' + containersResult.stdout);
    
    log.success('VDS настроена и приложение запущено');
    
    // Отправляем финальный статус через WebSocket
    sendWebSocketStatus(true, `VDS ${domain} успешно настроена`);
    sendWebSocketLog('success', `🎉 VDS настроена успешно! Приложение доступно по адресу: https://${domain}`, 'complete', 100);
    
    // Обновляем состояние VDS
    vdsState = {
      configured: true,
      domain: domain,
      vdsIp: vdsIp
    };
    
    res.json({
      success: true,
      message: 'VDS настроена успешно и приложение запущено',
      domain: domain,
      vdsIp: vdsIp,
      applicationUrl: `https://${domain}`,
      systemInfo: systemInfo,
      nextSteps: [
        '✅ Системные требования проверены',
        '✅ VDS настроена и готова к работе',
        '✅ SSL сертификат получен',
        '✅ Docker контейнеры запущены',
        '✅ Приложение доступно по адресу: https://' + domain
      ]
    });
    
  } catch (error) {
    log.error('Ошибка настройки VDS: ' + error.message);
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

// Диагностика VDS
app.post('/vds/diagnostics', logRequest, async (req, res) => {
  try {
    const { 
      vdsIp, 
      sshUser,
      sshHost,
      sshPort = 22,
      sshConnectUser,
      sshConnectPassword
    } = req.body;
    
    // Нормализуем значения (удаляем пробелы)
    const normalizedVdsIp = String(vdsIp || '').trim();
    const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
    const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
    const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
    
    if (!normalizedVdsIp || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
      return res.status(400).json({
        success: false,
        message: 'Необходимы параметры: vdsIp, sshConnectUser, sshConnectPassword'
      });
    }
    
    log.info(`Диагностика VDS: ${normalizedVdsIp}`);
    
    const options = {
      vdsIp: normalizedVdsIp,
      sshHost: normalizedSshHost,
      sshPort,
      sshConnectUser: normalizedSshConnectUser,
      sshConnectPassword: normalizedSshConnectPassword
    };
    
    // 1. Проверка статуса системы
    const systemStatus = await execSshCommand('uptime && free -h && df -h', options);
    
    // 2. Проверка статуса Docker контейнеров
    const dockerStatus = await execSshCommand('docker ps --format "table {{.Names}}\\t{{.Status}}\\t{{.Ports}}"', options);
    
    // 3. Проверка портов
    const portsStatus = await execSshCommand('netstat -tlnp | grep -E ":(80|443|8000|9000|5432|11434|8001)"', options);
    
    // 4. Проверка Docker nginx контейнера
    const nginxStatus = await execSshCommand('docker ps --filter "name=dapp-frontend-nginx" --format "table {{.Names}}\\t{{.Status}}\\t{{.Ports}}" || echo "Docker nginx контейнер не найден"', options);
    
    // 5. Проверка fail2ban статуса
    const fail2banStatus = await execSshCommand('fail2ban-client status sshd', options);
    
    // 6. Проверка SSL сертификата
    const sslStatus = await execSshCommand('certbot certificates', options);
    
    // 7. Проверка логов на ошибки
    const errorLogs = await execSshCommand('journalctl -u docker --since "1 hour ago" --no-pager | tail -20', options);
    
    res.json({
      success: true,
      message: 'Диагностика VDS завершена',
      vdsIp: vdsIp,
      diagnostics: {
        system: systemStatus.stdout,
        docker: dockerStatus.stdout,
        ports: portsStatus.stdout,
        nginx: nginxStatus.stdout,
        fail2ban: fail2banStatus.stdout,
        ssl: sslStatus.stdout,
        errors: errorLogs.stdout
      },
      timestamp: new Date().toISOString()
    });
    
  } catch (error) {
    log.error('Ошибка диагностики VDS: ' + error.message);
    res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

// Graceful shutdown

process.on('SIGTERM', async () => {
  log.info('Получен сигнал SIGTERM, завершаем работу...');
  if (server) {
    server.close(() => {
      log.info('Сервер закрыт');
      process.exit(0);
    });
  } else {
    process.exit(0);
  }
});

process.on('SIGINT', async () => {
  log.info('Получен сигнал SIGINT, завершаем работу...');
  if (server) {
    server.close(() => {
      log.info('Сервер закрыт');
      process.exit(0);
    });
  } else {
    process.exit(0);
  }
});

// Обработка необработанных ошибок
process.on('uncaughtException', (error) => {
  log.error('Необработанная ошибка: ' + error.message);
});

process.on('unhandledRejection', (reason, promise) => {
  log.error('Необработанное отклонение промиса: ' + reason);
});

// WebSocket обработчики
wss.on('connection', (ws) => {
  log.info(`🔌 WebSocket подключение`);
  
  // Отправляем текущий статус новому подключению
  ws.send(JSON.stringify({
    type: 'webssh_status',
    connected: vdsState.configured,
    status: vdsState.configured ? 'connected' : 'disconnected',
    message: vdsState.configured ? `VDS ${vdsState.domain} настроена` : 'VDS не настроена',
    timestamp: new Date().toISOString()
  }));
  
  ws.on('close', () => {
    log.info(`🔌 WebSocket отключение`);
  });
  
  ws.on('error', (error) => {
    log.error('WebSocket ошибка:', error);
  });
});

// Запуск сервера
async function startServer() {
  try {
    log.info('🚀 Запуск WebSSH Agent...');
    
    server.listen(PORT, '0.0.0.0', () => {
      log.info(`🚀 WebSSH Agent запущен на порту ${PORT}`);
      log.success('🛡️ Агент готов к работе');
      log.info('🔌 WebSocket сервер готов для real-time логов');
    });
  } catch (error) {
    log.error('❌ Критическая ошибка запуска сервера:', error);
    process.exit(1);
  }
}

startServer();
