/**
 * Health Error Propagation — Effect Handlers
 *
 * Implements concrete actions for each CascadeEffect type produced by the
 * CascadeEngine. Each handler is a pure dispatch function — side effects
 * are performed through the provided aggregator and eventBus context.
 *
 * Callers (e.g. HealthStoreWiring) are responsible for iterating over
 * CascadeResult arrays and invoking this function for each result.
 *
 * @module health/effect-handlers
 */
import { createCascadeAppliedEvent } from './types.js';
import { createEventEnvelope } from '../events/index.js';
/**
 * Emits a synthetic health event on the bus.
 *
 * Health events (e.g. CASCADE_APPLIED) are not part of AnyRuntimeEvent — they live
 * outside the typed domain-event union. This helper centralises the necessary
 * cross-domain cast in a single, documented location rather than duplicating it
 * at each call site.
 */
function emitHealthEvent(bus, envelope) {
    // CASCADE_APPLIED and synthetic health events live outside the AnyRuntimeEvent typed union
    // by design. Keep the cross-domain cast isolated here so standard domain events can continue
    // using typed emitters.
    bus.emit(
    // Health events use 'session' as the routing domain — it is the most general
    // domain and health cascades originate from session-level failures.
    'session', envelope);
}
/**
 * Dispatches the appropriate runtime action for a given CascadeEffect.
 *
 * Handles all six CascadeEffect variants:
 * - `CANCEL_INFLIGHT` — cancels in-flight operations in the given scope
 * - `BLOCK_DISPATCH` — blocks new dispatch in the given scope (optionally queues)
 * - `MARK_CHILDREN` — marks child tasks/agents as failed or blocked
 * - `DEREGISTER_TOOLS` — deregisters tools, scoped by pluginId from sourceContext
 * - `EMIT_EVENT` — emits a domain event of the given eventType on the bus
 * - `BLOCK_NEW` — blocks new operations of the given scope
 *
 * After applying the effect, emits a `CASCADE_APPLIED` event on the event bus
 * so that other subsystems can observe cascade propagation.
 *
 * @param effect - The CascadeEffect to apply.
 * @param result - The CascadeResult that produced this effect (carries ruleId, source, etc.).
 * @param context - The aggregator and eventBus needed to perform side effects.
 */
export function handleCascadeEffect(effect, result, context) {
    const { aggregator, eventBus } = context;
    switch (effect.type) {
        case 'CANCEL_INFLIGHT': {
            // Mark the target domain as degraded to signal in-flight cancellation.
            // Actual cancellation of network/async operations is domain-specific;
            // subscribers observing CASCADE_APPLIED will perform the real cancellation.
            if (result.target !== 'ALL') {
                aggregator.updateDomainHealth(result.target, 'degraded', {
                    failureReason: `In-flight operations cancelled (scope: ${effect.scope})`,
                });
            }
            break;
        }
        case 'BLOCK_DISPATCH': {
            // Mark the target domain as degraded with the blocked scope noted.
            // If queueable, subscribers may hold and retry; otherwise, they must reject.
            if (result.target !== 'ALL') {
                aggregator.updateDomainHealth(result.target, 'degraded', {
                    failureReason: effect.queueable
                        ? `Dispatch blocked (scope: ${effect.scope}) — queuing enabled`
                        : `Dispatch blocked (scope: ${effect.scope}) — queuing disabled`,
                    degradedCapabilities: [effect.scope],
                });
            }
            break;
        }
        case 'MARK_CHILDREN': {
            // Mark child domains as failed or blocked.
            // For 'ALL' target, propagate to tasks and agents as the primary child domains.
            const childDomains = result.target === 'ALL'
                ? ['tasks', 'agents']
                : [result.target];
            for (const childDomain of childDomains) {
                aggregator.updateDomainHealth(childDomain, 'failed', {
                    failureReason: `Marked ${effect.status} by cascade from '${result.source}'${effect.notifyParent ? ' (parent notified)' : ''}`,
                });
            }
            break;
        }
        case 'DEREGISTER_TOOLS': {
            // Signal tool deregistration by marking the toolExecution domain degraded.
            // Subscribers (tool registry) observe CASCADE_APPLIED to perform actual deregistration.
            // If pluginId is in sourceContext, the scope is limited to that plugin's tools.
            const pluginId = result.sourceContext?.['pluginId'];
            const scope = pluginId ? `plugin:${pluginId}` : 'all-plugins';
            if (result.target !== 'ALL') {
                aggregator.updateDomainHealth(result.target, 'degraded', {
                    failureReason: `Tools deregistered (${scope})`,
                    degradedCapabilities: [`tool-dispatch:${scope}`],
                });
            }
            break;
        }
        case 'EMIT_EVENT': {
            // Emit the specified event type on the bus as a synthetic health event.
            // Since CASCADE_APPLIED is not in AnyRuntimeEvent, we use a cast to
            // emit on the 'session' domain (the most general available routing domain).
            const syntheticEnvelope = createEventEnvelope(effect.eventType, {
                type: effect.eventType,
                ruleId: result.ruleId,
                source: result.source,
                target: result.target,
                timestamp: result.timestamp,
                sourceContext: result.sourceContext,
            }, {
                sessionId: result.sourceContext?.['sessionId'] ?? 'runtime',
                source: 'health/effect-handlers',
            });
            emitHealthEvent(eventBus, syntheticEnvelope);
            break;
        }
        case 'BLOCK_NEW': {
            // Block new operations in the given scope by marking target domain degraded.
            if (result.target !== 'ALL') {
                aggregator.updateDomainHealth(result.target, 'degraded', {
                    failureReason: `New operations blocked (scope: ${effect.scope})`,
                    degradedCapabilities: [effect.scope],
                });
            }
            break;
        }
        default: {
            const _exhaustive = effect;
            throw new Error(`Unhandled cascade effect type: ${effect.type}`);
        }
    }
    // Emit CASCADE_APPLIED event for observability
    const appliedEvent = createCascadeAppliedEvent(result);
    const appliedEnvelope = createEventEnvelope('CASCADE_APPLIED', appliedEvent, {
        sessionId: result.sourceContext?.['sessionId'] ?? 'runtime',
        source: 'health/effect-handlers',
    });
    emitHealthEvent(eventBus, appliedEnvelope);
}
