import AsyncStorage from '@react-native-async-storage/async-storage'; import { Plant, Language, AppearanceMode, ColorPalette } from '../types'; const STORAGE_KEY = 'greenlens_plants'; const LANG_KEY = 'greenlens_language'; const ONBOARDING_KEY = 'greenlens_onboarding_complete'; const APPEARANCE_MODE_KEY = 'greenlens_appearance_mode'; const COLOR_PALETTE_KEY = 'greenlens_color_palette'; const PROFILE_IMAGE_KEY = 'greenlens_profile_image'; const PROFILE_NAME_KEY = 'greenlens_profile_name'; const LEXICON_SEARCH_HISTORY_KEY = 'greenlens_lexicon_search_history'; const LEXICON_SEARCH_HISTORY_LIMIT = 10; const DEFAULT_PROFILE_NAME = 'GreenLens User'; const isAppearanceMode = (value: string | null): value is AppearanceMode => value === 'system' || value === 'light' || value === 'dark'; const isColorPalette = (value: string | null): value is ColorPalette => value === 'forest' || value === 'ocean' || value === 'sunset' || value === 'mono'; const normalizeSearchQuery = (value: string): string => { return value .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .trim() .replace(/\s+/g, ' '); }; export const StorageService = { getPlants: async (): Promise => { try { const json = await AsyncStorage.getItem(STORAGE_KEY); return json ? JSON.parse(json) : []; } catch (e) { console.error('Failed to load plants', e); return []; } }, savePlant: async (plant: Plant): Promise => { const plants = await StorageService.getPlants(); const updatedPlants = [plant, ...plants]; await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(updatedPlants)); }, deletePlant: async (id: string): Promise => { const plants = await StorageService.getPlants(); const updatedPlants = plants.filter(p => p.id !== id); await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(updatedPlants)); }, updatePlant: async (updatedPlant: Plant): Promise => { const plants = await StorageService.getPlants(); const index = plants.findIndex(p => p.id === updatedPlant.id); if (index !== -1) { plants[index] = updatedPlant; await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(plants)); } }, getLanguage: async (): Promise => { try { const lang = await AsyncStorage.getItem(LANG_KEY); return (lang as Language) || 'en'; } catch (e) { return 'en'; } }, saveLanguage: async (lang: Language): Promise => { await AsyncStorage.setItem(LANG_KEY, lang); }, getAppearanceMode: async (): Promise => { try { const mode = await AsyncStorage.getItem(APPEARANCE_MODE_KEY); return isAppearanceMode(mode) ? mode : 'system'; } catch (e) { return 'system'; } }, saveAppearanceMode: async (mode: AppearanceMode): Promise => { await AsyncStorage.setItem(APPEARANCE_MODE_KEY, mode); }, getColorPalette: async (): Promise => { try { const palette = await AsyncStorage.getItem(COLOR_PALETTE_KEY); return isColorPalette(palette) ? palette : 'forest'; } catch (e) { return 'forest'; } }, saveColorPalette: async (palette: ColorPalette): Promise => { await AsyncStorage.setItem(COLOR_PALETTE_KEY, palette); }, getProfileImage: async (): Promise => { try { const imageUri = await AsyncStorage.getItem(PROFILE_IMAGE_KEY); return imageUri || null; } catch (e) { return null; } }, saveProfileImage: async (imageUri: string | null): Promise => { if (!imageUri) { await AsyncStorage.removeItem(PROFILE_IMAGE_KEY); return; } await AsyncStorage.setItem(PROFILE_IMAGE_KEY, imageUri); }, getProfileName: async (): Promise => { try { const profileName = await AsyncStorage.getItem(PROFILE_NAME_KEY); const normalized = profileName?.trim(); return normalized || DEFAULT_PROFILE_NAME; } catch (e) { return DEFAULT_PROFILE_NAME; } }, saveProfileName: async (name: string): Promise => { const normalized = name.trim(); await AsyncStorage.setItem(PROFILE_NAME_KEY, normalized || DEFAULT_PROFILE_NAME); }, getOnboardingComplete: async (): Promise => { try { const value = await AsyncStorage.getItem(ONBOARDING_KEY); return value === 'true'; } catch (e) { return false; } }, setOnboardingComplete: async (complete: boolean): Promise => { await AsyncStorage.setItem(ONBOARDING_KEY, complete ? 'true' : 'false'); }, getLexiconSearchHistory: async (): Promise => { try { const value = await AsyncStorage.getItem(LEXICON_SEARCH_HISTORY_KEY); if (!value) return []; const parsed = JSON.parse(value); if (!Array.isArray(parsed)) return []; return parsed.filter((item): item is string => typeof item === 'string'); } catch (e) { console.error('Failed to load lexicon search history', e); return []; } }, saveLexiconSearchQuery: async (query: string): Promise => { const trimmed = query.trim(); if (!trimmed) return; const history = await StorageService.getLexiconSearchHistory(); const normalized = normalizeSearchQuery(trimmed); const deduped = history.filter( item => normalizeSearchQuery(item) !== normalized ); const updated = [trimmed, ...deduped].slice(0, LEXICON_SEARCH_HISTORY_LIMIT); await AsyncStorage.setItem(LEXICON_SEARCH_HISTORY_KEY, JSON.stringify(updated)); }, clearLexiconSearchHistory: async (): Promise => { await AsyncStorage.removeItem(LEXICON_SEARCH_HISTORY_KEY); }, };