// ==========================================
// Armature — SDK: Renderer Registry
// ==========================================
// Kernel-level renderer registration. Extensions register
// block renderers (fenced code blocks) and post renderers
// (DOM post-processing) once; all surfaces consume via
// sw.markdown or direct runBlockRenderers/runPostRenderers.
//
// Factory: createRenderers(emitFn)
// ==========================================

/**
 * Create the renderer registry.
 *
 * @param {Function} emitFn — events.emit for change notifications
 * @returns {object} renderers
 */
export function createRenderers(emitFn) {
    /** @type {Array<{name:string, type:string, pattern?:string, match?:Function, priority:number, render:Function}>} */
    const _block = [];
    /** @type {Array<{name:string, priority:number, render:Function}>} */
    const _post = [];

    function _sortByPriority(arr) {
        arr.sort((a, b) => a.priority - b.priority);
    }

    return {
        /**
         * Register a renderer.
         *
         * @param {string} name — unique name (e.g. 'mermaid', 'katex-block')
         * @param {object} opts
         * @param {string} opts.type — 'block' or 'post'
         * @param {string} [opts.pattern] — exact lang match (block only)
         * @param {Function} [opts.match] — match(lang) → boolean (block only)
         * @param {number} [opts.priority=100] — lower runs first
         * @param {Function} opts.render — render(lang, code, container) for block,
         *                                 render(container) for post
         * @returns {Function} unregister
         */
        register(name, { type, pattern, match, priority = 100, render }) {
            if (type === 'post') {
                // Remove existing with same name
                const idx = _post.findIndex(r => r.name === name);
                if (idx !== -1) _post.splice(idx, 1);

                _post.push({ name, priority, render });
                _sortByPriority(_post);

                emitFn('renderers.changed', { name, type, action: 'register' }, { localOnly: true });
                return () => this.unregister(name);
            }

            // Block renderer
            const idx = _block.findIndex(r => r.name === name);
            if (idx !== -1) _block.splice(idx, 1);

            _block.push({ name, type: 'block', pattern, match, priority, render });
            _sortByPriority(_block);

            emitFn('renderers.changed', { name, type: 'block', action: 'register' }, { localOnly: true });
            return () => this.unregister(name);
        },

        /**
         * Remove a renderer by name.
         * @param {string} name
         */
        unregister(name) {
            let found = false;
            const bi = _block.findIndex(r => r.name === name);
            if (bi !== -1) { _block.splice(bi, 1); found = true; }

            const pi = _post.findIndex(r => r.name === name);
            if (pi !== -1) { _post.splice(pi, 1); found = true; }

            if (found) {
                emitFn('renderers.changed', { name, action: 'unregister' }, { localOnly: true });
            }
        },

        /**
         * Run block renderers for a fenced code block.
         * First match wins. Returns true if handled.
         *
         * @param {string} lang — language tag from fenced block
         * @param {string} code — raw code content
         * @param {HTMLElement} container — DOM element to render into
         * @returns {boolean} true if a renderer handled it
         */
        runBlockRenderers(lang, code, container) {
            const l = (lang || '').toLowerCase();
            for (const r of _block) {
                const matched = r.pattern
                    ? r.pattern === l
                    : (r.match ? r.match(l) : false);
                if (matched) {
                    try {
                        r.render(lang, code, container);
                    } catch (e) {
                        console.error(`[sw.renderers] Block renderer "${r.name}" error:`, e);
                        return false;
                    }
                    return true;
                }
            }
            return false;
        },

        /**
         * Run all post renderers on a container.
         * @param {HTMLElement} container
         */
        runPostRenderers(container) {
            for (const r of _post) {
                try {
                    r.render(container);
                } catch (e) {
                    console.error(`[sw.renderers] Post renderer "${r.name}" error:`, e);
                }
            }
        },

        /**
         * List registered renderers (debug).
         * @returns {{ block: Array<{name:string, priority:number}>, post: Array<{name:string, priority:number}> }}
         */
        list() {
            return {
                block: _block.map(r => ({ name: r.name, priority: r.priority, pattern: r.pattern })),
                post: _post.map(r => ({ name: r.name, priority: r.priority })),
            };
        },
    };
}
