"use strict";
/**
 * WebSearch Status and Readiness
 *
 * Provides status checking and display functions for WebSearch.
 *
 * @module utils/websearch/status
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.displayWebSearchStatus = exports.getWebSearchReadiness = exports.buildWebSearchReadiness = exports.getCliInstallHints = exports.hasAnyWebSearchCli = exports.getWebSearchCliProviders = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const ui_1 = require("../ui");
const config_manager_1 = require("../config-manager");
const gemini_cli_1 = require("./gemini-cli");
const grok_cli_1 = require("./grok-cli");
const opencode_cli_1 = require("./opencode-cli");
const provider_secrets_1 = require("./provider-secrets");
const types_1 = require("./types");
const config_loader_facade_1 = require("../../config/config-loader-facade");
const PROVIDER_STATE_FILE = 'websearch-provider-state.json';
function hasEnvValue(name) {
    return (process.env[name] || '').trim().length > 0;
}
function hasValidSearxngUrl(url) {
    const normalized = (0, types_1.normalizeSearxngBaseUrl)(url);
    return normalized !== null && normalized !== '';
}
function getProviderStatePath() {
    return (0, path_1.join)((0, config_manager_1.getCcsDir)(), 'cache', PROVIDER_STATE_FILE);
}
function readProviderCooldowns(now = Date.now()) {
    try {
        const statePath = getProviderStatePath();
        if (!(0, fs_1.existsSync)(statePath)) {
            return {};
        }
        const parsed = JSON.parse((0, fs_1.readFileSync)(statePath, 'utf8'));
        const nextCooldowns = {};
        for (const [providerId, entry] of Object.entries(parsed.cooldowns || {})) {
            const until = Number.parseInt(String(entry?.until || ''), 10);
            if (!Number.isFinite(until) || until <= now) {
                continue;
            }
            nextCooldowns[providerId] = {
                reason: typeof entry?.reason === 'string' ? entry.reason : 'rate_limited',
                until,
            };
        }
        return nextCooldowns;
    }
    catch {
        return {};
    }
}
function formatCooldownDuration(until, now = Date.now()) {
    const remainingSec = Math.max(1, Math.ceil((until - now) / 1000));
    if (remainingSec >= 3600) {
        return `~${Math.ceil(remainingSec / 3600)}h`;
    }
    if (remainingSec >= 60) {
        return `~${Math.ceil(remainingSec / 60)}m`;
    }
    return `~${remainingSec}s`;
}
function formatCooldownReason(reason) {
    switch (reason) {
        case 'quota_exhausted':
            return 'quota exhaustion';
        case 'rate_limited':
            return 'rate limiting';
        default:
            return 'a temporary provider error';
    }
}
function applyCooldownStatus(provider, cooldowns, now = Date.now()) {
    if (!(provider.enabled && provider.available)) {
        return provider;
    }
    const cooldown = cooldowns[provider.id];
    if (!cooldown) {
        return provider;
    }
    return {
        ...provider,
        available: false,
        detail: `Cooling down ${formatCooldownDuration(cooldown.until, now)} after ${formatCooldownReason(cooldown.reason)}`,
    };
}
function getLegacyProviderStatuses() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    const geminiStatus = (0, gemini_cli_1.getGeminiCliStatus)();
    const grokStatus = (0, grok_cli_1.getGrokCliStatus)();
    const opencodeStatus = (0, opencode_cli_1.getOpenCodeCliStatus)();
    const geminiAuthed = geminiStatus.installed && (0, gemini_cli_1.isGeminiAuthenticated)();
    return [
        {
            id: 'gemini',
            kind: 'legacy-cli',
            name: 'Gemini CLI',
            command: 'gemini',
            enabled: wsConfig.providers?.gemini?.enabled ?? false,
            available: geminiAuthed,
            version: geminiStatus.version ?? null,
            installCommand: 'npm install -g @google/gemini-cli',
            docsUrl: 'https://github.com/google-gemini/gemini-cli',
            requiresApiKey: false,
            description: 'Optional legacy LLM fallback with Google web search.',
            detail: geminiStatus.installed
                ? geminiAuthed
                    ? 'Authenticated'
                    : "Run 'gemini' to login"
                : 'Not installed',
        },
        {
            id: 'opencode',
            kind: 'legacy-cli',
            name: 'OpenCode',
            command: 'opencode',
            enabled: wsConfig.providers?.opencode?.enabled ?? false,
            available: opencodeStatus.installed,
            version: opencodeStatus.version ?? null,
            installCommand: 'curl -fsSL https://opencode.ai/install | bash',
            docsUrl: 'https://github.com/sst/opencode',
            requiresApiKey: false,
            description: 'Optional legacy LLM fallback via OpenCode.',
            detail: opencodeStatus.installed ? 'Installed' : 'Not installed',
        },
        {
            id: 'grok',
            kind: 'legacy-cli',
            name: 'Grok CLI',
            command: 'grok',
            enabled: wsConfig.providers?.grok?.enabled ?? false,
            available: grokStatus.installed && hasEnvValue('GROK_API_KEY'),
            version: grokStatus.version ?? null,
            installCommand: 'npm install -g @vibe-kit/grok-cli',
            docsUrl: 'https://github.com/superagent-ai/grok-cli',
            requiresApiKey: true,
            apiKeyEnvVar: 'GROK_API_KEY',
            description: 'Optional legacy LLM fallback with xAI Grok.',
            detail: grokStatus.installed
                ? hasEnvValue('GROK_API_KEY')
                    ? 'Ready'
                    : 'Set GROK_API_KEY'
                : 'Not installed',
        },
    ];
}
/**
 * Get all WebSearch providers with their current status.
 */
