import { spawnSync } from "node:child_process";
import { homedir } from "node:os";
import { join } from "node:path";
import { z } from "zod";
import { isValidHostname } from "./capability";

export const HostLogsResponseSchema = z.object({
  host: z.string().min(1),
  lines: z.array(z.string()),
  truncated: z.boolean(),
}).strict();

export type HostLogsResponse = z.infer<typeof HostLogsResponseSchema>;

export type SyncSpawnExec = (
  cmd: string[],
  options: { shell: false; timeoutMs?: number },
) => { exitCode: number | null; stdout: string; stderr: string };

const LOG_LINE_LIMIT = 200;
const REMOTE_LOG_COMMAND = [
  "journalctl",
  "-u",
  "overdeck-*",
  "--no-pager",
  "-n",
  String(LOG_LINE_LIMIT),
  "--output=short-iso",
];

function defaultSyncSpawn(
  cmd: string[],
  options: { shell: false; timeoutMs?: number },
): { exitCode: number | null; stdout: string; stderr: string } {
  const [command, ...args] = cmd;
  if (!command) throw new Error("spawn command must not be empty");
  const result = spawnSync(command, args, {
    encoding: "utf8",
    timeout: options.timeoutMs,
  });
  return {
    exitCode: result.status,
    stdout: result.stdout ?? "",
    stderr: result.stderr ?? "",
  };
}

function sshArgv(host: string, home: string, remoteCommand: readonly string[]): string[] {
  return [
    "ssh",
    "-p",
    "2222",
    "-i",
    join(home, ".ssh/id_ed25519_buildbox"),
    host,
    ...remoteCommand,
  ];
}

const FALLBACK_LOG_COMMAND = [
  "journalctl",
  "-n",
  String(LOG_LINE_LIMIT),
  "--no-pager",
  "--output=short-iso",
] as const;

function runRemoteLogs(
  hostname: string,
  exec: SyncSpawnExec,
  home: string,
  timeoutMs: number,
): { exitCode: number | null; stdout: string; stderr: string } | null {
  for (const remoteCommand of [REMOTE_LOG_COMMAND, FALLBACK_LOG_COMMAND]) {
    try {
      const result = exec(sshArgv(hostname, home, remoteCommand), { shell: false, timeoutMs });
      if (result.exitCode === 0) return result;
    } catch {
      // try fallback
    }
  }
  return null;
}

export function fetchHostLogs(
  hostname: string,
  exec: SyncSpawnExec = defaultSyncSpawn,
  home: string = homedir(),
  timeoutMs: number = 5_000,
): HostLogsResponse | { error: "invalid-hostname" | "fetch-failed" } {
  if (!isValidHostname(hostname)) {
    return { error: "invalid-hostname" };
  }

  const result = runRemoteLogs(hostname, exec, home, timeoutMs);
  if (!result) {
    return { error: "fetch-failed" };
  }

  const lines = result.stdout
    .split("\n")
    .map((line) => line.replace(/\r$/, ""))
    .filter((line) => line.length > 0);

  return HostLogsResponseSchema.parse({
    host: hostname,
    lines,
    truncated: lines.length >= LOG_LINE_LIMIT,
  });
}
