const Docker = require('dockerode');
const { exec } = require('child_process');
const path = require('path');
const config = require('./config');

const docker = new Docker({ socketPath: '/var/run/docker.sock' });

/**
 * Récupérer les informations d'un conteneur Docker
 */
async function getContainerInfo(containerName) {
  try {
    const container = docker.getContainer(containerName);
    const info = await container.inspect();
    return {
      id: info.Id.substring(0, 12),
      name: info.Name.replace('/', ''),
      state: info.State.Status,
      running: info.State.Running,
      startedAt: info.State.StartedAt,
      image: info.Config.Image,
      created: info.Created,
    };
  } catch (err) {
    return null;
  }
}

/**
 * Récupérer les logs d'un conteneur
 */
async function getContainerLogs(containerName, tail = 100) {
  try {
    const container = docker.getContainer(containerName);
    const logs = await container.logs({
      stdout: true,
      stderr: true,
      tail: tail,
      timestamps: true,
    });
    // Parse buffer to string
    return logs.toString('utf8');
  } catch (err) {
    return `Erreur lors de la récupération des logs: ${err.message}`;
  }
}

/**
 * Lister tous les conteneurs
 */
async function listContainers() {
  try {
    const containers = await docker.listContainers({ all: true });
    return containers.map((c) => ({
      id: c.Id.substring(0, 12),
      names: c.Names.map((n) => n.replace('/', '')),
      state: c.State,
      status: c.Status,
      image: c.Image,
      created: new Date(c.Created * 1000).toISOString(),
    }));
  } catch (err) {
    return [];
  }
}

/**
 * Redéployer une application (docker compose build + up)
 */
function redeployApp(appConfig) {
  return new Promise((resolve, reject) => {
    const appDir = path.join(config.appsBasePath, appConfig.directory);
    const cmd = `cd ${appDir} && docker compose build --no-cache && docker compose up -d`;

    const startTime = Date.now();
    const logLines = [];

    logLines.push(`[${new Date().toISOString()}] Début du redéploiement de ${appConfig.name}`);
    logLines.push(`[${new Date().toISOString()}] Répertoire: ${appDir}`);
    logLines.push(`[${new Date().toISOString()}] Commande: ${cmd}`);

    const process = exec(cmd, {
      maxBuffer: 1024 * 1024 * 10, // 10MB
      timeout: 300000, // 5 minutes
    });

    process.stdout.on('data', (data) => {
      const lines = data.toString().split('\n').filter(Boolean);
      lines.forEach((line) => {
        logLines.push(`[${new Date().toISOString()}] ${line}`);
      });
    });

    process.stderr.on('data', (data) => {
      const lines = data.toString().split('\n').filter(Boolean);
      lines.forEach((line) => {
        logLines.push(`[${new Date().toISOString()}] ${line}`);
      });
    });

    process.on('close', (code) => {
      const duration = ((Date.now() - startTime) / 1000).toFixed(1);
      if (code === 0) {
        logLines.push(`[${new Date().toISOString()}] Redéploiement terminé avec succès en ${duration}s`);
        resolve({ success: true, logs: logLines.join('\n'), duration });
      } else {
        logLines.push(`[${new Date().toISOString()}] Redéploiement échoué (code: ${code}) en ${duration}s`);
        resolve({ success: false, logs: logLines.join('\n'), duration, exitCode: code });
      }
    });

    process.on('error', (err) => {
      logLines.push(`[${new Date().toISOString()}] Erreur: ${err.message}`);
      resolve({ success: false, logs: logLines.join('\n'), error: err.message });
    });
  });
}

/**
 * Pull le dernier code depuis Gitea
 */
function gitPull(appConfig) {
  return new Promise((resolve, reject) => {
    const srcDir = path.join(config.appsBasePath, appConfig.directory, 'src');
    const cmd = `cd ${srcDir} && git pull origin main 2>&1 || git pull origin master 2>&1`;

    exec(cmd, { timeout: 60000 }, (error, stdout, stderr) => {
      if (error) {
        resolve({ success: false, output: stderr || error.message });
      } else {
        resolve({ success: true, output: stdout });
      }
    });
  });
}



/**
 * Démarrer un conteneur Docker
 */
async function startContainer(containerName) {
  try {
    const container = docker.getContainer(containerName);
    await container.start();
    return { success: true, message: `Conteneur ${containerName} démarré` };
  } catch (err) {
    return { success: false, message: err.message };
  }
}

/**
 * Arrêter un conteneur Docker
 */
async function stopContainer(containerName) {
  try {
    const container = docker.getContainer(containerName);
    await container.stop({ t: 10 });
    return { success: true, message: `Conteneur ${containerName} arrêté` };
  } catch (err) {
    return { success: false, message: err.message };
  }
}

/**
 * Redémarrer un conteneur Docker
 */
async function restartContainer(containerName) {
  try {
    const container = docker.getContainer(containerName);
    await container.restart({ t: 10 });
    return { success: true, message: `Conteneur ${containerName} redémarré` };
  } catch (err) {
    return { success: false, message: err.message };
  }
}

