import type { ApiSearchProvider, ApiSearchSettings } from "@/lib/contacts/discovery-types" import { techBrandIcon } from "@/lib/admin-settings/tech-brand-icons" export type ApiSearchProviderType = | "brave" | "bing" | "duckduckgo" | "searxng" | "custom" export type SearchProviderCatalogEntry = { type: ApiSearchProviderType label: string description: string icon?: string docsUrl?: string requiresApiKey: boolean requiresBaseURL: boolean supportsCustomMapping: boolean } export const SEARCH_PROVIDER_CATALOG: SearchProviderCatalogEntry[] = [ { type: "brave", label: "Brave Search", description: "API officielle Brave (X-Subscription-Token).", icon: techBrandIcon("brave"), docsUrl: "https://api.search.brave.com", requiresApiKey: true, requiresBaseURL: false, supportsCustomMapping: false, }, { type: "bing", label: "Bing Web Search", description: "Azure Cognitive Services Bing Search v7.", icon: techBrandIcon("bing"), docsUrl: "https://learn.microsoft.com/azure/cognitive-services/bing-web-search/", requiresApiKey: true, requiresBaseURL: false, supportsCustomMapping: false, }, { type: "duckduckgo", label: "DuckDuckGo", description: "Scraping HTML léger, sans clé API (best-effort).", icon: techBrandIcon("duckduckgo"), requiresApiKey: false, requiresBaseURL: false, supportsCustomMapping: false, }, { type: "searxng", label: "SearXNG", description: "Instance SearXNG auto-hébergée (format JSON).", icon: techBrandIcon("searxng"), docsUrl: "https://docs.searxng.org/", requiresApiKey: false, requiresBaseURL: true, supportsCustomMapping: false, }, { type: "custom", label: "API JSON personnalisée", description: "Endpoint GET JSON avec mapping des champs résultats.", icon: techBrandIcon("custom"), requiresApiKey: false, requiresBaseURL: true, supportsCustomMapping: true, }, ] export function catalogEntry(type: ApiSearchProviderType): SearchProviderCatalogEntry { return ( SEARCH_PROVIDER_CATALOG.find((entry) => entry.type === type) ?? SEARCH_PROVIDER_CATALOG[0] ) } export function emptySearchProvider(type: ApiSearchProviderType = "brave"): ApiSearchProvider { const entry = catalogEntry(type) return { id: crypto.randomUUID(), name: entry.label, type, api_key: "", base_url: defaultBaseURL(type), query_param: "q", auth_header: defaultAuthHeader(type), results_path: type === "custom" ? "results" : "", title_field: type === "custom" ? "title" : "", url_field: type === "custom" ? "url" : "", description_field: type === "custom" ? "description" : "", } } function defaultBaseURL(type: ApiSearchProviderType): string { switch (type) { case "bing": return "https://api.bing.microsoft.com/v7.0/search" case "searxng": return "https://searx.example.org" case "custom": return "https://api.example.com/search" default: return "" } } function defaultAuthHeader(type: ApiSearchProviderType): string { switch (type) { case "bing": return "Ocp-Apim-Subscription-Key" default: return "" } } export function ensureSearchSettingsDefaults(settings: ApiSearchSettings): ApiSearchSettings { const providers = settings.providers ?? [] let defaultId = settings.default_provider_id ?? "" if (!defaultId && providers.length === 1) { defaultId = providers[0]?.id ?? "" } if (defaultId && !providers.some((provider) => provider.id === defaultId)) { defaultId = providers[0]?.id ?? "" } return { ...settings, default_provider_id: defaultId, providers, } } export function normalizeSearchProviders(raw: ApiSearchSettings | undefined): ApiSearchSettings { const providers = raw?.providers?.length ? raw.providers.map((provider) => ({ ...emptySearchProvider(provider.type ?? "brave"), ...provider, type: provider.type ?? "brave", })) : [] return ensureSearchSettingsDefaults({ default_provider_id: raw?.default_provider_id || providers[0]?.id || "", providers, }) } export function isSearchProviderConfigured( provider: ApiSearchProvider, options?: { apiKeyConfigured?: boolean }, ): boolean { switch (provider.type) { case "brave": case "bing": return Boolean(provider.api_key?.trim() || options?.apiKeyConfigured) case "duckduckgo": return true case "searxng": return Boolean(provider.base_url?.trim()) case "custom": return Boolean( provider.base_url?.trim() && provider.results_path?.trim() && provider.title_field?.trim() && provider.url_field?.trim(), ) default: return false } }