import { Language } from '../../types'; import { BackendDatabaseEntry } from './contracts'; import { LEXICON_BATCH_1_ENTRIES } from '../../constants/lexiconBatch1'; import { LEXICON_BATCH_2_ENTRIES } from '../../constants/lexiconBatch2'; import { normalizeSearchText, rankHybridEntries } from '../../utils/hybridSearch'; interface MockPlantSeed { botanicalName: string; names: Record; descriptions: Record; imageUri: string; categories: string[]; baseConfidence: number; care: { waterIntervalDays: number; light: Record; temp: string; }; } const PLANTS: MockPlantSeed[] = [ { botanicalName: 'Monstera deliciosa', names: { de: 'Monstera', en: 'Monstera', es: 'Monstera' }, descriptions: { de: 'Beliebte Zimmerpflanze mit grossen gelochten Blaettern. Mag helles indirektes Licht.', en: 'Popular indoor plant with large split leaves. Prefers bright indirect light.', es: 'Planta de interior popular con hojas grandes. Prefiere luz brillante indirecta.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Monstera_deliciosa2.jpg/500px-Monstera_deliciosa2.jpg', categories: ['easy', 'low_light', 'air_purifier'], baseConfidence: 0.76, care: { waterIntervalDays: 7, light: { de: 'Halbschatten', en: 'Partial shade', es: 'Sombra parcial' }, temp: '18-24C', }, }, { botanicalName: 'Sansevieria trifasciata', names: { de: 'Bogenhanf', en: 'Snake Plant', es: 'Lengua de suegra' }, descriptions: { de: 'Sehr robust und trockenheitsvertraeglich. Kommt auch mit wenig Licht klar.', en: 'Very resilient and drought tolerant. Handles low light well.', es: 'Muy resistente y tolera sequia. Funciona bien con poca luz.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fb/Snake_Plant_%28Sansevieria_trifasciata_%27Laurentii%27%29.jpg/500px-Snake_Plant_%28Sansevieria_trifasciata_%27Laurentii%27%29.jpg', categories: ['easy', 'succulent', 'low_light'], baseConfidence: 0.73, care: { waterIntervalDays: 14, light: { de: 'Schatten bis Sonne', en: 'Shade to sun', es: 'Sombra a sol' }, temp: '16-30C', }, }, { botanicalName: 'Aloe vera', names: { de: 'Aloe Vera', en: 'Aloe Vera', es: 'Aloe Vera' }, descriptions: { de: 'Sukkulente mit heilender Gelstruktur in den Blaettern. Braucht viel Licht.', en: 'Succulent with gel-filled leaves. Needs plenty of light.', es: 'Suculenta con hojas de gel. Necesita bastante luz.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Aloe_vera_flower_inset.png/500px-Aloe_vera_flower_inset.png', categories: ['succulent', 'easy', 'sun'], baseConfidence: 0.78, care: { waterIntervalDays: 12, light: { de: 'Sonnig', en: 'Sunny', es: 'Soleado' }, temp: '18-30C', }, }, { botanicalName: 'Epipremnum aureum', names: { de: 'Efeutute', en: 'Pothos', es: 'Poto' }, descriptions: { de: 'Schnell wachsende Haengepflanze fuer Einsteiger. Vertraegt variierende Bedingungen.', en: 'Fast-growing trailing plant ideal for beginners. Handles varied conditions.', es: 'Planta colgante de crecimiento rapido para principiantes.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Money_Plant_%28Epipremnum_aureum%29_4.jpg/500px-Money_Plant_%28Epipremnum_aureum%29_4.jpg', categories: ['easy', 'hanging', 'air_purifier'], baseConfidence: 0.74, care: { waterIntervalDays: 7, light: { de: 'Halbschatten bis hell', en: 'Partial shade to bright', es: 'Sombra parcial a brillante' }, temp: '18-27C', }, }, { botanicalName: 'Spathiphyllum', names: { de: 'Einblatt', en: 'Peace Lily', es: 'Cuna de moises' }, descriptions: { de: 'Bluetenpflanze fuer Innenraeume. Zeigt Wasserbedarf schnell durch haengende Blaetter.', en: 'Indoor flowering plant. Droops clearly when it needs water.', es: 'Planta de flor de interior. Muestra rapido cuando necesita riego.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Spathiphyllum_cochlearispathum_RTBG.jpg/500px-Spathiphyllum_cochlearispathum_RTBG.jpg', categories: ['flowering', 'low_light'], baseConfidence: 0.72, care: { waterIntervalDays: 5, light: { de: 'Halbschatten', en: 'Partial shade', es: 'Sombra parcial' }, temp: '18-26C', }, }, { botanicalName: 'Zamioculcas zamiifolia', names: { de: 'Gluecksfeder', en: 'ZZ Plant', es: 'Planta ZZ' }, descriptions: { de: 'Extrem pflegeleichte Pflanze mit dickem Rhizom. Perfekt fuer wenig Zeit.', en: 'Extremely low-maintenance plant with thick rhizomes. Great for busy schedules.', es: 'Planta muy facil de cuidar con rizomas gruesos.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Zamioculcas_zamiifolia_1.jpg/500px-Zamioculcas_zamiifolia_1.jpg', categories: ['easy', 'low_light'], baseConfidence: 0.77, care: { waterIntervalDays: 14, light: { de: 'Wenig Licht bis hell', en: 'Low light to bright', es: 'Poca luz a brillante' }, temp: '18-28C', }, }, { botanicalName: 'Calathea orbifolia', names: { de: 'Korbmarante', en: 'Calathea', es: 'Calathea' }, descriptions: { de: 'Dekorative Blaetter mit Muster. Liebt gleichmaessige Feuchte und hohe Luftfeuchtigkeit.', en: 'Decorative patterned leaves. Prefers steady moisture and humidity.', es: 'Hojas decorativas con patron. Prefiere humedad constante.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Calathea_orbifolia_2.jpg/500px-Calathea_orbifolia_2.jpg', categories: ['patterned', 'pet_friendly'], baseConfidence: 0.68, care: { waterIntervalDays: 4, light: { de: 'Halbschatten', en: 'Partial shade', es: 'Sombra parcial' }, temp: '18-25C', }, }, { botanicalName: 'Chlorophytum comosum', names: { de: 'Gruenlilie', en: 'Spider Plant', es: 'Cinta' }, descriptions: { de: 'Unkomplizierte Zimmerpflanze mit langen gebogenen Blaettern. Vermehrt sich schnell.', en: 'Easy houseplant with long arched leaves. Propagates quickly.', es: 'Planta sencilla con hojas largas arqueadas. Se multiplica rapido.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Hierbabuena_0611_Revised.jpg/500px-Hierbabuena_0611_Revised.jpg', categories: ['easy', 'pet_friendly', 'air_purifier'], baseConfidence: 0.79, care: { waterIntervalDays: 6, light: { de: 'Hell bis halbschattig', en: 'Bright to partial shade', es: 'Brillante a sombra parcial' }, temp: '16-24C', }, }, { botanicalName: 'Anthurium andraeanum', names: { de: 'Flamingoblume', en: 'Anthurium', es: 'Anturio' }, descriptions: { de: 'Auffaellige Hochblaetter und lange Bluetezeit. Braucht Waerme und gleichmaessige Feuchte.', en: 'Known for vivid spathes and long bloom cycles. Likes warmth and even moisture.', es: 'Conocida por sus flores vistosas. Requiere calor y riego regular.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/AnthuriumAndraenum.jpg/500px-AnthuriumAndraenum.jpg', categories: ['flowering', 'high_humidity'], baseConfidence: 0.71, care: { waterIntervalDays: 6, light: { de: 'Hell ohne direkte Sonne', en: 'Bright indirect light', es: 'Luz brillante indirecta' }, temp: '18-27C', }, }, { botanicalName: 'Nephrolepis exaltata', names: { de: 'Schwertfarn', en: 'Boston Fern', es: 'Helecho de Boston' }, descriptions: { de: 'Fein gefiederter Farn. Benoetigt regelmaessige Feuchte und keine direkte Sonne.', en: 'Fine-textured fern. Needs regular moisture and no harsh direct sun.', es: 'Helecho de textura fina. Necesita humedad constante.', }, imageUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Boston_Fern_%282873392811%29.png/500px-Boston_Fern_%282873392811%29.png', categories: ['pet_friendly', 'air_purifier', 'high_humidity'], baseConfidence: 0.7, care: { waterIntervalDays: 3, light: { de: 'Halbschatten', en: 'Partial shade', es: 'Sombra parcial' }, temp: '16-24C', }, }, ]; const hashString = (value: string): number => { let hash = 2166136261; for (let i = 0; i < value.length; i += 1) { hash ^= value.charCodeAt(i); hash = Math.imul(hash, 16777619); } return hash >>> 0; }; const clampConfidence = (value: number): number => { return Number(Math.max(0.4, Math.min(0.99, value)).toFixed(2)); }; const toEntry = ( seed: MockPlantSeed, lang: Language, confidence: number, ): BackendDatabaseEntry => { return { name: seed.names[lang], botanicalName: seed.botanicalName, description: seed.descriptions[lang], confidence: clampConfidence(confidence), careInfo: { waterIntervalDays: seed.care.waterIntervalDays, light: seed.care.light[lang], temp: seed.care.temp, }, imageUri: seed.imageUri, categories: seed.categories, }; }; const getFullCatalog = (lang: Language): BackendDatabaseEntry[] => { const seeds = PLANTS.map(seed => toEntry(seed, lang, seed.baseConfidence)); const batches = getBatchCatalog(lang); const catalog = [...seeds, ...batches]; const seenBotanical = new Set(); const seenImage = new Set(); return catalog.filter(e => { const botanicalKey = normalizeSearchText(e.botanicalName); const imageKey = e.imageUri.trim(); if (seenBotanical.has(botanicalKey) || seenImage.has(imageKey)) { return false; } seenBotanical.add(botanicalKey); seenImage.add(imageKey); return true; }); }; export const getMockCatalog = (lang: Language): BackendDatabaseEntry[] => { return getFullCatalog(lang); }; export const getMockPlantByImage = ( imageUri: string, lang: Language, preferHighConfidence: boolean, ): BackendDatabaseEntry => { const seeds = PLANTS; const indexHash = hashString(`${lang}:${imageUri}`); const selectedSeed = seeds[indexHash % seeds.length]; const varianceHash = hashString(`confidence:${imageUri}:${selectedSeed.botanicalName}`); const variance = (varianceHash % 28) / 100; // 0.00 - 0.27 const confidenceBase = selectedSeed.baseConfidence - 0.08 + variance; const confidence = preferHighConfidence ? confidenceBase + 0.09 : confidenceBase; return toEntry(selectedSeed, lang, confidence); }; const getBatchCatalog = (lang: Language): BackendDatabaseEntry[] => { const all = [...LEXICON_BATCH_1_ENTRIES, ...LEXICON_BATCH_2_ENTRIES]; return all.map(entry => ({ name: entry.name, botanicalName: entry.botanicalName, description: entry.description || '', confidence: entry.confidence, careInfo: { waterIntervalDays: entry.careInfo.waterIntervalDays, light: entry.careInfo.light, temp: entry.careInfo.temp, }, imageUri: entry.imageUri, categories: entry.categories, })); }; export const searchMockCatalog = ( query: string, lang: Language, limit = 12, ): BackendDatabaseEntry[] => { const normalizedQuery = normalizeSearchText(query); const deduped = getFullCatalog(lang); if (!normalizedQuery) return deduped.slice(0, limit); return rankHybridEntries(deduped, normalizedQuery, limit) .map((candidate) => candidate.entry); };