import { exec } from "../ssh";
import type { AgentKind } from "../agents/types";

export type ScanCandidate = {
  name: string;
  kind: AgentKind;
  deployMethod: "systemd" | "docker" | "pm2";
  deployRef: string;
  evidence: string;
  alreadyImported?: boolean;
};

const KIND_PATTERNS: { kind: AgentKind; rx: RegExp }[] = [
  { kind: "hermes", rx: /hermes/i },
  { kind: "openclaw", rx: /openclaw|claw|chopper/i },
  { kind: "opencode", rx: /opencode|sst|claude-?code/i },
];

export function guessKind(label: string): AgentKind | null {
  for (const { kind, rx } of KIND_PATTERNS) if (rx.test(label)) return kind;
  return null;
}

const PM2_USERS_SCRIPT =
  "for d in /root/.pm2 /home/*/.pm2; do " +
  "  [ -d \"$d\" ] || continue; " +
  "  u=$(stat -c %U \"$d\" 2>/dev/null); " +
  "  echo \"$u\"; " +
  "done | sort -u";

const PM2_PATH_PREFIX =
  "export PATH=\"$HOME/.npm-global/bin:$HOME/.local/bin:$(ls -d $HOME/.nvm/versions/node/*/bin 2>/dev/null | head -1):/usr/local/bin:$PATH\"";

function pm2As(user: string, cmd: string): string {
  if (user === "root") return `bash -c '${PM2_PATH_PREFIX}; ${cmd.replace(/'/g, "'\\''")}'`;
  return `sudo -u ${user} bash -c '${PM2_PATH_PREFIX}; ${cmd.replace(/'/g, "'\\''")}'`;
}

const PM2_PARSE =
  "python3 -c \"import sys,json; " +
  "ps=json.load(sys.stdin); " +
  "[print(p.get('name','?'),'|', p.get('pm2_env',{}).get('status','?'),'|', p.get('pid') or 0,'|', p.get('pm2_env',{}).get('pm_exec_path') or '') for p in ps]\"";

export async function scanHost(hostId: string): Promise<{
  candidates: ScanCandidate[];
  errors: string[];
  raw: Record<string, string>;
}> {
  const errors: string[] = [];
  const raw: Record<string, string> = {};
  const candidates: ScanCandidate[] = [];

  // -------- systemd ----------
  try {
    const r = await exec(
      hostId,
      "systemctl list-units --type=service --no-legend --no-pager --all 2>/dev/null | awk '{print $1}' | grep -iE 'agent|opencode|openclaw|hermes|chopper' | head -50",
      { timeoutMs: 10_000 },
    );
    raw.systemd = r.stdout;
    if (r.ok && r.stdout.trim()) {
      for (const line of r.stdout.split("\n")) {
        const unit = line.trim();
        if (!unit || !unit.endsWith(".service")) continue;
        const base = unit.replace(/\.service$/, "");
        const kind = guessKind(unit) || "opencode";
        candidates.push({
          name: base.replace(/^agent-/, "") || base,
          kind,
          deployMethod: "systemd",
          deployRef: unit,
          evidence: `systemctl: ${unit}`,
        });
      }
    }
  } catch (e: unknown) {
    errors.push(`systemd scan: ${e instanceof Error ? e.message : String(e)}`);
  }

  // -------- docker ----------
  try {
    const r = await exec(
      hostId,
      "command -v docker >/dev/null 2>&1 && docker ps --format '{{.Names}}|{{.Image}}|{{.Status}}' 2>/dev/null || echo NO_DOCKER",
      { timeoutMs: 10_000 },
    );
    raw.docker = r.stdout;
    if (r.stdout && !/NO_DOCKER/.test(r.stdout)) {
      for (const line of r.stdout.split("\n")) {
        if (!line.trim()) continue;
        const [name, image, status] = line.split("|");
        if (!name) continue;
        const kind = guessKind(name) || guessKind(image) || null;
        if (!kind) continue; // chỉ giữ container có vẻ là agent
        candidates.push({
          name,
          kind,
          deployMethod: "docker",
          deployRef: name,
          evidence: `docker: ${image} (${status})`,
        });
      }
    }
  } catch (e: unknown) {
    errors.push(`docker scan: ${e instanceof Error ? e.message : String(e)}`);
  }

  // -------- pm2 (per user) ----------
  try {
    const userList = await exec(hostId, PM2_USERS_SCRIPT, { timeoutMs: 5_000 });
    raw["pm2.users"] = userList.stdout;
    const users = userList.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
    for (const user of users) {
      const j = await exec(
        hostId,
        `${pm2As(user, "pm2 jlist 2>/dev/null")} | ${PM2_PARSE}`,
        { timeoutMs: 10_000 },
      );
      raw[`pm2.${user}`] = j.stdout;
      if (!j.ok) continue;
      for (const line of j.stdout.split("\n")) {
        if (!line.trim()) continue;
        const [name, status, pid, execPath] = line.split("|").map((s) => s.trim());
        if (!name || name === "?") continue;
        const kind = guessKind(name) || guessKind(execPath || "") || null;
        if (!kind) continue;
        const ref = user === "root" ? name : `${user}:${name}`;
        candidates.push({
          name,
          kind,
          deployMethod: "pm2",
          deployRef: ref,
          evidence: `pm2 (${user}): ${status}, pid ${pid}${execPath ? `, ${execPath}` : ""}`,
        });
      }
    }
  } catch (e: unknown) {
    errors.push(`pm2 scan: ${e instanceof Error ? e.message : String(e)}`);
  }

  return { candidates, errors, raw };
}
