import 'package:flutter/material.dart'; import '../services/sms_scanner.dart'; import '../models/subscription.dart'; import '../services/sms_scan/subscription_converter.dart'; import '../services/sms_scan/subscription_filter.dart'; import '../providers/subscription_provider.dart'; import 'package:provider/provider.dart'; import '../utils/logger.dart'; import '../providers/navigation_provider.dart'; import '../providers/category_provider.dart'; import '../l10n/app_localizations.dart'; class SmsScanController extends ChangeNotifier { // 상태 관리 bool _isLoading = false; bool get isLoading => _isLoading; String? _errorMessage; String? get errorMessage => _errorMessage; List _scannedSubscriptions = []; List get scannedSubscriptions => _scannedSubscriptions; int _currentIndex = 0; int get currentIndex => _currentIndex; String? _selectedCategoryId; String? get selectedCategoryId => _selectedCategoryId; final TextEditingController websiteUrlController = TextEditingController(); // 의존성 final SmsScanner _smsScanner = SmsScanner(); final SubscriptionConverter _converter = SubscriptionConverter(); final SubscriptionFilter _filter = SubscriptionFilter(); @override void dispose() { websiteUrlController.dispose(); super.dispose(); } void setSelectedCategoryId(String? categoryId) { _selectedCategoryId = categoryId; notifyListeners(); } void resetWebsiteUrl() { websiteUrlController.text = ''; } Future scanSms(BuildContext context) async { _isLoading = true; _errorMessage = null; _scannedSubscriptions = []; _currentIndex = 0; notifyListeners(); try { // SMS 스캔 실행 Log.i('SMS 스캔 시작'); final scannedSubscriptionModels = await _smsScanner.scanForSubscriptions(); Log.d('스캔된 구독: ${scannedSubscriptionModels.length}개'); if (scannedSubscriptionModels.isNotEmpty) { Log.d( '첫 번째 구독: ${scannedSubscriptionModels[0].serviceName}, 반복 횟수: ${scannedSubscriptionModels[0].repeatCount}'); } if (!context.mounted) return; if (scannedSubscriptionModels.isEmpty) { Log.i('스캔된 구독이 없음'); _errorMessage = AppLocalizations.of(context).subscriptionNotFound; _isLoading = false; notifyListeners(); return; } // SubscriptionModel을 Subscription으로 변환 final scannedSubscriptions = _converter.convertModelsToSubscriptions(scannedSubscriptionModels); // 2회 이상 반복 결제된 구독만 필터링 final repeatSubscriptions = _filter.filterByRepeatCount(scannedSubscriptions, 2); Log.d('반복 결제된 구독: ${repeatSubscriptions.length}개'); if (repeatSubscriptions.isNotEmpty) { Log.d( '첫 번째 반복 구독: ${repeatSubscriptions[0].serviceName}, 반복 횟수: ${repeatSubscriptions[0].repeatCount}'); } if (repeatSubscriptions.isEmpty) { Log.i('반복 결제된 구독이 없음'); _errorMessage = AppLocalizations.of(context).repeatSubscriptionNotFound; _isLoading = false; notifyListeners(); return; } // 구독 목록 가져오기 final provider = Provider.of(context, listen: false); final existingSubscriptions = provider.subscriptions; Log.d('기존 구독: ${existingSubscriptions.length}개'); // 중복 구독 필터링 final filteredSubscriptions = _filter.filterDuplicates(repeatSubscriptions, existingSubscriptions); Log.d('중복 제거 후 구독: ${filteredSubscriptions.length}개'); if (filteredSubscriptions.isNotEmpty) { Log.d( '첫 번째 필터링된 구독: ${filteredSubscriptions[0].serviceName}, 반복 횟수: ${filteredSubscriptions[0].repeatCount}'); } // 중복 제거 후 신규 구독이 없는 경우 if (filteredSubscriptions.isEmpty) { Log.i('중복 제거 후 신규 구독이 없음'); _isLoading = false; notifyListeners(); return; } _scannedSubscriptions = filteredSubscriptions; _isLoading = false; websiteUrlController.text = ''; // URL 입력 필드 초기화 notifyListeners(); } catch (e) { Log.e('SMS 스캔 중 오류 발생', e); if (context.mounted) { _errorMessage = AppLocalizations.of(context).smsScanErrorWithMessage(e.toString()); _isLoading = false; notifyListeners(); } } } Future addCurrentSubscription(BuildContext context) async { if (_currentIndex >= _scannedSubscriptions.length) return; final subscription = _scannedSubscriptions[_currentIndex]; try { final provider = Provider.of(context, listen: false); final categoryProvider = Provider.of(context, listen: false); final finalCategoryId = _selectedCategoryId ?? subscription.category ?? getDefaultCategoryId(categoryProvider); // websiteUrl 처리 final websiteUrl = websiteUrlController.text.trim().isNotEmpty ? websiteUrlController.text.trim() : subscription.websiteUrl; Log.d( '구독 추가 시도: ${subscription.serviceName}, 카테고리: $finalCategoryId, URL: $websiteUrl'); // addSubscription 호출 await provider.addSubscription( serviceName: subscription.serviceName, monthlyCost: subscription.monthlyCost, billingCycle: subscription.billingCycle, nextBillingDate: subscription.nextBillingDate, websiteUrl: websiteUrl, isAutoDetected: true, repeatCount: subscription.repeatCount, lastPaymentDate: subscription.lastPaymentDate, categoryId: finalCategoryId, currency: subscription.currency, ); Log.i('구독 추가 성공: ${subscription.serviceName}'); if (!context.mounted) return; moveToNextSubscription(context); } catch (e) { Log.e('구독 추가 중 오류 발생', e); // 오류가 있어도 다음 구독으로 이동 if (!context.mounted) return; moveToNextSubscription(context); } } void skipCurrentSubscription(BuildContext context) { final subscription = _scannedSubscriptions[_currentIndex]; Log.i('구독 건너뛰기: ${subscription.serviceName}'); moveToNextSubscription(context); } void moveToNextSubscription(BuildContext context) { _currentIndex++; websiteUrlController.text = ''; // URL 입력 필드 초기화 _selectedCategoryId = null; // 카테고리 선택 초기화 // 모든 구독을 처리했으면 홈 화면으로 이동 if (_currentIndex >= _scannedSubscriptions.length) { navigateToHome(context); } notifyListeners(); } void navigateToHome(BuildContext context) { // NavigationProvider를 사용하여 홈 화면으로 이동 final navigationProvider = Provider.of(context, listen: false); navigationProvider.updateCurrentIndex(0); } void resetState() { _scannedSubscriptions = []; _currentIndex = 0; _errorMessage = null; notifyListeners(); } String getDefaultCategoryId(CategoryProvider categoryProvider) { final otherCategory = categoryProvider.categories.firstWhere( (cat) => cat.name == 'other', orElse: () => categoryProvider.categories.first, ); Log.d('기본 카테고리 설정: ${otherCategory.name} (ID: ${otherCategory.id})'); return otherCategory.id; } void initializeWebsiteUrl() { if (_currentIndex < _scannedSubscriptions.length) { final currentSub = _scannedSubscriptions[_currentIndex]; if (websiteUrlController.text.isEmpty && currentSub.websiteUrl != null) { websiteUrlController.text = currentSub.websiteUrl!; } } } }