import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:uuid/uuid.dart'; import '../models/subscription_model.dart'; import '../services/notification_service.dart'; import 'package:provider/provider.dart'; import 'notification_provider.dart'; import '../navigator_key.dart'; class SubscriptionProvider extends ChangeNotifier { late Box _subscriptionBox; List _subscriptions = []; bool _isLoading = true; List get subscriptions => _subscriptions; bool get isLoading => _isLoading; double get totalMonthlyExpense { return _subscriptions.fold( 0.0, (sum, subscription) => sum + subscription.currentPrice, // 이벤트 가격 반영 ); } /// 월간 총 비용을 반환합니다. double getTotalMonthlyCost() { return totalMonthlyExpense; } /// 이벤트로 인한 총 절약액을 반환합니다. double get totalEventSavings { return _subscriptions.fold( 0.0, (sum, subscription) => sum + subscription.eventSavings, ); } /// 현재 이벤트 중인 구독 목록을 반환합니다. List get activeEventSubscriptions { return _subscriptions.where((sub) => sub.isCurrentlyInEvent).toList(); } Future init() async { try { _isLoading = true; notifyListeners(); _subscriptionBox = await Hive.openBox('subscriptions'); await refreshSubscriptions(); // 앱 시작 시 이벤트 상태 확인 await checkAndUpdateEventStatus(); _isLoading = false; notifyListeners(); } catch (e) { debugPrint('구독 초기화 중 오류 발생: $e'); _isLoading = false; notifyListeners(); rethrow; } } Future refreshSubscriptions() async { try { _subscriptions = _subscriptionBox.values.toList() ..sort((a, b) => a.nextBillingDate.compareTo(b.nextBillingDate)); notifyListeners(); } catch (e) { debugPrint('구독 목록 새로고침 중 오류 발생: $e'); rethrow; } } Future addSubscription({ required String serviceName, required double monthlyCost, required String billingCycle, required DateTime nextBillingDate, String? websiteUrl, String? categoryId, bool isAutoDetected = false, int repeatCount = 1, DateTime? lastPaymentDate, String currency = 'KRW', bool isEventActive = false, DateTime? eventStartDate, DateTime? eventEndDate, double? eventPrice, }) async { try { final subscription = SubscriptionModel( id: const Uuid().v4(), serviceName: serviceName, monthlyCost: monthlyCost, billingCycle: billingCycle, nextBillingDate: nextBillingDate, websiteUrl: websiteUrl, categoryId: categoryId, isAutoDetected: isAutoDetected, repeatCount: repeatCount, lastPaymentDate: lastPaymentDate, currency: currency, isEventActive: isEventActive, eventStartDate: eventStartDate, eventEndDate: eventEndDate, eventPrice: eventPrice, ); await _subscriptionBox.put(subscription.id, subscription); await refreshSubscriptions(); // 이벤트가 활성화된 경우 알림 스케줄 재설정 if (isEventActive && eventEndDate != null) { await _scheduleEventEndNotification(subscription); } } catch (e) { debugPrint('구독 추가 중 오류 발생: $e'); rethrow; } } Future updateSubscription(SubscriptionModel subscription) async { try { notifyListeners(); await _subscriptionBox.put(subscription.id, subscription); // 이벤트 관련 알림 업데이트 if (subscription.isEventActive && subscription.eventEndDate != null) { await _scheduleEventEndNotification(subscription); } else { // 이벤트가 비활성화된 경우 이벤트 종료 알림 취소 await NotificationService.cancelNotification( '${subscription.id}_event_end'.hashCode, ); } await refreshSubscriptions(); notifyListeners(); } catch (e) { debugPrint('구독 업데이트 중 오류 발생: $e'); rethrow; } } Future deleteSubscription(String id) async { try { await _subscriptionBox.delete(id); await refreshSubscriptions(); } catch (e) { debugPrint('구독 삭제 중 오류 발생: $e'); rethrow; } } Future _scheduleNotifications() async { final BuildContext? context = navigatorKey.currentContext; if (context == null) return; final notificationProvider = Provider.of( context, listen: false, ); if (!notificationProvider.isEnabled || !notificationProvider.isPaymentEnabled) { return; } for (final subscription in _subscriptions) { final notificationDate = subscription.nextBillingDate.subtract( const Duration(days: 3), ); if (notificationDate.isAfter(DateTime.now())) { await NotificationService.scheduleNotification( id: subscription.id.hashCode, title: '구독 결제 예정 알림', body: '${subscription.serviceName}의 결제가 3일 후 예정되어 있습니다.', scheduledDate: notificationDate, ); } } } Future clearAllSubscriptions() async { _isLoading = true; notifyListeners(); try { // 모든 알림 취소 for (var subscription in _subscriptions) { await NotificationService.cancelSubscriptionNotification(subscription); } // 모든 데이터 삭제 await _subscriptionBox.clear(); _subscriptions = []; notifyListeners(); } catch (e) { debugPrint('모든 구독 정보 삭제 중 오류 발생: $e'); rethrow; } finally { _isLoading = false; notifyListeners(); } } /// 이벤트 종료 알림을 스케줄링합니다. Future _scheduleEventEndNotification(SubscriptionModel subscription) async { if (subscription.eventEndDate != null && subscription.eventEndDate!.isAfter(DateTime.now())) { await NotificationService.scheduleNotification( id: '${subscription.id}_event_end'.hashCode, title: '이벤트 종료 알림', body: '${subscription.serviceName}의 할인 이벤트가 종료되었습니다.', scheduledDate: subscription.eventEndDate!, ); } } /// 모든 구독의 이벤트 상태를 확인하고 업데이트합니다. Future checkAndUpdateEventStatus() async { bool hasChanges = false; for (var subscription in _subscriptions) { // 이벤트가 종료되었지만 아직 활성화되어 있는 경우 if (subscription.isEventActive && subscription.eventEndDate != null && subscription.eventEndDate!.isBefore(DateTime.now())) { subscription.isEventActive = false; await _subscriptionBox.put(subscription.id, subscription); hasChanges = true; } } if (hasChanges) { await refreshSubscriptions(); } } }