import { fallbackEntity, fallbackSection } from './fallback.js';
import { autoDerivedSchema } from './auto-schema.js';
import { legacyRegisterServerSerializer } from '../core/plugin-host/legacy-adapter.js';
const MAX_DEPTH = 1;
class SerializerRegistry {
    serializers = new Map();
    register(serializer) {
        this.serializers.set(serializer.type, serializer);
    }
    has(type) {
        return this.serializers.has(type);
    }
    get(type) {
        return this.serializers.get(type);
    }
    listTypes() {
        return Array.from(this.serializers.keys()).sort();
    }
    getVersion(type) {
        return this.serializers.get(type)?.version ?? null;
    }
    serializeEntity(type, view, entity, reader, depth = 0) {
        return this.invoke(type, view, entity, reader, depth, () => fallbackEntity(entity, view));
    }
    serializeSection(view, section, reader) {
        return this.invoke('section', view, section, reader, 0, () => fallbackSection(section, view));
    }
    invoke(type, view, input, reader, depth, buildFallback) {
        const serializer = this.serializers.get(type);
        if (!serializer) {
            return { data: buildFallback(), fallback: true, error: 'no_serializer' };
        }
        const method = pickMethod(serializer, view);
        if (!method) {
            return { data: buildFallback(), fallback: true };
        }
        const ctx = { reader, depth, maxDepth: MAX_DEPTH };
        try {
            const data = method(input, ctx);
            const brokenRefs = extractBrokenRefs(data);
            return { data, fallback: false, ...(brokenRefs ? { brokenRefs } : {}) };
        }
        catch (err) {
            return {
                data: buildFallback(),
                fallback: true,
                error: `serializer_threw: ${err.message}`,
            };
        }
    }
    getSchema(type, view, db) {
        const serializer = this.serializers.get(type);
        if (serializer?.schema)
            return serializer.schema(view);
        if (db)
            return autoDerivedSchema(db, type);
        return { type: 'object', _auto: true, _note: 'schema unavailable without db handle' };
    }
    catalog(reader, db) {
        const types = {};
        for (const serializer of this.serializers.values()) {
            const views = [];
            if (serializer.inlineMention)
                views.push('inline_mention');
            if (serializer.singleElement)
                views.push('single_element');
            if (serializer.elementListItem)
                views.push('element_list_item');
            if (serializer.taggedListItem)
                views.push('tagged_list_item');
            if (serializer.detail)
                views.push('detail');
            const schemas = {};
            const viewList = [
                'inline_mention',
                'single_element',
                'element_list_item',
                'tagged_list_item',
                'detail',
            ];
            for (const view of viewList) {
                schemas[view] = this.getSchema(serializer.type, view, db);
            }
            types[serializer.type] = {
                version: serializer.version,
                views,
                schemas,
            };
        }
        void reader;
        return { types };
    }
}
export const serializerRegistry = new SerializerRegistry();
export function registerSerializer(serializer) {
    serializerRegistry.register(serializer);
    legacyRegisterServerSerializer(serializer);
}
function pickMethod(serializer, view) {
    switch (view) {
        case 'inline_mention':
            return serializer.inlineMention;
        case 'single_element':
            return serializer.singleElement;
        case 'element_list_item':
            return serializer.elementListItem;
        case 'tagged_list_item':
            return serializer.taggedListItem;
        case 'detail':
            return serializer.detail;
    }
}
function extractBrokenRefs(data) {
    if (!data || typeof data !== 'object')
        return undefined;
    const refs = data._brokenRefs;
    if (Array.isArray(refs) && refs.every((r) => typeof r === 'string'))
        return refs;
    return undefined;
}
//# sourceMappingURL=registry.js.map