import { NodeSSH } from 'node-ssh';
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const sshConnections = new Map();

function loadConfig() {
  return JSON.parse(readFileSync(join(__dirname, '..', 'config.json'), 'utf-8'));
}

async function getConnection(host, username = "root") {
  if (sshConnections.has(username + "@" + host)) {
    const conn = sshConnections.get(username + "@" + host);
    if (conn.isConnected()) return conn;
  }

  const ssh = new NodeSSH();
  await ssh.connect({
    host,
    username: username,
    privateKeyPath: '/root/.ssh/id_ed25519'
  });

  sshConnections.set(username + "@" + host, ssh);
  return ssh;
}

// Returns: "online", "starting", "stopping", "offline"
export async function getServerStatus(server) {
  try {
    const ssh = await getConnection(server.host, server.sshUser);

    if (server.runtime === 'docker') {
      const result = await ssh.execCommand(`docker inspect --format='{{.State.Status}}' ${server.containerName} 2>/dev/null`);
      const status = result.stdout.trim();
      if (status === 'running') return 'online';
      if (status === 'restarting' || status === 'created') return 'starting';
      if (status === 'removing' || status === 'paused') return 'stopping';
      return 'offline';
    } else if (server.runtime === 'systemd') {
      const result = await ssh.execCommand(`systemctl is-active ${server.serviceName}`);
      const status = result.stdout.trim();
      if (status === 'active') return 'online';
      if (status === 'activating' || status === 'reloading') return 'starting';
      if (status === 'deactivating') return 'stopping';
      return 'offline';
    } else {
      const result = await ssh.execCommand(`screen -ls | grep -E "\\.${server.screenName}[[:space:]]"`);
      if (result.code === 0) {
        const uptimeResult = await ssh.execCommand(`ps -o etimes= -p $(screen -ls | grep "\\.${server.screenName}" | awk '{print $1}' | cut -d. -f1) 2>/dev/null | head -1`);
        const uptime = parseInt(uptimeResult.stdout.trim()) || 999;
        if (uptime < 60) return 'starting';
        return 'online';
      }
      return 'offline';
    }
  } catch (err) {
    console.error(`Failed to get status for ${server.id}:`, err.message);
    return 'offline';
  }
}

export async function startServer(server, options = {}) {
  const ssh = await getConnection(server.host, server.sshUser);

  if (server.runtime === 'docker') {
    // Factorio with specific save
    if (server.type === 'factorio' && options.save) {
      const saveName = options.save.endsWith('.zip') ? options.save.replace('.zip', '') : options.save;
      // Stop and remove existing container
      await ssh.execCommand(`docker stop ${server.containerName} 2>/dev/null || true`);
      await ssh.execCommand(`docker rm ${server.containerName} 2>/dev/null || true`);
      // Start with specific save
      await ssh.execCommand(`
        docker run -d \
          --name ${server.containerName} \
          -p 34197:34197/udp \
          -p 27015:27015/tcp \
          -v /srv/docker/factorio/data:/factorio \
          -e SAVE_NAME=${saveName} -e LOAD_LATEST_SAVE=false \
          -e TZ=Europe/Berlin \
          -e PUID=845 \
          -e PGID=845 \
          --restart=unless-stopped \
          factoriotools/factorio
      `);
    } else {
      await ssh.execCommand(`docker start ${server.containerName}`);
    }
  } else if (server.runtime === 'systemd') {
    await ssh.execCommand(`systemctl start ${server.serviceName}`);
  } else {
    await ssh.execCommand(`screen -S ${server.screenName} -X quit 2>/dev/null`);
    await new Promise(resolve => setTimeout(resolve, 1000));
    await ssh.execCommand(`cd ${server.workDir} && screen -dmS ${server.screenName} ${server.startCmd}`);
  }
}

