import { mkdirSync, openSync, readFileSync, readlinkSync, realpathSync, rmSync } from "node:fs";
import { resolve } from "node:path";

const action = process.argv[2];
const root = process.cwd();
const repoRoot = resolve(root, "../..");
const stateDir = resolve(root, ".state/tasktree-native");
const pidPath = resolve(stateDir, "supervisor.pid");
const logPath = resolve(stateDir, "supervisor.log");
const servicesLogPath = resolve(stateDir, "services.log");

function readPid(): number | null {
  try {
    const pid = Number(readFileSync(pidPath, "utf8").trim());
    return Number.isInteger(pid) && pid > 0 ? pid : null;
  } catch {
    return null;
  }
}

function isRunning(pid: number | null): boolean {
  if (!pid) return false;
  try {
    process.kill(pid, 0);
    try {
      const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
      const state = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[0];
      if (state === "Z") return false;
    } catch {
      // Non-Linux hosts do not expose /proc; the signal probe remains authoritative there.
    }
    return true;
  } catch {
    return false;
  }
}

function canonicalPath(path: string): string {
  try {
    return realpathSync(path);
  } catch {
    return resolve(path);
  }
}

function runningProcessCwd(pid: number | null): string | null {
  if (!pid) return null;
  try {
    return canonicalPath(readlinkSync(`/proc/${pid}/cwd`));
  } catch {
    return null;
  }
}

function gitCommit(workspace: string | null): string | null {
  if (!workspace) return null;
  const result = Bun.spawnSync(["git", "-C", workspace, "rev-parse", "HEAD"], {
    stdout: "pipe",
    stderr: "ignore"
  });
  if (result.exitCode !== 0) return null;
  return result.stdout.toString().trim() || null;
}

async function waitForExit(pid: number): Promise<boolean> {
  const deadline = Date.now() + 15000;
  while (Date.now() < deadline) {
    if (!isRunning(pid)) return true;
    await Bun.sleep(25);
  }
  return !isRunning(pid);
}

mkdirSync(stateDir, { recursive: true });
const pid = readPid();

if (action === "start") {
  if (isRunning(pid)) {
    console.log(JSON.stringify({ ok: true, action, status: "already-running", pid, logPath }));
    process.exit(0);
  }
  const requiredEnvironment = ["TASKTREE_NATIVE_DATABASE_ENV_FILE", "TASKTREE_TEMPORAL_ADDRESS"] as const;
  const missingEnvironment = requiredEnvironment.filter((key) => !process.env[key]?.trim());
  if (missingEnvironment.length > 0) {
    console.error(JSON.stringify({
      ok: false,
      action,
      status: "preflight-failed",
      error: "missing-required-environment",
      missingEnvironment,
      valuesPrinted: false
    }));
    process.exit(1);
  }
  rmSync(pidPath, { force: true });
  const logFd = openSync(logPath, "a");
  const child = Bun.spawn([process.execPath, "scripts/tasktree-native-supervisor.ts"], {
    cwd: root,
    env: process.env,
    stdin: "ignore",
    stdout: logFd,
    stderr: logFd,
    detached: true
  });
  child.unref();
  await Bun.write(pidPath, `${child.pid}\n`);
  await Bun.sleep(150);
  if (!isRunning(child.pid)) {
    rmSync(pidPath, { force: true });
    console.error(JSON.stringify({
      ok: false,
      action,
      status: "start-failed",
      error: "supervisor-exited-before-ready",
      logPath,
      valuesPrinted: false
    }));
    process.exit(1);
  }
  console.log(JSON.stringify({ ok: true, action, status: "started", pid: child.pid, logPath }));
} else if (action === "stop") {
  if (!isRunning(pid)) {
    rmSync(pidPath, { force: true });
    console.log(JSON.stringify({ ok: true, action, status: "stopped" }));
  } else {
    process.kill(pid!, "SIGTERM");
    if (!await waitForExit(pid!)) {
      console.error(JSON.stringify({ ok: false, action, status: "stop-timeout", pid }));
      process.exit(1);
    }
    rmSync(pidPath, { force: true });
    console.log(JSON.stringify({ ok: true, action, status: "stopped", pid }));
  }
} else if (action === "status") {
  const running = isRunning(pid);
  const processCwd = runningProcessCwd(running ? pid : null);
  const sourceWorkspace = processCwd ? canonicalPath(resolve(processCwd, "../..")) : null;
  const expectedProcessCwd = canonicalPath(root);
  const expectedSourceWorkspace = canonicalPath(repoRoot);
  console.log(JSON.stringify({
    ok: running,
    action,
    status: running ? "running" : "stopped",
    pid: running ? pid : null,
    logPath,
    servicesLogPath,
    provenance: {
      expectedSourceWorkspace,
      sourceWorkspace,
      processCwd,
      commit: gitCommit(sourceWorkspace),
      workspaceMatches: sourceWorkspace === expectedSourceWorkspace,
      processCwdMatches: processCwd === expectedProcessCwd
    }
  }));
  process.exit(running ? 0 : 1);
} else if (action === "logs") {
  for (const path of [logPath, servicesLogPath]) {
    try {
      console.log(readFileSync(path, "utf8").trimEnd().split("\n").slice(-80).join("\n"));
    } catch {
      // A log is optional until its process has started.
    }
  }
} else {
  console.error("usage: bun run scripts/tasktree-native-service.ts <start|stop|status|logs>");
  process.exit(2);
}
