import { DEFAULT_DISCLAIMER, isPriceSearchResponse, parseN8nResponseBody, } from "@/src/lib/price-search/normalize"; import type { PriceSearchRequest, PriceSearchResponse } from "@/src/types/priceSearch"; function getWebhookConfig() { const url = process.env.N8N_PRICE_SEARCH_WEBHOOK_URL?.trim() ?? process.env.N8N_PRICE_WEBHOOK_URL?.trim(); const secret = process.env.N8N_PRICE_WEBHOOK_SECRET?.trim(); const timeoutMs = Number(process.env.N8N_PRICE_WEBHOOK_TIMEOUT_MS ?? 45_000); if (!url || !secret) { throw new Error("Ricerca prezzi n8n non configurata (URL o secret mancanti)"); } return { url, secret, timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : 45_000 }; } export async function searchPriceOffersViaN8n( input: PriceSearchRequest, meta?: { userId?: string; userName?: string }, ): Promise { const { url, secret, timeoutMs } = getWebhookConfig(); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "X-Webhook-Secret": secret, }, body: JSON.stringify({ ...input, meta: { source: "magricambi", userId: meta?.userId ?? null, userName: meta?.userName ?? null, requestedAt: new Date().toISOString(), }, }), signal: controller.signal, cache: "no-store", }); const text = await res.text(); const data = parseN8nResponseBody(text, res.status); if (!res.ok) { const err = data && typeof data === "object" && "error" in data ? String((data as { error: unknown }).error) : `Errore n8n (HTTP ${res.status})`; throw new Error(err); } if (!isPriceSearchResponse(data)) { throw new Error( "Formato risposta n8n non conforme. Verifica il nodo Respond OK del workflow.", ); } return data; } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new Error("Timeout ricerca prezzi: riprova tra poco"); } throw error; } finally { clearTimeout(timer); } } export { DEFAULT_DISCLAIMER };