import { buildProviderAccountSnapshot } from '../provider-accounts/registry.js';
import { getSettingsControlPlaneSnapshot } from '../settings/control-plane.js';
import { checkRecoveryFile, readLastSessionPointer } from '../session-persistence.js';
import { listPersistedWorktreeMeta, summarizeWorktreeOwnership } from '../worktree/registry.js';
import { inspectInboundTls, inspectOutboundTls } from '../network/index.js';
import { getSecuritySettingsReport } from '../security-settings.js';
const EVENT_DOMAINS = [
    'session',
    'turn',
    'tools',
    'permissions',
    'tasks',
    'agents',
    'workflows',
    'orchestration',
    'communication',
    'planner',
    'plugins',
    'mcp',
    'transport',
    'compaction',
    'ui',
    'ops',
];
function serializeEnvelope(envelope) {
    return {
        type: envelope.type,
        ts: envelope.ts,
        traceId: envelope.traceId,
        sessionId: envelope.sessionId,
        source: envelope.source,
        payload: envelope.payload,
    };
}
export class IntegrationHelperService {
    context;
    constructor(context) {
        this.context = context;
    }
    getRuntimeStore() {
        return this.context.runtimeStore;
    }
    getContext() {
        return this.context;
    }
    buildReview() {
        const state = this.context.runtimeStore.getState();
        const panelSnapshots = this.listPanels();
        return {
            apiFamilies: [
                'session lifecycle',
                'approvals',
                'tasks and agents',
                'remote state',
                'health and diagnostics',
                'panel targeting',
                'event subscriptions',
            ],
            routes: [
                'GET /api/review',
                'GET /api/session',
                'GET /api/tasks',
                'GET /api/automation',
                'GET /api/sessions',
                'GET /api/deliveries',
                'GET /api/control-plane',
                'GET /api/control-plane/auth',
                'GET /api/control-plane/clients',
                'GET /api/control-plane/events?domains=session,tasks',
                'GET /api/control-plane/web',
                'GET /api/v1/telemetry',
                'GET /api/v1/telemetry/events',
                'GET /api/v1/telemetry/errors',
                'GET /api/v1/telemetry/traces',
                'GET /api/v1/telemetry/metrics',
                'GET /api/v1/telemetry/stream',
                'GET /api/v1/telemetry/otlp/v1/traces',
                'GET /api/v1/telemetry/otlp/v1/logs',
                'GET /api/v1/telemetry/otlp/v1/metrics',
                'GET /api/routes',
                'GET /api/routes/bindings',
                'POST /api/routes/bindings',
                'GET /api/surfaces',
                'GET /api/channels/accounts',
                'GET /api/channels/accounts/:surface',
                'GET /api/channels/accounts/:surface/:accountId',
                'POST /api/channels/accounts/:surface/actions/:action',
                'POST /api/channels/accounts/:surface/:accountId/actions/:action',
                'GET /api/channels/capabilities',
                'GET /api/channels/capabilities/:surface',
                'GET /api/channels/tools',
                'GET /api/channels/tools/:surface',
                'GET /api/channels/agent-tools',
                'GET /api/channels/agent-tools/:surface',
                'POST /api/channels/tools/:surface/:toolId',
                'GET /api/channels/actions',
                'GET /api/channels/actions/:surface',
                'POST /api/channels/actions/:surface/:actionId',
                'POST /api/channels/targets/:surface/resolve',
                'POST /api/channels/authorize/:surface',
                'GET /api/watchers',
                'GET /api/service/status',
                'POST /api/service/install',
                'POST /api/service/uninstall',
                'GET /api/approvals',
                'GET /api/remote',
                'GET /api/health',
                'GET /api/accounts',
                'GET /api/providers',
                'GET /api/providers/:providerId',
                'GET /api/providers/:providerId/usage',
                'GET /api/settings',
                'GET /api/local-auth',
                'GET /api/continuity',
                'GET /api/worktrees',
                'GET /api/intelligence',
                'GET /api/panels',
                'POST /api/panels/open',
                'GET /api/events?domains=session,tasks',
            ],
            sessions: state.session.id ? 1 : 0,
            tasks: state.tasks.tasks.size,
            pendingApprovals: state.permissions.awaitingDecision ? 1 : 0,
            remoteContracts: this.context.remoteRunnerRegistry.listContracts().length,
            panels: panelSnapshots.length,
        };
    }
    getSessionSnapshot() {
        const state = this.context.runtimeStore.getState();
        return {
            id: state.session.id,
            title: this.context.getConversationTitle?.() ?? '',
            status: state.session.status,
            recoveryState: state.session.recoveryState,
            projectRoot: state.session.projectRoot,
            isResumed: state.session.isResumed,
            resumedFromId: state.session.resumedFromId,
            compactionState: state.session.compactionState,
            lastCompactedAt: state.session.lastCompactedAt,
            lineage: state.session.lineage,
        };
    }
    getTaskSnapshot() {
        const state = this.context.runtimeStore.getState();
        return {
            queued: state.tasks.queuedIds.length,
            running: state.tasks.runningIds.length,
            blocked: state.tasks.blockedIds.length,
            totals: {
                created: state.tasks.totalCreated,
                completed: state.tasks.totalCompleted,
                failed: state.tasks.totalFailed,
                cancelled: state.tasks.totalCancelled,
            },
            tasks: [...state.tasks.tasks.values()].map((task) => ({
                id: task.id,
                kind: task.kind,
                title: task.title,
                status: task.status,
                owner: task.owner,
                parentTaskId: task.parentTaskId,
                queuedAt: task.queuedAt,
                startedAt: task.startedAt,
                endedAt: task.endedAt,
                error: task.error,
            })),
        };
    }
    getAutomationSnapshot() {
        const jobs = this.context.automationManager.listJobs();
        const runs = this.context.automationManager.listRuns().slice(0, 50);
        return {
            totals: {
                jobs: jobs.length,
                enabled: jobs.filter((job) => job.enabled).length,
                paused: jobs.filter((job) => !job.enabled).length,
                runs: runs.length,
            },
            jobs: jobs.map((job) => ({
                id: job.id,
                name: job.name,
                enabled: job.enabled,
                status: job.status,
                schedule: job.schedule,
                nextRunAt: job.nextRunAt,
                lastRunAt: job.lastRunAt,
                runCount: job.runCount,
                failureCount: job.failureCount,
            })),
            recentRuns: runs.map((run) => ({
                id: run.id,
                jobId: run.jobId,
                status: run.status,
                trigger: run.triggeredBy.kind,
                queuedAt: run.queuedAt,
                startedAt: run.startedAt,
                endedAt: run.endedAt,
                agentId: run.agentId,
                error: run.error,
            })),
        };
    }
    getRouteSnapshot() {
        const state = this.context.runtimeStore.getState();
        return {
            totalBindings: state.routes.bindingIds.length,
            activeBindings: state.routes.activeBindingIds.length,
            recentBindings: state.routes.recentBindingIds.length,
            bindings: [...state.routes.bindings.values()].map((binding) => ({
                id: binding.id,
                kind: binding.kind,
                surfaceKind: binding.surfaceKind,
                surfaceId: binding.surfaceId,
                externalId: binding.externalId,
                threadId: binding.threadId,
                channelId: binding.channelId,
                sessionId: binding.sessionId,
                jobId: binding.jobId,
                runId: binding.runId,
                lastSeenAt: binding.lastSeenAt,
            })),
        };
    }
    getApprovalSnapshot() {
        const state = this.context.runtimeStore.getState();
        const approvals = this.context.approvalBroker.listApprovals(50);
        return {
            awaitingDecision: state.permissions.awaitingDecision,
            mode: state.permissions.mode,
            lastDecision: state.permissions.lastDecision,
            approvalCount: state.permissions.approvalCount,
            denialCount: state.permissions.denialCount,
            cachedChecks: state.permissions.cachedChecks,
            totalChecks: state.permissions.totalChecks,
            approvals,
        };
    }
    getSessionBrokerSnapshot() {
        const sessions = this.context.sessionBroker.listSessions(50);
        return {
            totals: {
                sessions: sessions.length,
                active: sessions.filter((session) => session.status === 'active').length,
                closed: sessions.filter((session) => session.status === 'closed').length,
            },
            sessions,
        };
    }
    getDeliverySnapshot() {
        const state = this.context.runtimeStore.getState();
        const attempts = [...state.deliveries.deliveryAttempts.values()]
            .sort((a, b) => (b.startedAt ?? b.endedAt ?? 0) - (a.startedAt ?? a.endedAt ?? 0))
            .slice(0, 100);
        return {
            totals: {
                queued: state.deliveries.totalQueued,
                started: state.deliveries.totalStarted,
                succeeded: state.deliveries.totalSucceeded,
                failed: state.deliveries.totalFailed,
                deadLettered: state.deliveries.totalDeadLettered,
            },
            attempts,
        };
    }
    getRemoteSnapshot() {
        const state = this.context.runtimeStore.getState();
        const remoteRegistry = this.context.remoteRunnerRegistry;
        const supervisor = this.context.remoteSupervisor.getSnapshot(this.context.runtimeStore);
        const contracts = remoteRegistry.listContracts();
        const pools = remoteRegistry.listPools();
        const artifacts = remoteRegistry.listArtifacts();
        const acpLastError = [...state.acp.connections.values()].find((connection) => connection.lastError)?.lastError;
        return {
            daemon: {
                transportState: state.daemon.transportState,
                isRunning: state.daemon.isRunning,
                reconnectAttempts: state.daemon.reconnectAttempts,
                runningJobCount: state.daemon.runningJobCount,
                ...(state.daemon.lastError ? { lastError: state.daemon.lastError } : {}),
            },
            acp: {
                transportState: state.acp.managerTransportState,
                activeConnectionIds: state.acp.activeConnectionIds,
                totalSpawned: state.acp.totalSpawned,
                totalFailed: state.acp.totalFailed,
                ...(acpLastError ? { lastError: acpLastError } : {}),
            },
            registry: {
                pools: pools.length,
                contracts: contracts.length,
                artifacts: artifacts.length,
                poolEntries: pools.map((pool) => ({
                    id: pool.id,
                    label: pool.label,
                    trustClass: pool.trustClass,
                    preferredTemplate: pool.preferredTemplate,
                    maxRunners: pool.maxRunners,
                    runnerIds: pool.runnerIds,
                })),
                contractEntries: contracts.map((contract) => ({
                    id: contract.id,
                    runnerId: contract.runnerId,
                    label: contract.label,
                    template: contract.template,
                    ...(contract.poolId ? { poolId: contract.poolId } : {}),
                    ...(contract.taskId ? { taskId: contract.taskId } : {}),
                    sourceTransport: contract.sourceTransport,
                    trustClass: contract.trustClass,
                    executionProtocol: contract.capabilityCeiling.executionProtocol,
                    reviewMode: contract.capabilityCeiling.reviewMode,
                    communicationLane: contract.capabilityCeiling.communicationLane,
                    transportState: contract.transport.state,
                    ...(contract.transport.lastError ? { lastError: contract.transport.lastError } : {}),
                })),
                artifactEntries: artifacts.map((artifact) => ({
                    id: artifact.id,
                    runnerId: artifact.runnerId,
                    createdAt: artifact.createdAt,
                    status: artifact.task.status,
                    summary: artifact.task.summary,
                    ...(artifact.task.error ? { error: artifact.task.error } : {}),
                })),
            },
            supervisor: {
                sessions: supervisor.sessions.length,
                degraded: supervisor.degradedConnections,
                capturedAt: supervisor.capturedAt,
                entries: supervisor.sessions.map((entry) => ({
                    runnerId: entry.runnerId,
                    label: entry.label,
                    transportState: entry.transportState,
                    heartbeat: entry.heartbeat.status,
                    ...(entry.taskId ? { taskId: entry.taskId } : {}),
                })),
            },
            distributed: {
                pairRequests: this.context.distributedRuntime.listPairRequests(100),
                peers: this.context.distributedRuntime.listPeers(undefined, 500),
                work: this.context.distributedRuntime.listWork(500),
                audit: this.context.distributedRuntime.listAudit(100),
            },
        };
    }
    getHealthSnapshot() {
        const state = this.context.runtimeStore.getState();
        const degradedDomains = [];
        const providerProblems = [...state.providerHealth.providers.values()]
            .filter((provider) => provider.status !== 'healthy' && provider.status !== 'unknown')
            .map((provider) => provider.providerId);
        const degradedMcpServers = [...state.mcp.servers.values()]
            .filter((server) => server.status === 'degraded' || server.status === 'auth_required' || server.schemaFreshness === 'quarantined')
            .map((server) => server.name);
        const quarantinedServers = [...state.mcp.servers.values()]
            .filter((server) => server.schemaFreshness === 'quarantined')
            .map((server) => server.name);
        if (providerProblems.length > 0)
            degradedDomains.push('providerHealth');
        if (degradedMcpServers.length > 0)
            degradedDomains.push('mcp');
        if (state.integrations.problemIds.length > 0)
            degradedDomains.push('integrations');
        if (state.daemon.transportState === 'degraded' || state.daemon.transportState === 'terminal_failure')
            degradedDomains.push('daemon');
        if (state.acp.managerTransportState === 'degraded' || state.acp.managerTransportState === 'terminal_failure')
            degradedDomains.push('acp');
        if (state.session.recoveryState === 'failed')
            degradedDomains.push('session');
        return {
            overall: degradedDomains.length > 0 ? 'degraded' : 'healthy',
            degradedDomains,
            providerProblems,
            mcpProblems: {
                degraded: degradedMcpServers,
                quarantined: quarantinedServers,
            },
            integrationProblems: state.integrations.problemIds,
            ...(this.context.configManager ? {
                network: {
                    controlPlane: inspectInboundTls(this.context.configManager, 'controlPlane'),
                    httpListener: inspectInboundTls(this.context.configManager, 'httpListener'),
                    outbound: inspectOutboundTls(this.context.configManager),
                },
            } : {}),
        };
    }
    async getAccountsSnapshot() {
        return await buildProviderAccountSnapshot({
            providerRegistry: this.context.providerRegistry,
            serviceRegistry: this.context.serviceRegistry,
            subscriptionManager: this.context.subscriptionManager,
            secretsManager: this.context.secretsManager,
        });
    }
    getSettingsSnapshot() {
        if (!this.context.configManager) {
            return { available: false, reason: 'configManager unavailable' };
        }
        const snapshot = getSettingsControlPlaneSnapshot(this.context.configManager);
        return {
            available: true,
            liveKeyCount: snapshot.liveKeyCount,
            profileCount: snapshot.profileCount,
            managedLockCount: snapshot.managedLockCount,
            resolvedCounts: snapshot.resolvedCounts,
            conflicts: snapshot.conflicts,
            recentFailures: snapshot.recentFailures,
            stagedManagedBundle: snapshot.stagedManagedBundle,
            rollbackHistory: snapshot.rollbackHistory,
        };
    }
    getSecuritySettingsReport() {
        return getSecuritySettingsReport(this.context.featureFlags);
    }
    getLocalAuthSnapshot() {
        const snapshot = this.context.localUserAuthManager.inspect();
        return {
            userStorePath: snapshot.userStorePath,
            bootstrapCredentialPath: snapshot.bootstrapCredentialPath,
            bootstrapCredentialPresent: snapshot.bootstrapCredentialPresent,
            userCount: snapshot.userCount,
            sessionCount: snapshot.sessionCount,
            users: snapshot.users,
            sessions: snapshot.sessions,
        };
    }
    getContinuitySnapshot() {
        const state = this.context.runtimeStore.getState();
        const recovery = checkRecoveryFile({
            workingDirectory: this.context.workingDirectory,
            homeDirectory: this.context.homeDirectory,
        });
        return {
            sessionId: state.session.id,
            status: state.session.status,
            recoveryState: state.session.recoveryState,
            lastSessionPointer: readLastSessionPointer({
                workingDirectory: this.context.workingDirectory,
                homeDirectory: this.context.homeDirectory,
            }),
            recoveryFilePresent: Boolean(recovery),
            recoveryFile: recovery ?? null,
        };
    }
    getWorktreeSnapshot() {
        const records = listPersistedWorktreeMeta({ workingDirectory: this.context.workingDirectory });
        return {
            summary: summarizeWorktreeOwnership(records),
            records,
        };
    }
    getIntelligenceSnapshot() {
        const intelligence = this.context.runtimeStore.getState().intelligence;
        return {
            diagnosticsStatus: intelligence.diagnosticsStatus,
            symbolSearchStatus: intelligence.symbolSearchStatus,
            completionsStatus: intelligence.completionsStatus,
            hoverStatus: intelligence.hoverStatus,
            errorCount: intelligence.errorCount,
            warningCount: intelligence.warningCount,
            totalRequests: intelligence.totalRequests,
            avgLatencyMs: intelligence.avgLatencyMs,
        };
    }
    listPanels() {
        const manager = this.context.panelManager;
        const openIds = new Set([
            ...manager.getTopPane().panels.map((panel) => panel.id),
            ...manager.getBottomPane().panels.map((panel) => panel.id),
        ]);
        return manager
            .getRegisteredTypes()
            .map((registration) => ({
            id: registration.id,
            name: registration.name,
            category: registration.category,
            description: registration.description,
            open: openIds.has(registration.id),
        }))
            .sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
    }
    openPanel(id, pane) {
        const manager = this.context.panelManager;
        const known = manager.getRegisteredTypes().some((registration) => registration.id === id);
        if (!known)
            return false;
        manager.open(id, pane);
        manager.show();
        return true;
    }
    createEventStream(request, domains) {
        const selectedDomains = domains.length > 0 ? domains : EVENT_DOMAINS;
        const encoder = new TextEncoder();
        let teardown = () => { };
        // raise ReadableStream HWM so startup `ready` + live events aren't
        // dropped by the backpressure guard before a consumer has pulled the first chunk.
        const stream = new ReadableStream({
            start: (controller) => {
                const unsubs = selectedDomains.map((domain) => this.context.runtimeBus.onDomain(domain, (envelope) => {
                    // Drop event if the stream buffer is full (backpressure guard).
                    if ((controller.desiredSize ?? 1) <= 0)
                        return;
                    controller.enqueue(encoder.encode(`event: ${domain}\ndata: ${JSON.stringify(serializeEnvelope(envelope))}\n\n`));
                }));
                const heartbeat = setInterval(() => {
                    controller.enqueue(encoder.encode(': heartbeat\n\n'));
                }, 15_000);
                // Don't block clean process exit.
                heartbeat.unref?.();
                teardown = () => {
                    clearInterval(heartbeat);
                    for (const unsub of unsubs)
                        unsub();
                };
                request.signal.addEventListener('abort', () => {
                    teardown();
                    controller.close();
                }, { once: true });
                controller.enqueue(encoder.encode(`event: ready\ndata: ${JSON.stringify({ domains: selectedDomains })}\n\n`));
            },
            cancel: () => {
                teardown();
            },
        }, new CountQueuingStrategy({ highWaterMark: 256 }));
        return new Response(stream, {
            headers: {
                'content-type': 'text/event-stream',
                'cache-control': 'no-cache',
                connection: 'keep-alive',
            },
        });
    }
}
