import 'package:hive_flutter/hive_flutter.dart'; import 'package:lunchpick/domain/entities/weather_info.dart'; import 'package:lunchpick/domain/repositories/weather_repository.dart'; class WeatherRepositoryImpl implements WeatherRepository { static const String _boxName = 'weather_cache'; static const String _keyCachedWeather = 'cached_weather'; static const String _keyLastUpdateTime = 'last_update_time'; static const Duration _cacheValidDuration = Duration(hours: 1); Future get _box async => await Hive.openBox(_boxName); @override Future getCurrentWeather({ required double latitude, required double longitude, }) async { // TODO: 실제 날씨 API 호출 구현 // 여기서는 임시로 더미 데이터 반환 final dummyWeather = WeatherInfo( current: WeatherData(temperature: 20, isRainy: false, description: '맑음'), nextHour: WeatherData(temperature: 22, isRainy: false, description: '맑음'), ); // 캐시에 저장 await cacheWeatherInfo(dummyWeather); return dummyWeather; } @override Future getCachedWeather() async { final box = await _box; // 캐시가 유효한지 확인 final isValid = await _isCacheValid(); if (!isValid) { return null; } // 캐시된 데이터 가져오기 final cachedData = box.get(_keyCachedWeather); if (cachedData == null) { return null; } try { // 안전한 타입 변환 if (cachedData is! Map) { print( 'WeatherCache: Invalid data type - expected Map but got ${cachedData.runtimeType}', ); await clearWeatherCache(); return null; } final Map weatherMap = Map.from( cachedData, ); // Map 구조 검증 if (!weatherMap.containsKey('current') || !weatherMap.containsKey('nextHour')) { print('WeatherCache: Missing required fields in weather data'); await clearWeatherCache(); return null; } return _weatherInfoFromMap(weatherMap); } catch (e) { // 캐시 데이터가 손상된 경우 print('WeatherCache: Error parsing cached weather data: $e'); await clearWeatherCache(); return null; } } @override Future cacheWeatherInfo(WeatherInfo weatherInfo) async { final box = await _box; // WeatherInfo를 Map으로 변환하여 저장 final weatherMap = _weatherInfoToMap(weatherInfo); await box.put(_keyCachedWeather, weatherMap); await box.put(_keyLastUpdateTime, DateTime.now().toIso8601String()); } @override Future clearWeatherCache() async { final box = await _box; await box.delete(_keyCachedWeather); await box.delete(_keyLastUpdateTime); } @override Future isWeatherUpdateNeeded() async { final box = await _box; // 캐시된 날씨 정보가 없으면 업데이트 필요 if (!box.containsKey(_keyCachedWeather)) { return true; } // 캐시가 유효한지 확인 return !(await _isCacheValid()); } Future _isCacheValid() async { final box = await _box; final lastUpdateTimeStr = box.get(_keyLastUpdateTime); if (lastUpdateTimeStr == null) { return false; } try { // 날짜 파싱 시도 final lastUpdateTime = DateTime.tryParse(lastUpdateTimeStr); if (lastUpdateTime == null) { print('WeatherCache: Invalid date format in cache: $lastUpdateTimeStr'); return false; } final now = DateTime.now(); final difference = now.difference(lastUpdateTime); return difference < _cacheValidDuration; } catch (e) { print('WeatherCache: Error checking cache validity: $e'); return false; } } Map _weatherInfoToMap(WeatherInfo weatherInfo) { return { 'current': { 'temperature': weatherInfo.current.temperature, 'isRainy': weatherInfo.current.isRainy, 'description': weatherInfo.current.description, }, 'nextHour': { 'temperature': weatherInfo.nextHour.temperature, 'isRainy': weatherInfo.nextHour.isRainy, 'description': weatherInfo.nextHour.description, }, }; } WeatherInfo _weatherInfoFromMap(Map map) { try { // current 필드 검증 final currentMap = map['current'] as Map?; if (currentMap == null) { throw FormatException('Missing current weather data'); } // nextHour 필드 검증 final nextHourMap = map['nextHour'] as Map?; if (nextHourMap == null) { throw FormatException('Missing nextHour weather data'); } // 필수 필드 검증 및 기본값 제공 final currentTemp = currentMap['temperature'] as num? ?? 20; final currentRainy = currentMap['isRainy'] as bool? ?? false; final currentDesc = currentMap['description'] as String? ?? '알 수 없음'; final nextTemp = nextHourMap['temperature'] as num? ?? 20; final nextRainy = nextHourMap['isRainy'] as bool? ?? false; final nextDesc = nextHourMap['description'] as String? ?? '알 수 없음'; return WeatherInfo( current: WeatherData( temperature: currentTemp.round(), isRainy: currentRainy, description: currentDesc, ), nextHour: WeatherData( temperature: nextTemp.round(), isRainy: nextRainy, description: nextDesc, ), ); } catch (e) { print('WeatherCache: Error converting map to WeatherInfo: $e'); print('WeatherCache: Map data: $map'); rethrow; } } }