// ─────────────────────────────────────────────────────────────────────────────
// Single capability-tagged MediaProvider registry (ADR-007).
//
// CONTEXT invariant #2: OpenRouter is the only external gateway and EVERY media
// provider — image, video, tts — registers here. There is exactly ONE registry,
// ONE contract, ONE envKey convention. Adding a provider has a single entry
// point; "which registry do I touch?" has a permanent answer.
//
// Providers are tagged by a single `capability` discriminator ("image" |
// "video" | "tts"), not boolean flags — every provider serves exactly one
// capability. `envKey` is declarative (pneuma envMapping style): availability =
// process.env[provider.envKey] is set, except edge-tts (zero-cost local binary,
// always available). Exactly one provider per capability carries `default:true`.
//
// History: image lived here already; video had a parallel array registry under
// src/server/providers/ (runway/sora/kling stubs + seedance), and TTS had TWO
// modules (the src/tts-providers fallback registry + a standalone
// src/providers/tts/index.ts synth). ADR-007 consolidated all four: video is now
// honestly seedance-only (the stubs are gone — they produced nothing and implied
// direct vendor calls), and the two TTS paths merged into src/providers/tts/.
// ─────────────────────────────────────────────────────────────────────────────
import { OpenRouterImageProvider } from "./openrouter-image.js";
import { createSeedanceProvider } from "./video/seedance.js";
import { edgeTtsProvider } from "./tts/edge-tts.js";
import { geminiTtsProvider } from "./tts/gemini-tts.js";
import { createLyriaProvider } from "./audio/lyria.js";
// Map keyed by `${capability}:${name}` so the same name could (in theory) exist
// under two capabilities without collision. Insertion order is preserved, so
// getDefaultProvider's "first registered" tiebreak is deterministic.
const providers = new Map();
// Historical inbound aliases. Renamed ids stay resolvable forever because old
// chat history, recipe docs and external scripts carry the old literal (e.g.
// `--provider nanobanana`); normalizing at this single lookup chokepoint means
// no HTTP route / CLI / config reader needs to know the history.
const PROVIDER_ID_ALIASES = {
    nanobanana: "openrouter-image",
};
function canonicalName(name) {
    return PROVIDER_ID_ALIASES[name] ?? name;
}
function key(capability, name) {
    return `${capability}:${canonicalName(name)}`;
}
export function registerProvider(p) {
    providers.set(key(p.capability, p.name), p);
}
export function getProvider(capability, name) {
    return providers.get(key(capability, name));
}
export function getDefaultProvider(capability) {
    let first;
    for (const p of providers.values()) {
        if (p.capability !== capability)
            continue;
        if (p.default)
            return p;
        if (!first)
            first = p;
    }
    return first;
}
/** Lists providers, optionally filtered to one capability. */
export function listProviders(capability) {
    const out = [];
    for (const p of providers.values()) {
        if (capability && p.capability !== capability)
            continue;
        const available = p.name === edgeTtsProvider.id || Boolean(process.env[p.envKey]);
        out.push({
            name: p.name,
            capability: p.capability,
            envKey: p.envKey,
            default: Boolean(p.default),
            available,
        });
    }
    return out;
}
/** Test/teardown seam — clears the registry. */
export function _resetProviders() {
    providers.clear();
}
// ─── Capability adapters ─────────────────────────────────────────────────────
// Wrap each concrete provider so it satisfies the MediaProvider contract with a
// single `capability` tag + declarative envKey, instead of the old boolean
// flags / parallel ENV_KEY map.
function imageEntry(p) {
    return {
        name: p.name,
        capability: "image",
        // displayName tracks the default model, not the gateway id — the id
        // ("openrouter-image") is stable while DEFAULT_MODEL may move.
        displayName: "GPT Image 2 (via OpenRouter)",
        envKey: "OPENROUTER_API_KEY",
        default: true,
        generateImage: (opts) => p.generateImage(opts),
    };
}
function videoEntry(p) {
    return {
        name: p.id,
        capability: "video",
        displayName: p.displayName,
        envKey: "OPENROUTER_API_KEY",
        default: true,
        generateVideo: (opts) => p.generateVideo(opts),
    };
}
function ttsEntry(p, opts) {
    return {
        name: p.id,
        capability: "tts",
        envKey: opts.envKey,
        default: opts.default,
        tts: p,
    };
}
function musicEntry(p) {
    return {
        name: p.id,
        capability: "music",
        displayName: p.displayName,
        // Declarative: doctor / availability gate on OPENROUTER_API_KEY. The HTTP
        // route injects the key from config.openrouter.apiKey at call time, so this
        // adapter (env-keyed singleton) is only the list/default/validation handle.
        envKey: "OPENROUTER_API_KEY",
        default: true,
        generateMusic: (opts) => p.generateMusic(opts),
    };
}
/**
 * Register the providers that need no runtime config: video (seedance) and TTS
 * (Gemini-via-OpenRouter default + edge-tts fallback — PRD-0003 §2 flipped the
 * chain from edge→openai-direct to Gemini→edge; openai-direct is retired). These
 * are stateless adapters — a key gates *availability*, not *registration* — so,
 * like the old module-level video array, they register at import time. This
 * makes the registry usable by any module that imports it (e.g. api.ts under
 * test) without a full server boot. Idempotent: re-registering the same key is a
 * harmless overwrite.
 */
function registerStaticProviders() {
    if (!getProvider("video", "seedance")) {
        registerProvider(videoEntry(createSeedanceProvider()));
    }
    if (!getProvider("tts", "gemini")) {
        registerProvider(ttsEntry(geminiTtsProvider, { envKey: "OPENROUTER_API_KEY", default: true }));
    }
    if (!getProvider("tts", "edge-tts")) {
        registerProvider(ttsEntry(edgeTtsProvider, { envKey: "EDGE_TTS_PATH" }));
    }
    if (!getProvider("music", "lyria")) {
        // Keyless singleton — registration is key-independent; the key gates
        // availability + is injected per-call by the BGM route from config.yaml.
        registerProvider(musicEntry(createLyriaProvider(process.env.OPENROUTER_API_KEY ?? "")));
    }
}
// Populate the keyless capabilities on import — mirrors the pre-ADR-007 video
// registry that was a module-level array, so direct importers see them.
registerStaticProviders();
/**
 * Assemble every capability in one place. Video + TTS are registered statically
 * on import (see registerStaticProviders); this adds the keyed image provider
 * (OpenRouter gpt-5.4-image-2) once a key is present, and re-asserts the static
 * set so a fresh process / reset has them too.
 */
export async function initProviders(config) {
    registerStaticProviders();
    // image — OpenRouter gpt-5.4-image-2, default for "image".
    if (config.openrouter?.apiKey && !getProvider("image", "openrouter-image")) {
        registerProvider(imageEntry(new OpenRouterImageProvider(config.openrouter.apiKey)));
    }
}
//# sourceMappingURL=registry.js.map