import {
  clearModelCache,
  createLLMClient,
  detectModel
} from "./chunk-HHAJ3QF6.js";
import {
  detectBackend,
  getModelProfile
} from "./chunk-HGAMIPFO.js";
import {
  logger
} from "./chunk-K44MW7JJ.js";

// src/server/provider-manager.ts
function normalizeModelId(s) {
  return s.toLowerCase().replace(/[-_\s:.]+/g, "");
}
async function fetchModelsFromBackend(url, apiKey) {
  const headers = { "Content-Type": "application/json" };
  if (apiKey) {
    headers["Authorization"] = `Bearer ${apiKey}`;
  }
  try {
    const response = await fetch(url, { method: "GET", headers, signal: AbortSignal.timeout(1e4) });
    if (!response.ok) {
      logger.debug("Failed to fetch models", { url, status: response.status });
      return [];
    }
    const data = await response.json();
    if (data.data && Array.isArray(data.data)) {
      return data.data.map((m) => ({
        id: m.id,
        contextWindow: m.max_model_len ?? void 0
      }));
    }
    return [];
  } catch (error) {
    logger.debug("Error fetching models", { url, error: error instanceof Error ? error.message : String(error) });
    return [];
  }
}
function enrichWithProfileDefaults(model) {
  const profile = getModelProfile(model.id);
  return {
    ...model,
    defaultTemperature: profile.temperature,
    defaultTopP: profile.topP,
    ...profile.topK !== void 0 && { defaultTopK: profile.topK },
    defaultMaxTokens: profile.defaultMaxTokens
  };
}
function mergeModelsWithUserOverrides(backendModels, userModels) {
  const normalizedUserIdMap = new Map(userModels.map((m) => [normalizeModelId(m.id), m]));
  const updatedModels = backendModels.map((backendModel) => {
    const existingUserModel = normalizedUserIdMap.get(normalizeModelId(backendModel.id));
    if (existingUserModel) {
      return enrichWithProfileDefaults({ ...existingUserModel, id: backendModel.id });
    }
    return enrichWithProfileDefaults(backendModel);
  });
  const normalizedBackendIds = new Set(backendModels.map((m) => normalizeModelId(m.id)));
  for (const userModel of userModels) {
    if (!normalizedBackendIds.has(normalizeModelId(userModel.id))) {
      updatedModels.push(enrichWithProfileDefaults(userModel));
    }
  }
  return updatedModels;
}
async function fetchAvailableModelsFromBackend(baseUrl, apiKey) {
  const url = baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
  const models = await fetchModelsFromBackend(url, apiKey);
  return models.map((m) => m.id);
}
async function fetchModelsWithContext(baseUrl, apiKey, backend) {
  logger.info("fetchModelsWithContext called", { baseUrl, apiKey: !!apiKey, backend });
  if (backend === "ollama") {
    logger.info("Fetching Ollama models via /api/tags and /api/show");
    return fetchOllamaModelsWithContext(baseUrl, apiKey);
  }
  const isOpenCodeGo = baseUrl.includes("opencode.ai/zen/go");
  const url = isOpenCodeGo ? baseUrl.replace("/zen/go", "/zen").replace(/\/v1$/, "") + "/v1/models" : baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
  logger.info("Fetching models via /v1/models", { url });
  const models = await fetchModelsFromBackend(url, apiKey);
  if (models.length === 0) return [];
  logger.info("Fetched models from /v1/models", { count: models.length });
  return models.map((m) => ({
    id: m.id,
    contextWindow: m.contextWindow ?? 2e5,
    source: m.contextWindow ? "backend" : "default"
  }));
}
async function fetchOllamaModelsWithContext(baseUrl, _apiKey) {
  const tagsUrl = `${baseUrl}/api/tags`;
  try {
    const tagsResponse = await fetch(tagsUrl, {
      signal: AbortSignal.timeout(1e4)
    });
    if (!tagsResponse.ok) {
      logger.debug("Failed to fetch Ollama tags", { url: tagsUrl });
      return [];
    }
    const tagsData = await tagsResponse.json();
    if (!tagsData.models || !Array.isArray(tagsData.models)) {
      return [];
    }
    const modelsWithContext = [];
    for (const model of tagsData.models) {
      try {
        const showUrl = `${baseUrl}/api/show`;
        const showResponse = await fetch(showUrl, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name: model.name, verbose: true }),
          signal: AbortSignal.timeout(5e3)
        });
        if (showResponse.ok) {
          const showData = await showResponse.json();
          const contextLength = showData.model_info?.llama?.context_length ?? showData.model_info?.context_length ?? 2e5;
          modelsWithContext.push({
            id: model.name,
            contextWindow: contextLength,
            source: contextLength !== 2e5 ? "backend" : "default"
          });
        } else {
          modelsWithContext.push({
            id: model.name,
            contextWindow: 2e5,
            source: "default"
          });
        }
      } catch (error) {
        logger.debug("Failed to fetch Ollama model context", {
          model: model.name,
          error: error instanceof Error ? error.message : String(error)
        });
        modelsWithContext.push({
          id: model.name,
          contextWindow: 2e5,
          source: "default"
        });
      }
    }
    return modelsWithContext;
  } catch (error) {
    logger.info("Error fetching Ollama models", {
      url: baseUrl,
      error: error instanceof Error ? error.message : String(error)
    });
    return [];
  }
}
function parseDefaultModelSelection(selection) {
  if (!selection) return { providerId: void 0, model: void 0 };
  const slashIndex = selection.indexOf("/");
  if (slashIndex === -1) return { providerId: selection, model: "auto" };
  return {
    providerId: selection.substring(0, slashIndex),
    model: selection.substring(slashIndex + 1)
  };
}
function createProviderManager(config) {
  let providers = [...config.providers ?? []];
  let defaultModelSelection = config.defaultModelSelection;
  let llmClient = createLLMClient(config);
  const providerStatus = /* @__PURE__ */ new Map();
  logger.debug("ProviderManager created", {
    providers: providers.map((p) => ({
      id: p.id,
      models: p.models.map((m) => ({ id: m.id, contextWindow: m.contextWindow, source: m.source }))
    }))
  });
  for (const p of providers) {
    providerStatus.set(p.id, "unknown");
  }
  function createConfigForProvider(provider, model) {
    return {
      ...config,
      llm: {
        ...config.llm,
        baseUrl: provider.url.includes("/v1") ? provider.url : `${provider.url}/v1`,
        model,
        backend: provider.backend,
        ...provider.apiKey && { apiKey: provider.apiKey }
      }
    };
  }
  const { providerId: activeProviderId, model: activeModel } = parseDefaultModelSelection(defaultModelSelection);
  if (activeProviderId && activeModel) {
    const activeProvider = providers.find((p) => p.id === activeProviderId);
    if (activeProvider && activeProvider.apiKey) {
      llmClient = createLLMClient(createConfigForProvider(activeProvider, activeModel));
    }
  }
  return {
    getProviders() {
      return providers.map((p) => ({
        ...p,
        status: providerStatus.get(p.id) ?? "unknown"
      }));
    },
    getActiveProvider() {
      const { providerId } = parseDefaultModelSelection(defaultModelSelection);
      if (!providerId) return void 0;
      return providers.find((p) => p.id === providerId);
    },
    getActiveProviderId() {
      const { providerId } = parseDefaultModelSelection(defaultModelSelection);
      return providerId;
    },
    getCurrentModel() {
      const { model } = parseDefaultModelSelection(defaultModelSelection);
      return model;
    },
    getLLMClient() {
      return llmClient;
    },
    async activateProvider(providerId, options) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return { success: false, error: "Provider not found" };
      }
      const currentModel = parseDefaultModelSelection(defaultModelSelection).model ?? "auto";
      const targetModel = options?.model ?? currentModel;
      const isModelSwitch = providerId === parseDefaultModelSelection(defaultModelSelection).providerId && options?.model && options.model !== currentModel;
      if (providerId === parseDefaultModelSelection(defaultModelSelection).providerId && !isModelSwitch) {
        return { success: true };
      }
      logger.info("Switching provider", {
        from: parseDefaultModelSelection(defaultModelSelection).providerId,
        to: providerId,
        providerName: provider.name,
        url: provider.url,
        model: targetModel
      });
      const providerConfig = createConfigForProvider(provider, targetModel);
      const newClient = createLLMClient(providerConfig);
      try {
        const url = provider.url.includes("/v1") ? provider.url.replace("/v1", "") : provider.url;
        clearModelCache(url);
        const backend = provider.backend;
        logger.info("activateProvider fetching models", { providerId, providerName: provider.name, url, backend });
        const modelsWithContext = await fetchModelsWithContext(url, provider.apiKey, backend);
        const userModels = provider.models.filter((m) => m.source === "user");
        logger.debug("activateProvider", {
          providerId,
          backendModelsCount: modelsWithContext.length,
          userModelsCount: userModels.length
        });
        if (modelsWithContext.length > 0) {
          const updatedModels = mergeModelsWithUserOverrides(modelsWithContext, userModels);
          providers = providers.map((p) => p.id === providerId ? { ...p, models: updatedModels } : p);
        } else if (userModels.length > 0) {
          logger.debug("Backend unavailable during provider switch, preserving user models", {
            providerId,
            userModelsCount: userModels.length
          });
          providers = providers.map((p) => p.id === providerId ? { ...p, models: userModels } : p);
        }
        if (provider.backend === "auto") {
          const detected = await detectBackend(url);
          newClient.setBackend(detected);
        } else {
          newClient.setBackend(provider.backend);
        }
        if (targetModel === "auto") {
          const detected = await detectModel(url);
          if (detected) {
            newClient.setModel(detected);
          }
        } else {
          newClient.setModel(targetModel);
        }
        providerStatus.set(providerId, "connected");
      } catch (err) {
        logger.warn("Could not connect to provider", {
          providerId,
          error: err instanceof Error ? err.message : String(err)
        });
        providerStatus.set(providerId, "disconnected");
      }
      providers = providers.map((p) => ({
        ...p,
        isActive: p.id === providerId
      }));
      defaultModelSelection = `${providerId}/${targetModel}`;
      llmClient = newClient;
      logger.info("Provider activated", {
        providerId,
        providerName: provider.name,
        model: llmClient.getModel(),
        backend: llmClient.getBackend()
      });
      return { success: true };
    },
    addProvider(providerData) {
      const id = crypto.randomUUID();
      const provider = {
        ...providerData,
        id,
        createdAt: (/* @__PURE__ */ new Date()).toISOString()
      };
      if (providerData.isActive || providers.length === 0) {
        providers = providers.map((p) => ({ ...p, isActive: false }));
        provider.isActive = true;
        defaultModelSelection = `${id}/auto`;
      }
      providers.push(provider);
      providerStatus.set(id, "unknown");
      return provider;
    },
    removeProvider(providerId) {
      const index = providers.findIndex((p) => p.id === providerId);
      if (index === -1) return false;
      const wasActive = providers[index]?.isActive;
      providers.splice(index, 1);
      providerStatus.delete(providerId);
      if (wasActive && providers.length > 0) {
        providers[0].isActive = true;
        defaultModelSelection = `${providers[0].id}/auto`;
      } else if (providers.length === 0) {
        defaultModelSelection = void 0;
      }
      return true;
    },
    setProviders(newProviders, newDefaultModelSelection) {
      const wasActiveProviderId = this.getActiveProviderId();
      providers = [...newProviders];
      defaultModelSelection = newDefaultModelSelection;
      providerStatus.clear();
      for (const p of providers) {
        providerStatus.set(p.id, "unknown");
      }
      const newActiveProviderId = this.getActiveProviderId();
      if (newActiveProviderId && newActiveProviderId !== wasActiveProviderId) {
        const activeProvider = providers.find((p) => p.id === newActiveProviderId);
        if (activeProvider) {
          const providerConfig = createConfigForProvider(activeProvider, this.getCurrentModel() ?? "auto");
          llmClient = createLLMClient(providerConfig);
          logger.info("setProviders: recreated LLM client for new active provider", {
            providerId: newActiveProviderId,
            url: activeProvider.url,
            hasApiKey: !!activeProvider.apiKey
          });
        }
      }
    },
    getProviderStatus(providerId) {
      return providerStatus.get(providerId) ?? "unknown";
    },
    async getProviderModels(providerId) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return [];
      }
      if (provider.models && provider.models.length > 0) {
        return provider.models;
      }
      const url = provider.url.includes("/v1") ? provider.url.replace("/v1", "") : provider.url;
      const backend = provider.backend;
      return fetchModelsWithContext(url, provider.apiKey, backend);
    },
    async setDefaultModelSelection(providerId, model) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return { success: false, error: "Provider not found" };
      }
      logger.info("Setting default model selection", { providerId, model, providerName: provider.name });
      defaultModelSelection = `${providerId}/${model}`;
      providers = providers.map((p) => ({ ...p, isActive: p.id === providerId }));
      const currentProviderId = parseDefaultModelSelection(defaultModelSelection).providerId;
      if (currentProviderId === providerId) {
        llmClient.setModel(model);
        logger.info("Model updated", { providerId, model });
      }
      return { success: true };
    },
    getCurrentModelContext() {
      const { providerId, model } = parseDefaultModelSelection(defaultModelSelection);
      if (!providerId || !model) return config.context.maxTokens;
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) return config.context.maxTokens;
      const modelConfig = provider.models.find((m) => m.id === model);
      return modelConfig?.contextWindow ?? config.context.maxTokens;
    },
    async updateModelContext(providerId, modelId, contextWindow) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return { success: false, error: "Provider not found" };
      }
      const modelIndex = provider.models.findIndex((m) => m.id === modelId);
      if (modelIndex === -1) {
        providers = providers.map(
          (p) => p.id === providerId ? { ...p, models: [...p.models, { id: modelId, contextWindow, source: "user" }] } : p
        );
      } else {
        providers = providers.map(
          (p) => p.id === providerId ? {
            ...p,
            models: p.models.map(
              (m, i) => i === modelIndex ? { ...m, contextWindow, source: "user" } : m
            )
          } : p
        );
      }
      logger.info("Model context updated", { providerId, modelId, contextWindow });
      return { success: true };
    },
    async updateModelSettings(providerId, modelId, settings) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return { success: false, error: "Provider not found" };
      }
      const existingModel = provider.models.find((m) => m.id === modelId);
      const finalTemp = settings.temperature !== void 0 && settings.temperature !== null ? settings.temperature : existingModel?.temperature;
      const finalTopP = settings.topP !== void 0 && settings.topP !== null ? settings.topP : existingModel?.topP;
      const finalTopK = settings.topK !== void 0 && settings.topK !== null ? settings.topK : existingModel?.topK;
      const finalMaxTokens = settings.maxTokens !== void 0 && settings.maxTokens !== null ? settings.maxTokens : existingModel?.maxTokens;
      const finalSupportsVision = settings.supportsVision !== void 0 ? settings.supportsVision : existingModel?.supportsVision;
      logger.info("Updating model settings", {
        providerId,
        modelId,
        existing: existingModel,
        incoming: settings,
        final: {
          temperature: finalTemp,
          topP: finalTopP,
          topK: finalTopK,
          maxTokens: finalMaxTokens,
          supportsVision: finalSupportsVision
        }
      });
      const updatedModel = enrichWithProfileDefaults({
        id: modelId,
        contextWindow: settings.contextWindow ?? existingModel?.contextWindow ?? 2e5,
        source: "user",
        ...finalTemp !== void 0 && { temperature: finalTemp },
        ...finalTopP !== void 0 && { topP: finalTopP },
        ...finalTopK !== void 0 && { topK: finalTopK },
        ...finalMaxTokens !== void 0 && { maxTokens: finalMaxTokens },
        ...finalSupportsVision !== void 0 && { supportsVision: finalSupportsVision }
      });
      if (existingModel) {
        providers = providers.map(
          (p) => p.id === providerId ? { ...p, models: p.models.map((m) => m.id === modelId ? updatedModel : m) } : p
        );
      } else {
        providers = providers.map((p) => p.id === providerId ? { ...p, models: [...p.models, updatedModel] } : p);
      }
      logger.info("Model settings updated", { providerId, modelId, final: updatedModel });
      return { success: true, model: updatedModel };
    },
    getModelSettings(modelId) {
      const provider = providers.find((p) => p.models.some((m) => m.id === modelId));
      const model = provider?.models.find((m) => m.id === modelId);
      if (!model) return void 0;
      return {
        ...model.temperature !== void 0 && { temperature: model.temperature },
        ...model.topP !== void 0 && { topP: model.topP },
        ...model.topK !== void 0 && { topK: model.topK },
        ...model.maxTokens !== void 0 && { maxTokens: model.maxTokens },
        ...model.supportsVision !== void 0 && { supportsVision: model.supportsVision }
      };
    },
    async refreshProviderModels(providerId) {
      const provider = providers.find((p) => p.id === providerId);
      if (!provider) {
        return { success: false, error: "Provider not found" };
      }
      const url = provider.url.includes("/v1") ? provider.url.replace("/v1", "") : provider.url;
      const backend = provider.backend;
      logger.info("refreshProviderModels fetching models", { providerId, providerName: provider.name, url, backend });
      const modelsWithContext = await fetchModelsWithContext(url, provider.apiKey, backend);
      const userModels = provider.models.filter((m) => m.source === "user");
      logger.info("refreshProviderModels", {
        providerId,
        userModelsCount: userModels.length,
        backendModelsCount: modelsWithContext.length
      });
      if (modelsWithContext.length === 0) {
        providerStatus.set(providerId, "disconnected");
        if (userModels.length > 0) {
          logger.debug("Backend unavailable, preserving user models", {
            providerId,
            userModels: userModels.map((m) => ({ id: m.id, contextWindow: m.contextWindow }))
          });
          providers = providers.map((p) => p.id === providerId ? { ...p, models: userModels } : p);
          return { success: true };
        }
        return { success: false, error: "No models returned from backend" };
      }
      providerStatus.set(providerId, "connected");
      const updatedModels = mergeModelsWithUserOverrides(modelsWithContext, userModels);
      providers = providers.map((p) => p.id === providerId ? { ...p, models: updatedModels } : p);
      const { providerId: currentProviderId, model: currentModel } = parseDefaultModelSelection(defaultModelSelection);
      if (currentProviderId === providerId && currentModel) {
        const normalizedCurrentModel = normalizeModelId(currentModel);
        const matchedModel = updatedModels.find((m) => normalizeModelId(m.id) === normalizedCurrentModel);
        if (matchedModel && matchedModel.id !== currentModel) {
          defaultModelSelection = `${providerId}/${matchedModel.id}`;
          logger.debug("Updated defaultModelSelection after fuzzy match", { from: currentModel, to: matchedModel.id });
        }
      }
      logger.info("Provider models refreshed", { providerId, modelCount: updatedModels.length });
      return { success: true };
    }
  };
}

export {
  fetchAvailableModelsFromBackend,
  parseDefaultModelSelection,
  createProviderManager
};
//# sourceMappingURL=chunk-RF2JGDSS.js.map