export async function stopServer(server) {
  const ssh = await getConnection(server.host, server.sshUser);

  if (server.runtime === 'docker') {
    await ssh.execCommand(`docker stop ${server.containerName}`);
  } else if (server.runtime === 'systemd') {
    await ssh.execCommand(`systemctl stop ${server.serviceName}`);
  } else {
    // Different stop commands per server type
    const stopCmd = server.type === 'zomboid' ? 'quit' : 'stop';
    await ssh.execCommand(`screen -S ${server.screenName} -p 0 -X stuff '${stopCmd}\n'`);

    for (let i = 0; i < 30; i++) {
      await new Promise(resolve => setTimeout(resolve, 2000));
      const check = await ssh.execCommand(`screen -ls | grep -E "\\.${server.screenName}[[:space:]]"`);
      if (check.code !== 0) {
        console.log(`Server ${server.id} stopped after ${(i + 1) * 2} seconds`);
        return;
      }
    }

    console.log(`Force killing ${server.id} after timeout`);
    await ssh.execCommand(`screen -S ${server.screenName} -X quit`);
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}

export async function restartServer(server) {
  await stopServer(server);
  await new Promise(resolve => setTimeout(resolve, 2000));
  await startServer(server);
}

export async function getConsoleLog(server, lines = 50) {
  const ssh = await getConnection(server.host, server.sshUser);

  if (server.runtime === 'docker') {
    const result = await ssh.execCommand(`/usr/local/bin/docker-logs-tz ${server.containerName} ${lines}`);
    return result.stdout || result.stderr;
  } else if (server.runtime === 'systemd') {
    const result = await ssh.execCommand(`tail -n ${lines} ${server.workDir}/logs/VRisingServer.log 2>/dev/null || journalctl -u ${server.serviceName} -n ${lines} --no-pager`);
    return result.stdout || result.stderr;
  } else if (server.logFile) {
    const result = await ssh.execCommand(`tail -n ${lines} ${server.logFile} 2>/dev/null || echo No log file found`);
    return result.stdout;
  } else {
    const result = await ssh.execCommand(`tail -n ${lines} ${server.workDir}/logs/latest.log 2>/dev/null || echo No log file found`);
    return result.stdout;
  }
}

export async function getProcessUptime(server) {
  try {
    const ssh = await getConnection(server.host, server.sshUser);

    if (server.runtime === "docker") {
      const result = await ssh.execCommand(`docker inspect --format="{{.State.StartedAt}}" ${server.containerName} 2>/dev/null`);
      if (result.stdout.trim()) {
        const startTime = new Date(result.stdout.trim());
        if (!isNaN(startTime.getTime())) {
          return Math.floor((Date.now() - startTime.getTime()) / 1000);
        }
      }
    } else if (server.runtime === "systemd") {
      const result = await ssh.execCommand(`systemctl show ${server.serviceName} --property=ActiveEnterTimestamp | cut -d= -f2 | xargs -I{} date -d "{}" +%s 2>/dev/null`);
      const startEpoch = parseInt(result.stdout.trim());
      if (!isNaN(startEpoch)) {
        return Math.floor(Date.now() / 1000) - startEpoch;
      }
    } else {
      const result = await ssh.execCommand(`ps -o etimes= -p $(pgrep -f "SCREEN.*${server.screenName}") 2>/dev/null | head -1`);
      const uptime = parseInt(result.stdout.trim());
      if (!isNaN(uptime)) return uptime;
    }
    return 0;
  } catch (err) {
    console.error(`Failed to get uptime for ${server.id}:`, err.message);
    return 0;
  }
}

// ============ FACTORIO-SPECIFIC FUNCTIONS ============

export async function listFactorioSaves(server) {
  const ssh = await getConnection(server.host, server.sshUser);
  const result = await ssh.execCommand('ls -la /srv/docker/factorio/data/saves/*.zip 2>/dev/null');

  if (result.code !== 0 || !result.stdout.trim()) {
    return [];
  }

  const saves = [];
  const lines = result.stdout.trim().split('\n');

  for (const line of lines) {
    const match = line.match(/(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+\s+\d+\s+[\d:]+)\s+(.+)$/);
    if (match) {
      const filename = match[7].split('/').pop();
      // Skip autosaves
      if (filename.startsWith('_autosave')) continue;

      const size = match[5];
      const modified = match[6];

      saves.push({
        name: filename.replace('.zip', ''),
        filename,
        size,
        modified
      });
    }
  }

  return saves;
}

export async function deleteFactorioSave(server, saveName) {
  const ssh = await getConnection(server.host, server.sshUser);
  const filename = saveName.endsWith('.zip') ? saveName : `${saveName}.zip`;

  // Security check - prevent path traversal
  if (filename.includes('/') || filename.includes('..')) {
    throw new Error('Invalid save name');
  }

  const result = await ssh.execCommand(`rm /srv/docker/factorio/data/saves/${filename}`);
  if (result.code !== 0) {
    throw new Error(result.stderr || 'Failed to delete save');
  }
}

export async function createFactorioWorld(server, saveName, settings) {
  const ssh = await getConnection(server.host, server.sshUser);

  // Security check
  if (saveName.includes("/") || saveName.includes("..") || saveName.includes(" ")) {
    throw new Error("Invalid save name - no spaces or special characters allowed");
  }

  // Write map-gen-settings.json
  const settingsJson = JSON.stringify(settings, null, 2);
  await ssh.execCommand(`cat > /srv/docker/factorio/data/config/map-gen-settings.json << 'SETTINGSEOF'
${settingsJson}
SETTINGSEOF`);

  // Create new world using --create flag (correct method)
  console.log(`Creating new Factorio world: ${saveName}`);
  const createResult = await ssh.execCommand(`
    docker run --rm \
      -v /srv/docker/factorio/data:/factorio \
      factoriotools/factorio \
      /opt/factorio/bin/x64/factorio \
      --create /factorio/saves/${saveName}.zip \
      --map-gen-settings /factorio/config/map-gen-settings.json
  `, { execOptions: { timeout: 120000 } });

  console.log("World creation output:", createResult.stdout, createResult.stderr);

  // Verify save was created
  const checkResult = await ssh.execCommand(`ls /srv/docker/factorio/data/saves/${saveName}.zip`);
  if (checkResult.code !== 0) {
    throw new Error("World creation failed - save file not found. Output: " + createResult.stderr);
  }

  return true;
}

export async function getFactorioCurrentSave(server) {
  const ssh = await getConnection(server.host, server.sshUser);

  // Check if container has SAVE_NAME env set
  const envResult = await ssh.execCommand(`docker inspect --format="{{range .Config.Env}}{{println .}}{{end}}" ${server.containerName} 2>/dev/null | grep "^SAVE_NAME="`);
  if (envResult.stdout.trim()) {
    const saveName = envResult.stdout.trim().replace("SAVE_NAME=", "");
    return { save: saveName, source: "configured" };
  }

  // Otherwise find newest save file
  const result = await ssh.execCommand("ls -t /srv/docker/factorio/data/saves/*.zip 2>/dev/null | head -1");
  if (result.stdout.trim()) {
    const filename = result.stdout.trim().split("/").pop().replace(".zip", "");
    return { save: filename, source: "newest" };
  }

  return { save: null, source: null };
}
