/**
 * Provider REST routes.
 *
 * Lists available providers, exposes auth flows, and returns
 * dynamically-fetched model catalogues for each backend.
 *
 *   GET    /api/providers                  — list available providers
 *   GET    /api/providers/:id/auth/status  — get provider auth status
 *   POST   /api/providers/:id/auth/login   — start provider login
 *   POST   /api/providers/:id/auth/logout  — log out provider
 *   GET    /api/providers/:id/models       — list models for a provider
 */
import { fetchOpenAIModels, fetchOpenRouterModels, fetchOllamaModels, } from "../providers/model-fetchers.js";
import { requireAuth } from "../security/http-auth.js";
import { ProviderSnapshotCache } from "../providers/provider-snapshot.js";
export function registerProviderRoutes(app, config, deps) {
    const { providerRegistry, ws } = deps;
    // Availability + auth probing is expensive (spawns provider CLIs / ACP agents).
    // Compute it once, serve from cache, and refresh in the background so page
    // loads are instant. Warm it now so the first request never pays the cost.
    const snapshotCache = new ProviderSnapshotCache(providerRegistry);
    snapshotCache.warm();
    /** List available providers (local + remote) */
    app.get("/api/providers", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        // `?fresh=1` forces a synchronous re-probe (used by explicit UI refresh).
        const fresh = request.query?.fresh;
        const force = fresh === "1" || fresh === "true";
        const providerSnapshots = await snapshotCache.get(force);
        // Collect remote provider info from connected filesystem nodes (cheap, live).
        const remoteProviders = [];
        if (ws) {
            for (const node of ws.getFsNodes()) {
                if (node.isGateway)
                    continue;
                remoteProviders.push({
                    nodeId: node.id,
                    nodeName: node.name,
                    platform: node.platform,
                    providers: node.providers ?? [],
                });
            }
        }
        return {
            providers: providerSnapshots,
            remoteProviders,
        };
    });
    /** Get provider auth status */
    app.get("/api/providers/:id/auth/status", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        const { id } = request.params;
        const provider = providerRegistry.get(id);
        if (!provider)
            return reply.status(404).send({ error: `Unknown provider: ${id}` });
        if (!provider.getAuthStatus) {
            return reply.status(501).send({ error: `Provider ${id} does not support auth status` });
        }
        return provider.getAuthStatus();
    });
    /** Start provider login */
    app.post("/api/providers/:id/auth/login", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        const { id } = request.params;
        const provider = providerRegistry.get(id);
        if (!provider)
            return reply.status(404).send({ error: `Unknown provider: ${id}` });
        if (!provider.startLogin) {
            return reply.status(501).send({ error: `Provider ${id} does not support login` });
        }
        const result = await provider.startLogin();
        if (!result.ok && result.status === "unsupported") {
            return reply.status(501).send(result);
        }
        if (!result.ok) {
            return reply.status(500).send(result);
        }
        return result;
    });
    /** Send code input to running login process (reverse device-code / browser-callback flow) */
    app.post("/api/providers/:id/auth/login/input", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        const { id } = request.params;
        const { code } = (request.body ?? {});
        if (!code || typeof code !== "string" || !code.trim()) {
            return reply.status(400).send({ error: "Missing or empty code" });
        }
        const provider = providerRegistry.get(id);
        if (!provider)
            return reply.status(404).send({ error: `Unknown provider: ${id}` });
        if (!provider.sendLoginInput) {
            return reply.status(501).send({ error: `Provider ${id} does not support code input` });
        }
        provider.sendLoginInput(code.trim());
        return { ok: true };
    });
    /** Log out provider */
    app.post("/api/providers/:id/auth/logout", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        const { id } = request.params;
        const provider = providerRegistry.get(id);
        if (!provider)
            return reply.status(404).send({ error: `Unknown provider: ${id}` });
        if (!provider.logout) {
            return reply.status(501).send({ error: `Provider ${id} does not support logout` });
        }
        const result = await provider.logout();
        if (!result.ok && result.status === "unsupported") {
            return reply.status(501).send(result);
        }
        if (!result.ok) {
            return reply.status(500).send(result);
        }
        return result;
    });
    /** List models for a specific provider */
    app.get("/api/providers/:id/models", async (request, reply) => {
        const authUser = await requireAuth(request, reply, config.jwtSecret);
        if (!authUser)
            return;
        const { id } = request.params;
        const provider = providerRegistry.get(id);
        if (!provider) {
            return reply.status(404).send({ error: `Unknown provider: ${id}` });
        }
        if (!provider.listModels) {
            return { models: [] };
        }
        try {
            let models = await provider.listModels();
            // For jait provider, return models from ALL configured backends, grouped
            if (id === "jait" && deps.userService) {
                const settings = deps.userService.getSettings(authUser.id);
                const userApiKeys = settings.apiKeys ?? {};
                const jaitBackend = settings.jaitBackend || "openai";
                // Fetch each backend's catalogue concurrently — these are independent
                // network calls, so running them in parallel caps the worst case at a
                // single timeout instead of the sum of all three.
                // ── OpenAI models (dynamic when API key available) ────────
                const openaiKey = userApiKeys["OPENAI_API_KEY"]?.trim() || config.openaiApiKey;
                const openaiBaseUrl = userApiKeys["OPENAI_BASE_URL"]?.trim() || config.openaiBaseUrl || "https://api.openai.com/v1";
                const openaiModelsP = (async () => {
                    const fallback = models.map((m) => ({ ...m, group: "OpenAI" }));
                    if (!(openaiKey && openaiBaseUrl.includes("openai.com")))
                        return fallback;
                    try {
                        const apiModels = await fetchOpenAIModels(openaiKey, openaiBaseUrl);
                        return apiModels.length > 0 ? apiModels.map((m) => ({ ...m, group: "OpenAI" })) : fallback;
                    }
                    catch {
                        return fallback;
                    }
                })();
                // ── OpenRouter models ─────────────────────────────────────
                const openRouterKey = userApiKeys["OPENROUTER_API_KEY"]?.trim();
                const openRouterModelsP = (async () => {
                    if (!openRouterKey)
                        return [];
                    try {
                        const orModels = await fetchOpenRouterModels(openRouterKey);
                        if (orModels.length > 0)
                            return orModels.map((m) => ({ ...m, group: "OpenRouter" }));
                    }
                    catch {
                        // fall through to static list
                    }
                    const { OPENROUTER_MODELS } = await import("../providers/jait-provider.js");
                    return OPENROUTER_MODELS.map((m) => ({ ...m, group: "OpenRouter" }));
                })();
                // ── Ollama models ─────────────────────────────────────────
                const ollamaUrl = userApiKeys["OLLAMA_URL"]?.trim() || config.ollamaUrl;
                const ollamaModelsP = (async () => {
                    if (!ollamaUrl)
                        return [];
                    try {
                        const ollamaModels = await fetchOllamaModels(ollamaUrl);
                        return ollamaModels.map((m) => ({ ...m, group: "Ollama" }));
                    }
                    catch {
                        // Ollama unreachable — skip
                        return [];
                    }
                })();
                const [openaiGroup, openRouterGroup, ollamaGroup] = await Promise.all([
                    openaiModelsP,
                    openRouterModelsP,
                    ollamaModelsP,
                ]);
                models = [...openaiGroup, ...openRouterGroup, ...ollamaGroup];
                // Prepend recent models (if they exist in the full list)
                const recentIds = settings.recentModels ?? [];
                if (recentIds.length > 0) {
                    const modelMap = new Map(models.map((m) => [m.id, m]));
                    const recents = recentIds
                        .filter((rid) => modelMap.has(rid))
                        .map((rid) => ({ ...modelMap.get(rid), isRecent: true }));
                    return { models, currentBackend: jaitBackend, recentModels: recents.slice(0, 5).map((r) => r.id) };
                }
                return { models, currentBackend: jaitBackend };
            }
            return { models };
        }
        catch (err) {
            return reply.status(500).send({ error: err instanceof Error ? err.message : "Failed to list models" });
        }
    });
    app.log.info("Provider routes registered at /api/providers");
}
//# sourceMappingURL=providers.js.map