import { getModelId, getProviderLabel, getRuntimeLabel, runSideCall, } from "./model-provider.js";
import { logger } from "../logger.js";
import { getCurrentVersion } from "../utils/version-check.js";
import { resolveSerpApiKey } from "../serpapi-credentials.js";
export function buildHelpText() {
    return [
        "VisionClaw Commands:",
        "/help — Show this command list",
        "/status — Show agent status and service health (cached)",
        "/status refresh — Force fresh service healthchecks",
        "/requests — List pending Telegram chat approvals",
        "/approve <id> — Approve a pending Telegram chat request",
        "/block <id> — Block a pending or approved Telegram chat",
        "/chats — List approved and blocked Telegram chats",
        "/tasks — Show current task, todo list, and recent steps",
        "/stop — Stop current task and go to sleep immediately",
        "/sleep <minutes> — Set heartbeat interval (1-1440 min)",
        "/console — Get the web console URL",
        "/restart — Restart the agent",
        "/tunnel restart — Restart the SSH tunnel",
        "/backup — Create a ZIP backup of memories, skills, and sessions",
        "/silent [on|off] — Toggle silent compaction mode (suppress cleanup notifications)",
        "/usage — Show tool call usage report with trends",
        "/screenshot — Take a desktop screenshot and send it back",
        "/rdp [<minutes>] — Get a temporary remote desktop link (default 30, max 480)",
        "/realtime — Open the realtime voice Mini App",
        "/upgrade_beta — Upgrade to the latest beta release",
        "/upgrade_dev <tag> — Upgrade to a dev release by npm dist-tag (e.g. dev-feat-rdp)",
        "/android-use on|off — Toggle Android use tools (requires restart)",
        "/refresh — Force context compaction to fix a confused agent",
        "/chat-monitor on|off — Toggle real-time chat message monitor (WeChat, Feishu)",
    ].join("\n");
}
const HEALTHCHECK_STALE_MS = 5 * 60 * 1000;
const PROBE_TIMEOUT_MS = 5_000;
let healthCheckCache = null;
function getHealthCheckCacheKey(config) {
    return [
        config.provider,
        config.model,
        config.vertexProjectId ?? "",
        config.vertexRegion,
        config.azureOpenAIDeployment ?? "",
        config.ninerouterBaseUrl,
        config.anthropicBaseUrl ?? "",
        config.awsRegion ?? "",
    ].join("|");
}
function formatUptime(seconds) {
    const h = Math.floor(seconds / 3600);
    const m = Math.floor((seconds % 3600) / 60);
    if (h > 0)
        return `${h}h ${m}m`;
    return `${m}m`;
}
function formatAge(ms) {
    const sec = Math.round(ms / 1000);
    if (sec < 60)
        return `${sec}s ago`;
    const min = Math.round(sec / 60);
    return `${min}m ago`;
}
async function withTimeout(promise, ms) {
    return Promise.race([
        promise,
        new Promise((_resolve, reject) => setTimeout(() => { reject(new Error(`Timed out after ${ms}ms`)); }, ms)),
    ]);
}
async function probeModelApi(config) {
    const provider = getProviderLabel(config.provider);
    const start = Date.now();
    try {
        await withTimeout(runSideCall(config, {
            systemPrompt: "You are a health probe. Reply with a single word.",
            userMessage: "ping",
            modelId: getModelId(config, { useCase: "health_probe" }),
            maxTokens: 16,
            timeoutMs: PROBE_TIMEOUT_MS,
        }), PROBE_TIMEOUT_MS);
        const elapsed = Date.now() - start;
        return { name: `${provider} API`, ok: true, detail: `ok (${elapsed}ms)` };
    }
    catch (err) {
        return {
            name: `${provider} API`,
            ok: false,
            detail: `error: ${err instanceof Error ? err.message : String(err)}`,
        };
    }
}
async function probeGoogleCalendar(config) {
    try {
        const { GoogleCalendarClient } = await import("../calendar/google-calendar.js");
        const cal = new GoogleCalendarClient(config);
        const now = new Date();
        const soon = new Date(now.getTime() + 60_000);
        await withTimeout(cal.listUpcomingEvents(now, soon, 1), PROBE_TIMEOUT_MS);
        return { name: "Google Calendar", ok: true, detail: "ok" };
    }
    catch (err) {
        return {
            name: "Google Calendar",
            ok: false,
            detail: `error: ${err instanceof Error ? err.message : String(err)}`,
        };
    }
}
async function probeGoogleDrive(config) {
    try {
        const { GoogleDriveClient } = await import("../drive/google-drive.js");
        const drive = new GoogleDriveClient(config);
        await withTimeout(drive.listFiles({ maxResults: 1 }), PROBE_TIMEOUT_MS);
        return { name: "Google Drive", ok: true, detail: "ok" };
    }
    catch (err) {
        return {
            name: "Google Drive",
            ok: false,
            detail: `error: ${err instanceof Error ? err.message : String(err)}`,
        };
    }
}
async function runHealthChecks(config, agentState) {
    const checks = [
        probeModelApi(config),
        probeGoogleCalendar(config),
        probeGoogleDrive(config),
    ];
    const results = await Promise.allSettled(checks);
    const healthResults = results.map((r, i) => {
        if (r.status === "fulfilled")
            return r.value;
        const names = ["API", "Google Calendar", "Google Drive"];
        return { name: names[i] ?? "unknown", ok: false, detail: `error: ${r.reason instanceof Error ? r.reason.message : String(r.reason)}` };
    });
    // Gmail — always running if the adapter is registered
    const gmailChannels = agentState.channelManager.getEnabledChannels();
    healthResults.push({
        name: "Google Gmail",
        ok: gmailChannels.includes("gmail"),
        detail: gmailChannels.includes("gmail") ? "ok" : "not connected",
    });
    // SerpAPI
    if (resolveSerpApiKey(config)) {
        healthResults.push({ name: "SerpAPI", ok: true, detail: "configured" });
    }
    // Telegram — we received the command, so it's working
    if (gmailChannels.includes("telegram")) {
        healthResults.push({ name: "Telegram", ok: true, detail: "ok" });
    }
    // MCP servers from SDK init
    if (agentState.mcpServers && agentState.mcpServers.length > 0) {
        for (const srv of agentState.mcpServers) {
            healthResults.push({
                name: `MCP: ${srv.name}`,
                ok: srv.status === "connected",
                detail: srv.status,
            });
        }
    }
    return healthResults;
}
export async function buildStatusReport(options) {
    const { agentState, sessionManager, messageQueue, config, ownerConfig, forceRefresh } = options;
    const lines = [];
    lines.push("🤖 VisionClaw Status");
    lines.push(`Version: ${getCurrentVersion()}`);
    lines.push(`Agent: ${config.agentName}`);
    lines.push(`Status: ${agentState.busy ? "Busy" : "Idle"}`);
    lines.push(`Uptime: ${formatUptime(process.uptime())}`);
    lines.push(`Heartbeat: every ${Math.round(ownerConfig.heartbeatIntervalMs / 60_000)}m`);
    lines.push(`Model: ${config.model}`);
    lines.push(`Provider: ${getProviderLabel(config.provider)}`);
    lines.push(`Runtime: ${getRuntimeLabel(config)}`);
    const transcriptInfo = sessionManager.activeSession.getTranscriptInfo();
    lines.push(`Transcript: ${transcriptInfo.kind}${transcriptInfo.sessionFilePath ? ` (${transcriptInfo.sessionFilePath})` : transcriptInfo.sessionDir ? ` (${transcriptInfo.sessionDir})` : ""}`);
    const usage = sessionManager.activeSession.getUsageSnapshot();
    if (usage) {
        const pct = (usage.usedPct * 100).toFixed(1);
        lines.push(`Context: ${pct}% (${usage.usedInputTokens.toLocaleString()} / ${usage.contextWindow.toLocaleString()} tokens)`);
    }
    lines.push(`Queued messages: ${messageQueue.size()}`);
    if (agentState.consoleUrl) {
        lines.push(`Console: ${agentState.consoleUrl}/console`);
    }
    if (agentState.busy) {
        lines.push("");
        lines.push("📋 Current Task:");
        const summary = agentState.activityTracker.getSummary();
        for (const line of summary.split("\n")) {
            lines.push(line);
        }
    }
    // Healthchecks — use cache or run fresh
    const now = Date.now();
    const cacheKey = getHealthCheckCacheKey(config);
    let cacheValid = false;
    if (healthCheckCache?.cacheKey === cacheKey) {
        cacheValid = (now - healthCheckCache.checkedAt) < HEALTHCHECK_STALE_MS;
    }
    let healthResults;
    let checkedAt;
    if (!forceRefresh && cacheValid && healthCheckCache) {
        healthResults = healthCheckCache.results;
        checkedAt = healthCheckCache.checkedAt;
    }
    else {
        try {
            healthResults = await runHealthChecks(config, agentState);
        }
        catch (err) {
            logger.warn(`Healthcheck failed: ${err instanceof Error ? err.message : String(err)}`);
            healthResults = [{ name: "healthcheck", ok: false, detail: "failed to run" }];
        }
        checkedAt = Date.now();
        healthCheckCache = { results: healthResults, checkedAt, cacheKey };
    }
    const age = now - checkedAt;
    const ageStr = age < 1000 ? "just now" : formatAge(age);
    lines.push("");
    lines.push(`🔍 Services (checked ${ageStr}, /status refresh to update):`);
    for (const r of healthResults) {
        const icon = r.ok ? "✅" : "❌";
        lines.push(`${icon} ${r.name}: ${r.detail}`);
    }
    return lines.join("\n");
}
//# sourceMappingURL=status.js.map