import fs from "node:fs/promises";
import path from "node:path";
import { getAppPaths } from "../config.js";
import { log } from "./logger.js";
// Anthropic's OAuth edge rejects requests whose claude-cli UA version lags too
// far behind the actual Claude Code release. Resolve dynamically from the npm
// registry so we never ship a stale-version time bomb.
const NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const FETCH_TIMEOUT_MS = 3000;
// Last known good version at publish time. Used only when the npm fetch fails
// and no on-disk cache exists (e.g. first run on an offline machine). Keep
// reasonably current — bump on each ezcoder release.
const FALLBACK_VERSION = "2.1.88";
let memoryCache = null;
let inflight = null;
function cachePath() {
    return path.join(getAppPaths().agentDir, "claude-code-version.json");
}
async function readDiskCache() {
    try {
        const raw = await fs.readFile(cachePath(), "utf-8");
        const parsed = JSON.parse(raw);
        if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
            return parsed;
        }
        return null;
    }
    catch {
        return null;
    }
}
async function writeDiskCache(data) {
    try {
        await fs.mkdir(getAppPaths().agentDir, { recursive: true, mode: 0o700 });
        await fs.writeFile(cachePath(), JSON.stringify(data), { mode: 0o600 });
    }
    catch (err) {
        log("WARN", "claude-code-version", `Failed to write cache: ${err instanceof Error ? err.message : String(err)}`);
    }
}
async function fetchLatest() {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
    try {
        const response = await fetch(NPM_LATEST_URL, { signal: controller.signal });
        if (!response.ok)
            return null;
        const data = (await response.json());
        if (typeof data.version === "string" && /^\d/.test(data.version)) {
            return data.version;
        }
        return null;
    }
    catch {
        return null;
    }
    finally {
        clearTimeout(timer);
    }
}
/**
 * Resolve the current Claude Code release version for spoofing the claude-cli
 * User-Agent on OAuth and inference requests. Cached in-memory for the process
 * lifetime and on disk for 24h. Falls back to a hardcoded constant if the npm
 * registry is unreachable and no cache exists.
 */
export async function getClaudeCodeVersion() {
    if (memoryCache && Date.now() < memoryCache.expiresAt) {
        return memoryCache.version;
    }
    if (inflight)
        return inflight;
    inflight = (async () => {
        const disk = await readDiskCache();
        const diskFresh = disk && Date.now() - disk.fetchedAt < CACHE_TTL_MS;
        if (disk && diskFresh) {
            memoryCache = { version: disk.version, expiresAt: Date.now() + CACHE_TTL_MS };
            return disk.version;
        }
        const fetched = await fetchLatest();
        if (fetched) {
            await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
            memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
            return fetched;
        }
        // npm unreachable — prefer stale disk cache over hardcoded fallback.
        const resolved = disk?.version ?? FALLBACK_VERSION;
        // Short TTL so we retry the npm fetch soon, but don't hammer it.
        memoryCache = { version: resolved, expiresAt: Date.now() + 5 * 60 * 1000 };
        log("WARN", "claude-code-version", `Failed to fetch latest Claude Code version; using ${resolved}`);
        return resolved;
    })();
    try {
        return await inflight;
    }
    finally {
        inflight = null;
    }
}
/** Build the User-Agent string Anthropic's OAuth + inference edges expect. */
export async function getClaudeCliUserAgent() {
    const version = await getClaudeCodeVersion();
    return `claude-cli/${version} (external, cli)`;
}
//# sourceMappingURL=claude-code-version.js.map