import { DEFAULT_PANEL_CONFIG, applyFilter, appendBounded, } from '@pellux/goodvibes-sdk/platform/runtime/diagnostics/types';
/**
 * ToolCallsPanel — diagnostic data provider for tool call telemetry.
 *
 * Listens to all tool domain events and builds a per-call timeline.
 * Completed calls are transferred to a bounded history buffer.
 */
export class ToolCallsPanel {
    _config;
    _eventBus;
    /** Active calls keyed by callId. */
    _active = new Map();
    /** Completed call history (oldest first). */
    _history = [];
    /** Registered change notification callbacks. */
    _subscribers = new Set();
    /** Unsubscribe function returned by the event bus. */
    _unsub = null;
    constructor(eventBus, config = DEFAULT_PANEL_CONFIG) {
        this._eventBus = eventBus;
        this._config = config;
        this._start();
    }
    /**
     * Subscribe to the tool domain and wire up event handlers.
     * @internal
     */
    _start() {
        const handler = (envelope) => {
            this._handleEnvelope(envelope);
        };
        this._unsub = this._eventBus.onDomain('tools', handler);
    }
    /**
     * Handle a tool domain envelope, routing to the appropriate update method.
     */
    _handleEnvelope(envelope) {
        const p = envelope.payload;
        // Type narrowing is done by checking the discriminant field on payload.
        if (!('type' in p))
            return;
        const type = p.type;
        const traceId = envelope.traceId;
        const sessionId = envelope.sessionId;
        switch (type) {
            case 'TOOL_RECEIVED': {
                const evt = p;
                this._active.set(evt.callId, {
                    callId: evt.callId,
                    turnId: evt.turnId,
                    tool: evt.tool,
                    args: evt.args,
                    phase: 'received',
                    receivedAt: envelope.ts,
                    traceId,
                    sessionId,
                });
                this._notify();
                break;
            }
            case 'TOOL_VALIDATED':
            case 'TOOL_PREHOOKED':
            case 'TOOL_EXECUTING':
            case 'TOOL_MAPPED':
            case 'TOOL_POSTHOOKED': {
                const evt = p;
                const phaseMap = {
                    TOOL_VALIDATED: 'validated',
                    TOOL_PREHOOKED: 'prehooked',
                    TOOL_EXECUTING: 'executing',
                    TOOL_MAPPED: 'mapped',
                    TOOL_POSTHOOKED: 'posthooked',
                };
                const record = this._active.get(evt.callId);
                if (record) {
                    record.phase = phaseMap[type];
                    this._notify();
                }
                break;
            }
            case 'TOOL_PERMISSIONED': {
                const evt = p;
                const record = this._active.get(evt.callId);
                if (record) {
                    record.phase = 'permissioned';
                    record.permissionApproved = evt.approved;
                    this._notify();
                }
                break;
            }
            case 'TOOL_SUCCEEDED': {
                const evt = p;
                const record = this._active.get(evt.callId);
                if (record) {
                    record.phase = 'succeeded';
                    record.completedAt = envelope.ts;
                    record.durationMs = evt.durationMs;
                    this._finalize(record);
                }
                break;
            }
            case 'TOOL_FAILED': {
                const evt = p;
                const record = this._active.get(evt.callId);
                if (record) {
                    record.phase = 'failed';
                    record.completedAt = envelope.ts;
                    record.durationMs = evt.durationMs;
                    record.error = evt.error;
                    this._finalize(record);
                }
                break;
            }
            case 'TOOL_CANCELLED': {
                const evt = p;
                const record = this._active.get(evt.callId);
                if (record) {
                    record.phase = 'cancelled';
                    record.completedAt = envelope.ts;
                    record.cancelReason = evt.reason;
                    this._finalize(record);
                }
                break;
            }
            default:
                break;
        }
    }
    /**
     * Move a completed record from active to history.
     */
    _finalize(record) {
        this._active.delete(record.callId);
        const entry = {
            callId: record.callId,
            turnId: record.turnId,
            tool: record.tool,
            args: record.args,
            phase: record.phase,
            receivedAt: record.receivedAt,
            completedAt: record.completedAt,
            durationMs: record.durationMs,
            error: record.error,
            cancelReason: record.cancelReason,
            permission: record.permissionApproved !== undefined
                ? { approved: record.permissionApproved }
                : undefined,
            traceId: record.traceId,
            sessionId: record.sessionId,
        };
        appendBounded(this._history, entry, this._config.bufferLimit);
        this._notify();
    }
    /**
     * Return a filtered snapshot of tool call entries.
     * Active calls are included as their current in-progress state.
     * History is ordered most-recent first.
     *
     * @param filter - Optional filter to restrict entries.
     */
    getSnapshot(filter) {
        // Convert active records to ToolCallEntry for inclusion
        const activeEntries = [];
        for (const record of this._active.values()) {
            activeEntries.push({
                callId: record.callId,
                turnId: record.turnId,
                tool: record.tool,
                args: record.args,
                phase: record.phase,
                receivedAt: record.receivedAt,
                completedAt: record.completedAt,
                durationMs: record.durationMs,
                error: record.error,
                cancelReason: record.cancelReason,
                permission: record.permissionApproved !== undefined
                    ? { approved: record.permissionApproved }
                    : undefined,
                traceId: record.traceId,
                sessionId: record.sessionId,
            });
        }
        // Combine: history (oldest-first) + active, then apply filter
        const combined = [...this._history, ...activeEntries];
        return applyFilter(combined, filter, (e) => e.receivedAt);
    }
    /**
     * Register a callback invoked whenever the data changes.
     * @returns An unsubscribe function.
     */
    subscribe(callback) {
        this._subscribers.add(callback);
        return () => this._subscribers.delete(callback);
    }
    /**
     * Release all event bus subscriptions and clear internal state.
     * After disposal the panel will no longer receive new events.
     */
    dispose() {
        if (this._unsub) {
            this._unsub();
            this._unsub = null;
        }
        this._subscribers.clear();
    }
    _notify() {
        for (const cb of this._subscribers) {
            try {
                cb();
            }
            catch {
                // Non-fatal: subscriber errors must not crash the provider
            }
        }
    }
}
