import fs from "fs/promises";
import os from "os";
import path from "path";
import { logger } from "./logger.js";
import { SKYRAMP_MD_CONTENT } from "./skyrampMdContent.js";
import { EXECUTOR_DOCKER_IMAGE, WORKER_DOCKER_IMAGE } from "./versions.js";
import { dockerImageExistsLocally, pullDockerImage } from "./docker.js";
export { WORKER_DOCKER_IMAGE };
// Global skill discovery paths (resolved relative to os.homedir()).
const SKILL_INSTALL_PATHS = [
    { relPath: path.join(".claude", "skills", "skyramp", "SKILLS.md"), description: "Claude Code native skill (~/.claude)", userLevel: true },
    { relPath: path.join(".cursor", "skills", "skyramp", "SKILLS.md"), description: "Cursor native skill (~/.cursor)", userLevel: true },
    { relPath: path.join(".github", "skills", "skyramp.md"), description: "Copilot global skill (~/.github/skills)", userLevel: true },
];
async function pathExists(filePath) {
    try {
        await fs.access(filePath);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Install or refresh SKYRAMP_MD_CONTENT at global skill discovery paths only (~/.claude, ~/.cursor, ~/.github/skills).
 * No workspace path required. Creates files if missing; overwrites if content differs.
 */
export async function ensureSkyrampMdInstalled() {
    const home = os.homedir();
    for (const { relPath, description, userLevel } of SKILL_INSTALL_PATHS) {
        if (!userLevel)
            continue;
        const targetPath = path.join(home, relPath);
        const dir = path.dirname(targetPath);
        try {
            if (!(await pathExists(dir)))
                await fs.mkdir(dir, { recursive: true });
            let existing = "";
            if (await pathExists(targetPath)) {
                try {
                    existing = await fs.readFile(targetPath, "utf8");
                }
                catch {
                    existing = "";
                }
            }
            if (existing === SKYRAMP_MD_CONTENT)
                continue;
            await fs.writeFile(targetPath, SKYRAMP_MD_CONTENT);
            logger.info(existing ? `Refreshed Skyramp skill file (${description})` : `Installed Skyramp skill file (${description})`, { targetPath });
        }
        catch (err) {
            logger.error(`Failed to install Skyramp skill file (${description})`, { targetPath, error: err });
        }
    }
}
/**
 * Skyramp init:
 * install/refresh global skill files (~/.claude, ~/.cursor, ~/.github/skills) (errors logged but do not block init),
 * check which images are missing, then kick off any needed pulls in the background and return immediately.
 * Deduplication for the MCP oninitialized path is handled by registerInitTriggerOnMCPInitialized in this module.
 *
 * Pull improvements over the previous SkyrampClient.initAgent() FFI approach:
 * - Skip-if-present: each image is checked independently via a fast daemon inspect (~5ms)
 *   before any pull is attempted, avoiding unnecessary network calls.
 * - Pull only what is missing: if one image is already present, only the other is pulled.
 *   Previously both images were always pulled regardless.
 * - Parallel pulls: missing images are pulled concurrently via Promise.all, halving
 *   wall-clock time when both need to be downloaded.
 * - Arch-aware: pull platform is detected from process.arch (linux/arm64 on Apple Silicon,
 *   linux/amd64 otherwise) with an automatic amd64 fallback if no arm64 manifest exists
 *   (e.g. skyramp/executor is amd64-only; skyramp/worker supports both).
 * - No FFI overhead: direct Dockerode calls replace the Go shared-library bridge,
 *   removing the cost of loading the .so and marshalling output through a C string.
 * - Cleaner error propagation: structured errors from Dockerode replace parsing a
 *   stringified Go output from the C FFI boundary.
 */
export async function initCheck() {
    // Install/refresh global skill files at discovery paths (~/.claude, ~/.cursor, ~/.github/skills).
    // Gated behind SKYRAMP_FEATURE_SKILL_INSTALL=1 until the feature is ready for general use.
    // Errors are logged per-path inside ensureSkyrampMdInstalled; failures do not block image pull.
    if (process.env.SKYRAMP_FEATURE_SKILL_INSTALL === "1") {
        await ensureSkyrampMdInstalled();
        logger.info("Skyramp skill files installed/refreshed");
    }
    // Check each image independently and pull only what is missing
    const [hasExecutor, hasWorker] = await Promise.all([
        dockerImageExistsLocally(EXECUTOR_DOCKER_IMAGE),
        dockerImageExistsLocally(WORKER_DOCKER_IMAGE),
    ]);
    const pulls = [];
    if (!hasExecutor) {
        logger.info("Pulling missing executor image", { image: EXECUTOR_DOCKER_IMAGE, platform: "linux/amd64" });
        pulls.push(pullDockerImage(EXECUTOR_DOCKER_IMAGE, "linux/amd64"));
    }
    if (!hasWorker) {
        logger.info("Pulling missing worker image", { image: WORKER_DOCKER_IMAGE });
        pulls.push(pullDockerImage(WORKER_DOCKER_IMAGE));
    }
    if (pulls.length === 0) {
        logger.info("Skyramp images already present locally, skipping pull", {
            executor: EXECUTOR_DOCKER_IMAGE,
            worker: WORKER_DOCKER_IMAGE,
        });
    }
    else {
        await Promise.all(pulls);
        logger.info("Skyramp init completed");
    }
}
let initInFlight = false;
let initCompleted = false;
/**
 * Runs initCheck asynchronously after the MCP handshake completes.
 * Prevents duplicate concurrent runs and skips future runs after a successful init.
 * If initCheck fails, a later call can retry within the same process.
 * Assign to server.server.oninitialized in main().
 */
export async function registerInitTriggerOnMCPInitialized() {
    if (initCompleted || initInFlight)
        return;
    initInFlight = true;
    try {
        await initCheck();
        initCompleted = true;
    }
    catch (err) {
        logger.error("Background initialization check failed", { error: err });
    }
    finally {
        initInFlight = false;
    }
}
