import { execFile } from "node:child_process";
import { accessSync, constants } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { promisify } from "node:util";
import { logger } from "../utils/logger.js";
const execFileAsync = promisify(execFile);
const COMMON_LOCATIONS = [
    join(homedir(), ".local", "bin", "opencode"),
    "/usr/local/bin/opencode",
    join(homedir(), ".opencode", "bin", "opencode"),
];
/**
 * Check if a binary path is executable.
 */
function isExecutable(binaryPath) {
    try {
        accessSync(binaryPath, constants.X_OK);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Get the version from an opencode binary by running `<path> --version`.
 * Returns the trimmed stdout or undefined on failure.
 */
async function getVersion(binaryPath) {
    try {
        const { stdout } = await execFileAsync(binaryPath, ["--version"], {
            timeout: 10_000,
        });
        return stdout.trim() || undefined;
    }
    catch {
        return undefined;
    }
}
/**
 * Find the opencode binary path using `which` (or `where` on Windows).
 */
async function findInPath() {
    try {
        const cmd = process.platform === "win32" ? "where" : "which";
        const { stdout } = await execFileAsync(cmd, ["opencode"], {
            timeout: 5_000,
        });
        const resolved = stdout.trim().split("\n")[0]?.trim();
        return resolved || undefined;
    }
    catch {
        return undefined;
    }
}
/**
 * Detect OpenCode installation by checking multiple sources:
 * 1. OPENCODE_PATH environment variable
 * 2. PATH lookup via `which`/`where`
 * 3. Common install locations
 *
 * For each found binary, verifies it's executable and retrieves version info.
 */
export async function detectOpenCode() {
    // 1. Check OPENCODE_PATH env variable
    const envPath = process.env.OPENCODE_PATH;
    if (envPath) {
        logger.debug(`Checking OPENCODE_PATH: ${envPath}`);
        if (isExecutable(envPath)) {
            const version = await getVersion(envPath);
            if (version !== undefined) {
                logger.info(`OpenCode detected via OPENCODE_PATH: ${version} at ${envPath}`);
                return { available: true, path: envPath, version, source: "env" };
            }
        }
        logger.warn(`OPENCODE_PATH set to ${envPath} but binary not executable or version check failed`);
    }
    // 2. Search PATH
    const pathBinary = await findInPath();
    if (pathBinary) {
        logger.debug(`Found opencode in PATH: ${pathBinary}`);
        const version = await getVersion(pathBinary);
        if (version !== undefined) {
            logger.info(`OpenCode detected in PATH: ${version} at ${pathBinary}`);
            return { available: true, path: pathBinary, version, source: "path" };
        }
    }
    // 3. Check common install locations
    for (const location of COMMON_LOCATIONS) {
        if (isExecutable(location)) {
            logger.debug(`Found opencode at common location: ${location}`);
            const version = await getVersion(location);
            if (version !== undefined) {
                logger.info(`OpenCode detected at common location: ${version} at ${location}`);
                return { available: true, path: location, version, source: "common" };
            }
        }
    }
    logger.warn("OpenCode not found in OPENCODE_PATH, PATH, or common locations");
    return { available: false };
}
//# sourceMappingURL=detect.js.map