import { randomUUID } from "node:crypto";
import { ensureOutDir } from "../artifacts/artifacts.js";
import { buildFileInstructions, outboxPath, stageFile, } from "../artifacts/attachments.js";
import { CODEX_AGENT_CAPABILITIES, agentLabel, agentReasoningLabel, agentReasoningOptions, isAgentId, } from "../agents/shared/agent.js";
import { getExternalSnapshotForSession } from "../agents/shared/agent-activity.js";
import { listAgentAdapterDescriptors } from "../agents/shared/agent-adapter.js";
import { AgentUpdateManager } from "../agents/shared/agent-updates.js";
import { createAgentSessionService, enabledAgents } from "../agents/shared/agent-factory.js";
import { AuditLogStore } from "../access/audit-log.js";
import { BotPreferencesStore } from "../state/bot-preferences.js";
import { ChannelCommandService } from "../channels/shared/channel-command-service.js";
import { ChannelTurnService } from "../channels/shared/channel-turn-service.js";
import { activeSessionSourceForContextKey, ChannelMirrorRegistry } from "../channels/shared/channel-mirror-registry.js";
import { listThreads as listCodexThreads } from "../agents/codex/codex-state.js";
import { friendlyErrorText } from "../core/error-messages.js";
import { clearLogFile, getAgentUpdateLogPath, getConnectorHealth, getConnectorLogPath, getPackageVersion, getUpdateLogPath, getVersionChecks, readConnectorState, readFormattedLogTail, spawnConnectorRestart, spawnSelfUpdate } from "../support/operations.js";
import { PromptStore, toPromptEnvelope } from "../state/prompt-store.js";
import { UnifiedJobStore } from "../state/job-store.js";
import { buildRuntimeMetrics } from "./metrics.js";
import { RelayArtifactService } from "./relay-artifact-service.js";
import { RelayAuthService } from "./relay-auth-service.js";
import { RelayExternalActivityMonitor } from "./relay-external-activity-monitor.js";
import { RelayQueueService } from "./relay-queue-service.js";
import { RuntimeSnapshotCache } from "./runtime-cache.js";
import { activeSessionPriority, activityToUnifiedJob, agentUpdateStatusToUnified, dedupeJobs, hostLoginCommand, hostLogoutCommand, isPromptTerminalActivity, normalizeMimeType, promptActivityToUnifiedJob, shouldRefreshActiveSessions, taskToUnifiedJob, uploadFileDtos, } from "./relay-runtime-helpers.js";
import { RelayDashboardService } from "./relay-dashboard-service.js";
import { capabilitiesOf } from "../channels/shared/bot-rendering.js";
import { renderSessionInfoPlain, renderSessionUsageRows } from "../channels/shared/session-format.js";
import { SessionLockStore } from "../access/session-locks.js";
import { SessionRegistry } from "../state/session-registry.js";
import { createSupportBundle } from "../support/support-bundle.js";
import { getObservabilityRegistry } from "../observability/observability-registry.js";
import { PeerStore } from "../peers/peer-store.js";
import { transcribeAudio } from "../artifacts/voice.js";
import { WebActivityStore, WebChatStore, } from "../web/web-state.js";
import { ensureSubscriberExternalMonitor, stopSubscriberExternalMonitorIfIdle } from "./relay-subscriber-external-monitor.js";
import { evaluateWorkspacePolicy, filterAllowedWorkspaces } from "../core/workspace-policy.js";
export const WEB_CONTEXT_KEY = "web:dashboard";
const ACTIVE_CODEX_DISCOVERY_LIMIT = 200;
const ACTIVE_ACTIVITY_TTL_MS = 6 * 60 * 60 * 1000;
const MAX_WEB_SESSION_PAGE_SIZE = 50;
const MAX_CHAT_HISTORY = 250;
export function relayRuntimeSubscribe(runtime, callback) {
    runtime.subscribers.add(callback);
    ensureSubscriberExternalMonitor(runtime);
    void runtime.snapshot().then((data) => callback({ type: "snapshot", data })).catch(() => { });
    void runtime.chatHistory().then((messages) => callback({ type: "chat_history", messages })).catch(() => { });
    callback({ type: "queue_update", queue: runtime.queue(), paused: runtime.queuePaused() });
    void runtime.activeSessions().then((active) => callback({ type: "active_sessions_update", active })).catch(() => { });
    callback({ type: "activity_update", events: runtime.activity({ limit: 50 }) });
    return () => {
        runtime.subscribers.delete(callback);
        stopSubscriberExternalMonitorIfIdle(runtime);
    };
}
export async function relayRuntimeSnapshot(runtime) {
    const session = await runtime.getSession(true);
    const info = runtime.publicInfo(session);
    return {
        session: info,
        sessionText: renderSessionInfoPlain(info),
        queue: runtime.queue(),
        queuePaused: runtime.queuePaused(),
        processing: session.isProcessing(),
        enabledAgents: enabledAgents(runtime.config),
        workspaces: filterAllowedWorkspaces(session.listWorkspaces(), runtime.config),
    };
}
export async function relayRuntimeStatus(runtime) {
    const cliOptions = runtime.cliPathOptions();
    const [health, versionChecks, snapshot] = await Promise.all([
        getConnectorHealth(cliOptions),
        getVersionChecks(cliOptions),
        runtime.snapshot(),
    ]);
    return {
        health,
        versionChecks,
        snapshot,
    };
}
export async function relayRuntimeBootstrapStatus(runtime) {
    return {
        health: {
            version: await getPackageVersion(),
            state: await readConnectorState(),
        },
        snapshot: await runtime.snapshot(),
    };
}
export async function relayRuntimeVersion(runtime, options = {}) {
    return runtime.dashboardService.version(options);
}
export async function relayRuntimeDiagnostics(runtime) {
    return runtime.dashboardService.diagnostics();
}
export async function relayRuntimeAdapterHealth(runtime) {
    return runtime.dashboardService.adapterHealth();
}
export function relayRuntimePermissions(runtime) {
    return {
        mode: "users",
        message: "Access is managed by NordRelay users, groups, Telegram identities, Telegram chat access records, Discord identities, and Discord channel access records.",
    };
}
export async function relayRuntimeMetrics(runtime) {
    const [active, jobs] = await Promise.all([
        runtime.activeSessions(),
        runtime.jobs(),
    ]);
    return buildRuntimeMetrics({
        queueLength: runtime.queueService.length(),
        queuePaused: runtime.queueService.isPaused(),
        activeTurnCount: active.sessions.length,
        jobs: jobs.jobs,
        activity: runtime.activity({ limit: 500 }),
    });
}
export function relayRuntimeObservability() {
    return getObservabilityRegistry().snapshot({ peerNames: observabilityPeerNames() });
}
function observabilityPeerNames() {
    const names = new Map();
    try {
        for (const peer of new PeerStore().listPublic()) {
            if (peer.id && peer.name)
                names.set(peer.id, peer.name);
            if (peer.nodeId && peer.name)
                names.set(peer.nodeId, peer.name);
        }
    }
    catch {
        // Observability must stay available even if peer metadata is temporarily unreadable.
    }
    return names;
}
export function relayRuntimeAudit(runtime, options = 50) {
    return runtime.auditStore.list(options);
}
export function relayRuntimeAuditPage(runtime, options = {}) {
    return runtime.auditStore.listPage(options);
}
export async function relayRuntimeSupportBundle(runtime, actor) {
    const bundle = await createSupportBundle({
        config: runtime.config,
        diagnostics: await runtime.diagnostics(),
        observability: runtime.observability(),
        adapterHealth: await runtime.adapterHealth(),
        auditEvents: runtime.auditStore.list(100),
        agentUpdateJobs: runtime.agentUpdates.list(),
        source: "web",
    });
    runtime.appendActivity({
        source: "web",
        status: "info",
        type: "diagnostics_bundle_exported",
        threadId: null,
        workspace: runtime.config.workspace,
        actor,
        detail: bundle.path,
    });
    runtime.appendAudit({
        action: "command",
        status: "ok",
        contextKey: runtime.contextKey,
        actor,
        description: "export diagnostics bundle",
        detail: bundle.path,
    });
    return bundle;
}
export async function relayRuntimeLogs(runtime, target = "connector", options = 100) {
    if (target === "update") {
        return readFormattedLogTail(options, getUpdateLogPath());
    }
    if (target === "agent-updates") {
        return readFormattedLogTail(options, getAgentUpdateLogPath());
    }
    return readFormattedLogTail(options);
}
export function relayRuntimeClearLogs(runtime, target = "connector", actor) {
    const result = clearLogFile(target === "update" ? getUpdateLogPath() : target === "agent-updates" ? getAgentUpdateLogPath() : getConnectorLogPath());
    runtime.appendActivity({
        source: "web",
        status: "info",
        type: "logs_cleared",
        threadId: null,
        workspace: runtime.config.workspace,
        actor,
        detail: `Cleared ${target} log.`,
    });
    runtime.appendAudit({
        action: "command",
        status: "ok",
        contextKey: runtime.contextKey,
        actor,
        description: `clear ${target} log`,
        detail: result.filePath,
    });
    return { ok: true, filePath: result.filePath, clearedAt: result.clearedAt.toISOString() };
}
export function relayRuntimeRestartConnector(runtime, actor) {
    spawnConnectorRestart();
    runtime.broadcastStatus("Restart requested. The dashboard may disconnect briefly.", "warn");
    runtime.appendActivity({
        source: "web",
        status: "info",
        type: "restart_requested",
        threadId: null,
        workspace: runtime.config.workspace,
        actor,
        detail: "Dashboard requested a connector restart.",
    });
    runtime.appendAudit({
        action: "command",
        status: "ok",
        contextKey: runtime.contextKey,
        actor,
        description: "restart connector",
    });
    return { ok: true, message: "Restart requested." };
}
export function relayRuntimeDispose(runtime) {
    if (runtime.externalMonitor) {
        runtime.externalMonitor.close();
        runtime.externalMonitor = undefined;
    }
    if (runtime.metricsHistoryTimer) {
        clearInterval(runtime.metricsHistoryTimer);
        runtime.metricsHistoryTimer = null;
    }
    if (runtime.metricsHistoryPoller) {
        runtime.metricsHistoryPoller.close();
        runtime.metricsHistoryPoller = null;
    }
    runtime.workflowService.dispose();
    runtime.dashboardService.stopBackgroundRefresh();
    runtime.agentUpdates.cancelAll();
    runtime.registry.disposeAll();
    runtime.subscribers.clear();
}