/**
 * Récupérer les métriques système du serveur (CPU, RAM, disque, réseau)
 */
function getServerMetrics() {
  return new Promise((resolve) => {
    const { execSync } = require("child_process");
    try {
      // nsenter -t 1 -m lit les métriques réelles de l'hôte VPS (pas de l'hyperviseur)
      const ns = "nsenter -t 1 -m --";

      // CPU : delta /proc/stat sur 200ms
      let cpuUsage = 0;
      try {
        const s1 = execSync(`${ns} cat /proc/stat | grep '^cpu '`).toString().trim().split(/\s+/);
        execSync("sleep 0.2");
        const s2 = execSync(`${ns} cat /proc/stat | grep '^cpu '`).toString().trim().split(/\s+/);
        const idle1 = parseInt(s1[4]), total1 = s1.slice(1).reduce((a,b)=>a+parseInt(b),0);
        const idle2 = parseInt(s2[4]), total2 = s2.slice(1).reduce((a,b)=>a+parseInt(b),0);
        const dIdle = idle2 - idle1, dTotal = total2 - total1;
        cpuUsage = dTotal > 0 ? Math.round((1 - dIdle / dTotal) * 100) : 0;
      } catch(e) {
        try {
          const cpuRaw = execSync(`${ns} top -bn1 | grep 'Cpu(s)' | awk '{print $2}'`).toString().trim();
          cpuUsage = parseFloat(cpuRaw) || 0;
        } catch(e2) {}
      }

      // RAM : cgroup de l'hôte (fiable même en environnement VPS virtualisé)
      // La RAM totale est lue depuis /sys/fs/cgroup/memory.max de l'hôte
      // La RAM utilisée est calculée via docker stats (valeurs réelles du VPS)
      let memTotal = 8589934592; // 8 Go par défaut
      try {
        const cgroupMax = execSync(`${ns} cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null`).toString().trim();
        if (cgroupMax && cgroupMax !== 'max' && !isNaN(parseInt(cgroupMax))) {
          memTotal = parseInt(cgroupMax);
        }
      } catch(e) {}

      // RAM utilisée : somme de tous les conteneurs via docker stats
      let memUsed = 0;
      try {
        const statsRaw = execSync("docker stats --no-stream --format '{{.MemUsage}}'").toString().trim();
        statsRaw.split('\n').forEach(line => {
          const match = line.match(/([\d.]+)(\w+)\s*\//);
          if (match) {
            const val = parseFloat(match[1]);
            const unit = match[2].toLowerCase();
            if (unit === 'gib') memUsed += val * 1024 * 1024 * 1024;
            else if (unit === 'mib') memUsed += val * 1024 * 1024;
            else if (unit === 'kib') memUsed += val * 1024;
            else memUsed += val;
          }
        });
      } catch(e) {}
      const memAvailable = memTotal - memUsed;
      const memFree = memAvailable;

      // Disque : df sur l'hôte
      const diskRaw = execSync(`${ns} df -B1 / | tail -1`).toString().trim().split(/\s+/);
      const diskTotal = parseInt(diskRaw[1]);
      const diskUsed  = parseInt(diskRaw[2]);
      const diskFree  = parseInt(diskRaw[3]);

      // Uptime et load
      const uptimeRaw = execSync(`${ns} cat /proc/uptime`).toString().trim().split(" ");
      const uptimeSeconds = parseFloat(uptimeRaw[0]);
      const loadRaw = execSync(`${ns} cat /proc/loadavg`).toString().trim().split(" ");
      const load1 = parseFloat(loadRaw[0]);
      const load5 = parseFloat(loadRaw[1]);
      const load15 = parseFloat(loadRaw[2]);

      // Réseau
      let netRx = 0, netTx = 0;
      try {
        const netRaw = execSync(`${ns} cat /proc/net/dev | grep -E 'eth0|ens|enp' | head -1`).toString().trim().split(/\s+/);
        netRx = parseInt(netRaw[1]) || 0;
        netTx = parseInt(netRaw[9]) || 0;
      } catch(e) {}

      resolve({
        cpu: { usage: cpuUsage },
        memory: {
          total: memTotal,
          used: memUsed,
          free: memFree,
          available: memAvailable,
          usagePercent: Math.round((memUsed / memTotal) * 100)
        },
        disk: {
          total: diskTotal,
          used: diskUsed,
          free: diskFree,
          usagePercent: Math.round((diskUsed / diskTotal) * 100)
        },
        uptime: uptimeSeconds,
        load: { load1, load5, load15 },
        network: { rx: netRx, tx: netTx },
        timestamp: new Date().toISOString()
      });
    } catch (err) {
      resolve({ error: err.message });
    }
  });
}
module.exports = {
  docker,
  getContainerInfo,
  getContainerLogs,
  listContainers,
  redeployApp,
  gitPull,
  startContainer,
  stopContainer,
  restartContainer,
  getServerMetrics,
};