function getWebSearchCliProviders() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    const apiKeyStates = (0, provider_secrets_1.getWebSearchApiKeyStates)();
    const cooldowns = readProviderCooldowns();
    const providers = [
        {
            id: 'exa',
            kind: 'backend',
            name: 'Exa',
            enabled: wsConfig.providers?.exa?.enabled ?? false,
            available: (wsConfig.providers?.exa?.enabled ?? false) && apiKeyStates.exa.available,
            version: null,
            docsUrl: 'https://docs.exa.ai/reference/search',
            requiresApiKey: true,
            apiKeyEnvVar: 'EXA_API_KEY',
            description: 'API-backed search with strong relevance and content extraction.',
            detail: apiKeyStates.exa.available
                ? `API key detected (${wsConfig.providers?.exa?.max_results ?? 5} results)`
                : apiKeyStates.exa.configured
                    ? 'Stored in dashboard, but Global Env is disabled'
                    : 'Set EXA_API_KEY',
        },
        {
            id: 'tavily',
            kind: 'backend',
            name: 'Tavily',
            enabled: wsConfig.providers?.tavily?.enabled ?? false,
            available: (wsConfig.providers?.tavily?.enabled ?? false) && apiKeyStates.tavily.available,
            version: null,
            docsUrl: 'https://docs.tavily.com/documentation/api-reference/endpoint/search',
            requiresApiKey: true,
            apiKeyEnvVar: 'TAVILY_API_KEY',
            description: 'Search API optimized for agent workflows and concise web result synthesis.',
            detail: apiKeyStates.tavily.available
                ? `API key detected (${wsConfig.providers?.tavily?.max_results ?? 5} results)`
                : apiKeyStates.tavily.configured
                    ? 'Stored in dashboard, but Global Env is disabled'
                    : 'Set TAVILY_API_KEY',
        },
        {
            id: 'brave',
            kind: 'backend',
            name: 'Brave Search',
            enabled: wsConfig.providers?.brave?.enabled ?? false,
            available: (wsConfig.providers?.brave?.enabled ?? false) && apiKeyStates.brave.available,
            version: null,
            docsUrl: 'https://brave.com/search/api/',
            requiresApiKey: true,
            apiKeyEnvVar: 'BRAVE_API_KEY',
            description: 'API-backed web search with cleaner result metadata.',
            detail: apiKeyStates.brave.available
                ? `API key detected (${wsConfig.providers?.brave?.max_results ?? 5} results)`
                : apiKeyStates.brave.configured
                    ? 'Stored in dashboard, but Global Env is disabled'
                    : 'Set BRAVE_API_KEY',
        },
        {
            id: 'searxng',
            kind: 'backend',
            name: 'SearXNG',
            enabled: wsConfig.providers?.searxng?.enabled ?? false,
            available: (wsConfig.providers?.searxng?.enabled ?? false) &&
                hasValidSearxngUrl(wsConfig.providers?.searxng?.url),
            version: null,
            docsUrl: 'https://docs.searxng.org/dev/search_api.html',
            requiresApiKey: false,
            description: 'Configurable SearXNG JSON backend for self-hosted or public instances.',
            detail: hasValidSearxngUrl(wsConfig.providers?.searxng?.url)
                ? `Configured (${wsConfig.providers?.searxng?.max_results ?? 5} results)`
                : 'Set a valid SearXNG base URL',
        },
        {
            id: 'duckduckgo',
            kind: 'backend',
            name: 'DuckDuckGo',
            enabled: wsConfig.providers?.duckduckgo?.enabled ?? true,
            available: wsConfig.providers?.duckduckgo?.enabled ?? true,
            version: null,
            docsUrl: 'https://duckduckgo.com',
            requiresApiKey: false,
            description: 'Default built-in HTML search backend. Zero setup.',
            detail: `Built-in (${wsConfig.providers?.duckduckgo?.max_results ?? 5} results)`,
        },
    ];
    return [...providers, ...getLegacyProviderStatuses()].map((provider) => applyCooldownStatus(provider, cooldowns));
}
exports.getWebSearchCliProviders = getWebSearchCliProviders;
/**
 * Check if any WebSearch provider is currently ready.
 */
