import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { getConfig, SSH_KEY_PATH, SSH_TIMEOUT } from "../config.js";
import { setService } from "../cache.js";
import type { HostConfig, ServiceStatus } from "../types.js";

const execFileAsync = promisify(execFile);

interface Pm2Process {
  name: string;
  pm_id: number;
  pm2_env: {
    status: string;
    pm_uptime: number;
    restart_time: number;
  };
  monit: {
    memory: number;
    cpu: number;
  };
}

async function collectPm2Services(): Promise<void> {
  const now = new Date().toISOString();
  try {
    const { stdout } = await execFileAsync("pm2", ["jlist"], { timeout: 10_000 });
    const processes: Pm2Process[] = JSON.parse(stdout);

    for (const proc of processes) {
      const status: ServiceStatus["status"] =
        proc.pm2_env.status === "online" ? "running" :
        proc.pm2_env.status === "errored" ? "errored" : "stopped";

      const uptimeMs = proc.pm2_env.pm_uptime ? Date.now() - proc.pm2_env.pm_uptime : 0;

      setService({
        id: `pm2-${proc.name}`,
        name: proc.name,
        host: "Local Host",
        status,
        uptime_seconds: Math.floor(uptimeMs / 1000),
        restart_count: proc.pm2_env.restart_time || 0,
        memory_bytes: proc.monit?.memory || 0,
        type: "pm2",
        last_checked: now,
      });
    }
  } catch (e) {
    console.error("Failed to collect PM2 services:", (e as Error).message);
  }
}

const KEY_SERVICES = ["nginx"];

async function collectSystemdServices(host: HostConfig): Promise<void> {
  const now = new Date().toISOString();
  const sshBase = [
    "-i", SSH_KEY_PATH,
    "-o", "StrictHostKeyChecking=accept-new",
    "-o", `ConnectTimeout=${SSH_TIMEOUT}`,
    "-o", "BatchMode=yes",
    `${host.user}@${host.ip}`,
  ];

  for (const svc of KEY_SERVICES) {
    try {
      const { stdout } = await execFileAsync("ssh", [
        ...sshBase,
        `systemctl is-active ${svc} 2>/dev/null && systemctl show ${svc} --property=ActiveEnterTimestamp,NRestarts,MemoryCurrent --no-pager 2>/dev/null`,
      ], { timeout: (SSH_TIMEOUT + 5) * 1000 });

      const lines = stdout.trim().split("\n");
      const isActive = lines[0]?.trim() === "active";
      let uptimeSeconds = 0;
      let restarts = 0;
      let memoryBytes = 0;

      for (const line of lines) {
        if (line.startsWith("ActiveEnterTimestamp=")) {
          const ts = line.split("=")[1]?.trim();
          if (ts) uptimeSeconds = Math.floor((Date.now() - new Date(ts).getTime()) / 1000);
        }
        if (line.startsWith("NRestarts=")) {
          restarts = parseInt(line.split("=")[1]) || 0;
        }
        if (line.startsWith("MemoryCurrent=")) {
          const val = line.split("=")[1]?.trim();
          if (val && val !== "[not set]") memoryBytes = parseInt(val) || 0;
        }
      }

      setService({
        id: `systemd-${host.id}-${svc}`,
        name: `${svc} (${host.name})`,
        host: host.name,
        status: isActive ? "running" : "stopped",
        uptime_seconds: uptimeSeconds,
        restart_count: restarts,
        memory_bytes: memoryBytes,
        type: "systemd",
        last_checked: now,
      });
    } catch (e) {
      setService({
        id: `systemd-${host.id}-${svc}`,
        name: `${svc} (${host.name})`,
        host: host.name,
        status: "errored",
        uptime_seconds: 0,
        restart_count: 0,
        memory_bytes: 0,
        type: "systemd",
        last_checked: now,
      });
      console.error(`Failed to check ${svc} on ${host.name}:`, (e as Error).message);
    }
  }
}

export async function collectAllServices(): Promise<void> {
  const config = getConfig();
  const sshHosts = config.hosts.filter((h) => h.type === "ssh");
  await Promise.allSettled([
    collectPm2Services(),
    ...sshHosts.map((h) => collectSystemdServices(h)),
  ]);
}
