/**
 * Plugin registry — typed extension points for AILF evaluation capabilities.
 *
 * Three-tier architecture:
 * - **Mode bases** define evaluation methodology (rubrics, scoring, prompts)
 * - **Domain presets** target a mode base and add domain config (sources,
 *   features, doc fetcher)
 * - **Framework assertions** are generic evaluation primitives available to
 *   all modes
 *
 * @see docs/design-docs/architecture-overhaul/extensibility-plugins.md
 */
/**
 * In-memory plugin registry implementation.
 */
export class InMemoryPluginRegistry {
    modes = new Map();
    assertions_ = new Map();
    rubricTemplates_ = new Map();
    fixtureResolvers_ = new Map();
    reportSinks_ = new Map();
    modeBases_ = new Map();
    presets_ = new Map();
    promptTemplates_ = {};
    scoringProfiles_ = {};
    docFetcherFactory_;
    sourceDefs_ = [];
    featureDefs_;
    registerPreset(preset) {
        // Skip archived presets entirely
        if (preset.status === "archived")
            return;
        // Store draft/paused presets in the map (for later activation via
        // --preset flag) but skip all side-effect registrations. This prevents
        // a draft preset from silently overwriting the doc fetcher, merging
        // scoring profiles, etc.
        this.presets_.set(preset.name, preset);
        if (preset.status === "draft" || preset.status === "paused")
            return;
        // Resolve mode base defaults
        const base = this.modeBases_.get(preset.mode);
        if (!base) {
            throw new Error(`Preset "${preset.name}" targets mode "${preset.mode}" ` +
                `but no mode base is registered for it. ` +
                `Available mode bases: ${[...this.modeBases_.keys()].join(", ") || "(none)"}`);
        }
        // Mode is already registered by registerModeBase() — no need to re-register.
        // Merge rubric templates: base defaults + preset overrides (by ID)
        const baseRubrics = new Map((base.rubricTemplates ?? []).map((r) => [r.id, r]));
        for (const r of preset.rubricTemplates ?? []) {
            baseRubrics.set(r.id, r);
        }
        for (const r of baseRubrics.values()) {
            this.registerRubricTemplate(r);
        }
        // Merge scoring profiles: base defaults + preset overrides (by name)
        const profiles = {
            ...base.scoringProfiles,
            ...preset.scoringProfiles,
        };
        if (Object.keys(profiles).length > 0) {
            this.registerScoringProfiles(profiles);
        }
        // Merge prompt templates: base defaults + preset overrides (by name)
        const prompts = {
            ...base.promptTemplates,
            ...preset.promptTemplates,
        };
        if (Object.keys(prompts).length > 0) {
            this.registerPromptTemplates(prompts);
        }
        // Merge assertions: base + preset (preset overrides by type)
        for (const a of base.assertions ?? [])
            this.registerAssertion(a);
        if (preset.assertions) {
            for (const a of preset.assertions)
                this.registerAssertion(a);
        }
        // Register domain-specific fields
        if (preset.fixtureResolvers) {
            for (const r of preset.fixtureResolvers)
                this.registerFixtureResolver(r);
        }
        if (preset.reportSinks) {
            for (const s of preset.reportSinks)
                this.registerReportSink(s);
        }
        if (preset.docFetcher) {
            this.registerDocFetcherFactory(preset.docFetcher);
        }
        if (preset.sourceDefs) {
            this.registerSourceDefs(preset.sourceDefs);
        }
        if (preset.featureDefs) {
            this.registerFeatureDefs(preset.featureDefs);
        }
    }
    registerMode(mode) {
        this.modes.set(mode.id, mode);
    }
    registerAssertion(assertion) {
        this.assertions_.set(assertion.type, assertion);
    }
    registerRubricTemplate(template) {
        this.rubricTemplates_.set(template.id, template);
    }
    registerFixtureResolver(resolver) {
        this.fixtureResolvers_.set(resolver.scheme, resolver);
    }
    registerReportSink(sink) {
        this.reportSinks_.set(sink.id, sink);
    }
    getModes() {
        return [...this.modes.values()];
    }
    getMode(id) {
        return this.modes.get(id);
    }
    getAssertions() {
        return [...this.assertions_.values()];
    }
    getRubricTemplates() {
        return [...this.rubricTemplates_.values()];
    }
    getFixtureResolvers() {
        return [...this.fixtureResolvers_.values()];
    }
    getReportSinks() {
        return [...this.reportSinks_.values()];
    }
    getPresets() {
        return [...this.presets_.values()];
    }
    registerPromptTemplates(templates) {
        Object.assign(this.promptTemplates_, templates);
    }
    getPromptTemplates() {
        return this.promptTemplates_;
    }
    registerScoringProfiles(profiles) {
        Object.assign(this.scoringProfiles_, profiles);
    }
    getScoringProfiles() {
        return this.scoringProfiles_;
    }
    registerDocFetcherFactory(factory) {
        this.docFetcherFactory_ = factory;
    }
    getDocFetcherFactory() {
        return this.docFetcherFactory_;
    }
    registerSourceDefs(sources) {
        this.sourceDefs_ = [...this.sourceDefs_, ...sources];
    }
    getSourceDefs() {
        return this.sourceDefs_;
    }
    registerFeatureDefs(features) {
        if (!this.featureDefs_) {
            this.featureDefs_ = features;
            return;
        }
        // Merge by feature ID: new features override existing on ID collision,
        // existing features not in new set are preserved.
        const merged = new Map(this.featureDefs_.features.map((f) => [f.id, f]));
        for (const f of features.features) {
            merged.set(f.id, f);
        }
        this.featureDefs_ = { features: [...merged.values()] };
    }
    getFeatureDefs() {
        return this.featureDefs_;
    }
    registerModeBase(base) {
        this.modeBases_.set(base.mode.id, base);
        // Also register the mode itself so getMode() works
        this.registerMode(base.mode);
    }
    getModeBase(modeId) {
        return this.modeBases_.get(modeId);
    }
    getModeBases() {
        return [...this.modeBases_.values()];
    }
}
