#!/usr/bin/env node

import { spawn, spawnSync } from "node:child_process";
import os from "node:os";
import path from "node:path";

export function findProjectRoot(start = process.cwd()) {
  const current = path.resolve(start);
  let cursor = current;
  while (true) {
    if (path.basename(cursor) === ".claude" || existsDir(path.join(cursor, ".claude"))) {
      return path.basename(cursor) === ".claude" ? path.dirname(cursor) : cursor;
    }
    const parent = path.dirname(cursor);
    if (parent === cursor) {
      return current;
    }
    cursor = parent;
  }
}

function existsDir(dir) {
  try {
    return spawnSync("test", ["-d", dir]).status === 0;
  } catch {
    return false;
  }
}

export function buildClaudeEnv() {
  const env = { ...process.env };
  delete env.CLAUDECODE;
  return env;
}

export async function callClaudeText(prompt, { model, timeout = 300_000 } = {}) {
  const args = ["-p", "--output-format", "text"];
  if (model) args.push("--model", model);
  return runClaude(args, { stdin: prompt, timeout });
}

export function runClaudeStream(args, { cwd, timeout = 30_000 } = {}) {
  const child = spawn("claude", args, {
    cwd,
    env: buildClaudeEnv(),
    stdio: ["ignore", "pipe", "ignore"],
  });

  const timer = setTimeout(() => {
    child.kill("SIGKILL");
  }, timeout);

  child.once("exit", () => clearTimeout(timer));
  child.once("error", () => clearTimeout(timer));
  return child;
}

async function runClaude(args, { stdin, timeout = 300_000, cwd } = {}) {
  return new Promise((resolve, reject) => {
    const child = spawn("claude", args, {
      cwd,
      env: buildClaudeEnv(),
      stdio: ["pipe", "pipe", "pipe"],
    });

    let stdout = "";
    let stderr = "";

    const timer = setTimeout(() => {
      child.kill("SIGKILL");
      reject(new Error(`claude -p timed out after ${Math.round(timeout / 1000)}s`));
    }, timeout);

    child.stdout.on("data", (chunk) => {
      stdout += String(chunk);
    });
    child.stderr.on("data", (chunk) => {
      stderr += String(chunk);
    });
    child.on("error", (error) => {
      clearTimeout(timer);
      reject(error);
    });
    child.on("close", (code) => {
      clearTimeout(timer);
      if (code !== 0) {
        reject(new Error(`claude -p exited ${code}\nstderr: ${stderr}`));
        return;
      }
      resolve(stdout);
    });

    if (stdin) {
      child.stdin.write(stdin);
    }
    child.stdin.end();
  });
}

export function uuidFragment() {
  return Math.random().toString(16).slice(2, 10) + Date.now().toString(16).slice(-4);
}

export function openInBrowser(target) {
  const platform = os.platform();
  if (platform === "darwin") {
    spawn("open", [target], { detached: true, stdio: "ignore" }).unref();
    return;
  }
  if (platform === "win32") {
    spawn("cmd", ["/c", "start", "", target], { detached: true, stdio: "ignore" }).unref();
    return;
  }
  spawn("xdg-open", [target], { detached: true, stdio: "ignore" }).unref();
}
