/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { execFileSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { isLocalEndpoint } from './local-llm-adapter.js';
let cachedResult = null;
let cacheTimestamp = 0;
const CACHE_TTL_MS = 60_000;
const EXTRA_SEARCH_PATHS = [
    join(homedir(), '.local', 'bin'),
    join(homedir(), '.npm-global', 'bin'),
    '/usr/local/bin',
    '/opt/homebrew/bin',
];
function findBinaryPath(name) {
    try {
        const expandedPath = [...EXTRA_SEARCH_PATHS, ...(process.env.PATH?.split(':') ?? [])].join(':');
        const result = execFileSync('which', [name], {
            encoding: 'utf8',
            timeout: 5000,
            env: { ...process.env, PATH: expandedPath },
        }).trim();
        return result || undefined;
    }
    catch {
        // Fallback: check common paths directly
        for (const dir of EXTRA_SEARCH_PATHS) {
            const candidate = join(dir, name);
            if (existsSync(candidate))
                return candidate;
        }
        return undefined;
    }
}
function probeRuntime(name) {
    const id = name;
    const path = findBinaryPath(name);
    if (!path)
        return { id, available: false };
    let version;
    try {
        const raw = execFileSync(path, ['--version'], { encoding: 'utf8', timeout: 5000 }).trim();
        const match = raw.match(/\d+\.\d+\.\d+/);
        version = match?.[0] ?? raw.split('\n')[0];
    }
    catch {
        // version check failed but binary exists
    }
    return { id, available: true, version, path };
}
/**
 * Detect a local LLM runtime by reading `.setup-agents/config.json` in the workspace
 * and performing a health check against the configured endpoint.
 */
async function detectLocalLlm(workspace) {
    const configPath = join(workspace, '.setup-agents', 'config.json');
    if (!existsSync(configPath)) {
        return { id: 'local', available: false };
    }
    let config;
    try {
        const raw = readFileSync(configPath, 'utf8');
        config = JSON.parse(raw);
    }
    catch {
        return { id: 'local', available: false };
    }
    const llmConfig = config.localLlm;
    if (!llmConfig?.endpoint || !llmConfig.model) {
        return { id: 'local', available: false };
    }
    if (!isLocalEndpoint(llmConfig.endpoint)) {
        return { id: 'local', available: false };
    }
    // Health check: GET /models with a 3s timeout
    try {
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), 3000);
        const response = await fetch(`${llmConfig.endpoint}/models`, {
            signal: controller.signal,
        });
        clearTimeout(timeout);
        if (!response.ok) {
            return { id: 'local', available: false, endpoint: llmConfig.endpoint, model: llmConfig.model };
        }
        return {
            id: 'local',
            available: true,
            model: llmConfig.model,
            provider: llmConfig.provider,
            endpoint: llmConfig.endpoint,
        };
    }
    catch {
        return { id: 'local', available: false, endpoint: llmConfig.endpoint, model: llmConfig.model };
    }
}
export async function detectRuntimes(workspace) {
    const now = Date.now();
    if (cachedResult && now - cacheTimestamp < CACHE_TTL_MS) {
        return cachedResult;
    }
    const runtimes = [probeRuntime('claude'), probeRuntime('codex')];
    if (workspace) {
        const localRuntime = await detectLocalLlm(workspace);
        runtimes.push(localRuntime);
    }
    cachedResult = { runtimes };
    cacheTimestamp = now;
    return cachedResult;
}
/** Clear cache (useful for testing) */
export function clearRuntimeCache() {
    cachedResult = null;
    cacheTimestamp = 0;
}
//# sourceMappingURL=runtime-detector.js.map