function hasAnyWebSearchCli() {
    return getWebSearchCliProviders().some((provider) => provider.enabled && provider.available);
}
exports.hasAnyWebSearchCli = hasAnyWebSearchCli;
/**
 * Get setup hints when no providers are ready.
 */
function getCliInstallHints() {
    if (hasAnyWebSearchCli()) {
        return [];
    }
    return [
        'WebSearch: no ready providers',
        '    Enable DuckDuckGo in Settings > WebSearch for zero-setup search',
        '    Or enable SearXNG and set a valid base URL (must support /search?format=json)',
        '    Or export EXA_API_KEY, TAVILY_API_KEY, or BRAVE_API_KEY for API-backed search',
        '    Optional legacy fallback: npm i -g @google/gemini-cli',
    ];
}
exports.getCliInstallHints = getCliInstallHints;
function buildWebSearchReadiness(enabled, providers) {
    if (!enabled) {
        return {
            readiness: 'unavailable',
            message: 'Disabled in config',
            providers,
        };
    }
    const enabledProviders = providers.filter((provider) => provider.enabled);
    const readyProviders = enabledProviders.filter((provider) => provider.available);
    if (readyProviders.length > 0) {
        return {
            readiness: 'ready',
            message: `Ready (${readyProviders.map((provider) => provider.name).join(' + ')})`,
            providers,
        };
    }
    if (enabledProviders.length > 0) {
        return {
            readiness: 'needs_setup',
            message: enabledProviders
                .map((provider) => `${provider.name}: ${provider.detail}`)
                .join(' | '),
            providers,
        };
    }
    return {
        readiness: 'unavailable',
        message: 'Enable at least one provider in Settings > WebSearch',
        providers,
    };
}
exports.buildWebSearchReadiness = buildWebSearchReadiness;
/**
 * Get WebSearch readiness status for display.
 */
function getWebSearchReadiness() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    const providers = getWebSearchCliProviders();
    return buildWebSearchReadiness(wsConfig.enabled, providers);
}
exports.getWebSearchReadiness = getWebSearchReadiness;
/**
 * Display WebSearch status (single line, equilibrium UX).
 */
function displayWebSearchStatus() {
    const status = getWebSearchReadiness();
    switch (status.readiness) {
        case 'ready':
            console.error((0, ui_1.ok)(`WebSearch: ${status.message}`));
            break;
        case 'needs_setup':
            console.error((0, ui_1.warn)(`WebSearch: ${status.message}`));
            break;
        case 'unavailable':
            console.error((0, ui_1.fail)(`WebSearch: ${status.message}`));
            for (const [index, hint] of getCliInstallHints().entries()) {
                console.error(index === 0 ? (0, ui_1.info)(hint) : hint);
            }
            break;
    }
}
exports.displayWebSearchStatus = displayWebSearchStatus;
//# sourceMappingURL=status.js.map