/**
 * AdapterPipeline — Dispatches CMD_DECODER_LOAD through an ordered interceptor chain.
 *
 * The pipeline owns an ordered array of CommandInterceptor instances.
 * On dispatchLoad(), it iterates through each interceptor via index-based
 * chaining (not recursive closures) to prevent stack overflow with many
 * interceptors. After all interceptors have run, the terminal closure
 * receives the final LoadContext.
 *
 * In Phase 4 the interceptor array is empty — dispatchLoad immediately
 * calls the terminal action, producing identical behavior to pre-pipeline code.
 */
class AdapterPipeline {
    private var interceptors: [CommandInterceptor] = []

    func addInterceptor(_ interceptor: CommandInterceptor) {
        interceptors.append(interceptor)
    }

    func dispatchLoad(
        event: AviationCommandEvent,
        context: LoadContext,
        terminal: @escaping (LoadContext) -> Void
    ) {
        var index = 0

        func next() {
            if index < interceptors.count {
                let interceptor = interceptors[index]
                index += 1
                interceptor.intercept(event: event, context: context, next: next)
            } else {
                terminal(context)
            }
        }

        next()
    }

    /// Dispatch a synthetic load for preload purposes.
    /// Runs DRM + Cache interceptors, then calls completion with prepared context.
    /// Skips PreloadAdapter to prevent re-entrant dispatch.
    func dispatchPreload(
        event: AviationCommandEvent,
        context: LoadContext,
        completion: @escaping (LoadContext) -> Void
    ) {
        var index = 0

        func next() {
            if index < interceptors.count {
                let interceptor = interceptors[index]
                index += 1
                if interceptor is PreloadAdapter {
                    next() // skip PreloadAdapter to prevent re-entry
                    return
                }
                interceptor.intercept(event: event, context: context, next: next)
            } else {
                completion(context)
            }
        }

        next()
    }
}
