import axios from 'axios'; import redisClient from './redis-client.js'; const OFF_API_BASE = 'https://world.openfoodfacts.org/api/v2'; const CACHE_TTL = 3600; const BABY_CATEGORIES = { 'baby food': 'baby_food', 'baby milk': 'baby_milk', 'baby cereal': 'baby_cereal', }; export function transformOFFProduct(offProduct) { if (!offProduct || !offProduct._id || !offProduct.product_name) { return null; } const categoriesTags = offProduct.categories_tags || []; let category = 'baby_food'; for (const tag of categoriesTags) { const lower = tag.toLowerCase(); if (lower.includes('baby-milk') || lower.includes('infant-milk')) { category = 'baby_milk'; break; } if (lower.includes('baby-cereal') || lower.includes('infant-cereal')) { category = 'baby_cereal'; break; } } return { id: offProduct._id, source: 'openfoodfacts', name: offProduct.product_name, brand: offProduct.brands || '', category, image_url: offProduct.image_url || null, nutriscore: offProduct.nutriscore_grade || null, ingredients: offProduct.ingredients_text || null, nova_group: offProduct.nova_group || null, eco_score: offProduct.ecoscore_grade || null, }; } export async function searchBabyProducts(query) { if (!query || query.trim().length < 2) { return []; } const searchTerm = query.trim().toLowerCase(); const cacheKey = `off:baby:${searchTerm}`; try { const cachedData = await redisClient.get(cacheKey); if (cachedData) { console.log(`📦 Cache hit for OFF search: ${searchTerm}`); return JSON.parse(cachedData); } console.log(`🌐 Fetching from OFF API: ${searchTerm}`); const response = await axios.get(`${OFF_API_BASE}/search`, { params: { categories_tags: 'baby-food', search_terms: searchTerm, json: true, fields: 'product_name,brands,image_url,nutriscore_grade,ingredients_text,nova_group,ecoscore_grade,categories_tags', page_size: 20, }, timeout: 5000, }); if (response.data && response.data.products) { const products = response.data.products .map(transformOFFProduct) .filter(Boolean); await redisClient.setEx(cacheKey, CACHE_TTL, JSON.stringify(products)); console.log(`✅ Cached ${products.length} OFF products for: ${searchTerm}`); return products; } return []; } catch (error) { console.error('Error searching baby products from OFF:', error.message); return []; } } export async function getBabyProductDetails(barcode) { if (!barcode) { return null; } const cacheKey = `off:product:${barcode}`; try { const cachedData = await redisClient.get(cacheKey); if (cachedData) { console.log(`📦 Cache hit for OFF product: ${barcode}`); return JSON.parse(cachedData); } console.log(`🌐 Fetching OFF product details: ${barcode}`); const response = await axios.get(`${OFF_API_BASE}/product/${barcode}.json`, { timeout: 5000, }); if (response.data && response.data.product) { const product = transformOFFProduct(response.data.product); if (product) { await redisClient.setEx(cacheKey, CACHE_TTL, JSON.stringify(product)); console.log(`✅ Cached OFF product: ${barcode}`); return product; } } return null; } catch (error) { console.error(`Error fetching OFF product ${barcode}:`, error.message); return null; } } export async function clearCache(pattern = 'off:*') { try { const keys = await redisClient.keys(pattern); if (keys.length > 0) { await redisClient.del(keys); console.log(`🗑️ Cleared ${keys.length} OFF cache entries`); return keys.length; } return 0; } catch (error) { console.error('Error clearing OFF cache:', error); return 0; } }