import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:injectable/injectable.dart'; import 'package:superport/data/models/zipcode_dto.dart'; import 'package:superport/domain/usecases/zipcode_usecase.dart'; import 'package:superport/utils/constants.dart'; @injectable class ZipcodeController extends ChangeNotifier { final ZipcodeUseCase _zipcodeUseCase; ZipcodeController(this._zipcodeUseCase); // 상태 변수들 List _zipcodes = []; ZipcodeDto? _selectedZipcode; bool _isLoading = false; String? _errorMessage; // 페이지네이션 int _currentPage = 1; int _totalPages = 1; int _totalCount = 0; final int _pageSize = PaginationConstants.defaultPageSize; // 검색 및 필터 String _searchQuery = ''; String? _selectedSido; String? _selectedGu; // 시도/구 목록 캐시 List _sidoList = []; List _guList = []; // 디바운스를 위한 타이머 Timer? _debounceTimer; // Getters List get zipcodes => _zipcodes; ZipcodeDto? get selectedZipcode => _selectedZipcode; bool get isLoading => _isLoading; String? get errorMessage => _errorMessage; int get currentPage => _currentPage; int get totalPages => _totalPages; int get totalCount => _totalCount; String get searchQuery => _searchQuery; String? get selectedSido => _selectedSido; String? get selectedGu => _selectedGu; List get sidoList => _sidoList; List get guList => _guList; bool get hasNextPage => _currentPage < _totalPages; bool get hasPreviousPage => _currentPage > 1; // 초기 데이터 로드 Future initialize() async { _isLoading = true; notifyListeners(); // 시도 목록 로드 await _loadSidoList(); // 초기 우편번호 목록 로드 (첫 페이지) await searchZipcodes(); } // 우편번호 검색 Future searchZipcodes({bool refresh = false}) async { if (refresh) { _currentPage = 1; } _setLoading(true); _clearError(); try { final response = await _zipcodeUseCase.searchZipcodes( page: _currentPage, limit: _pageSize, search: _searchQuery.isNotEmpty ? _searchQuery : null, sido: _selectedSido, gu: _selectedGu, ); _zipcodes = response.items; _totalCount = response.totalCount; _totalPages = response.totalPages; _currentPage = response.currentPage; notifyListeners(); } catch (e) { _setError('우편번호 검색에 실패했습니다: ${e.toString()}'); } finally { _setLoading(false); } } // 특정 우편번호로 정확한 주소 조회 Future getZipcodeByNumber(int zipcode) async { _setLoading(true); _clearError(); try { _selectedZipcode = await _zipcodeUseCase.getZipcodeByNumber(zipcode); notifyListeners(); } catch (e) { _setError('우편번호 조회에 실패했습니다: ${e.toString()}'); } finally { _setLoading(false); } } // 주소로 우편번호 빠른 검색 Future> quickSearchByAddress(String address) async { if (address.trim().isEmpty) return []; try { return await _zipcodeUseCase.searchByAddress(address); } catch (e) { return []; } } // 검색어 설정 (디바운스 적용) void setSearchQuery(String query) { _searchQuery = query; notifyListeners(); // 디바운스 처리 (500ms 대기 후 검색 실행) _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 500), () { searchZipcodes(refresh: true); }); } // 즉시 검색 실행 (디바운스 무시) Future executeSearch() async { _debounceTimer?.cancel(); _currentPage = 1; await searchZipcodes(); } // 시도 선택 Future setSido(String? sido) async { _selectedSido = sido; _selectedGu = null; // 시도 변경 시 구 초기화 _guList = []; // 구 목록 초기화 notifyListeners(); // 선택된 시도에 따른 구 목록 로드 if (sido != null) { await _loadGuListBySido(sido); } // 검색 새로고침 await searchZipcodes(refresh: true); } // 구 선택 Future setGu(String? gu) async { _selectedGu = gu; notifyListeners(); // 검색 새로고침 await searchZipcodes(refresh: true); } // 필터 초기화 Future clearFilters() async { _searchQuery = ''; _selectedSido = null; _selectedGu = null; _guList = []; _currentPage = 1; notifyListeners(); await searchZipcodes(); } // 페이지 이동 Future goToPage(int page) async { if (page < 1 || page > _totalPages) return; _currentPage = page; await searchZipcodes(); } // 다음 페이지 Future nextPage() async { if (hasNextPage) { await goToPage(_currentPage + 1); } } // 이전 페이지 Future previousPage() async { if (hasPreviousPage) { await goToPage(_currentPage - 1); } } // 시도 목록 로드 (캐시) Future _loadSidoList() async { try { _sidoList = await _zipcodeUseCase.getAllSidoList(); } catch (e) { debugPrint('시도 목록 로드 실패: $e'); _sidoList = []; } } // 선택된 시도의 구 목록 로드 Future _loadGuListBySido(String sido) async { try { _guList = await _zipcodeUseCase.getGuListBySido(sido); notifyListeners(); } catch (e) { debugPrint('구 목록 로드 실패: $e'); _guList = []; } } // 우편번호 선택 void selectZipcode(ZipcodeDto zipcode) { _selectedZipcode = zipcode; notifyListeners(); } // 선택 초기화 void clearSelection() { _selectedZipcode = null; notifyListeners(); } // 내부 헬퍼 메서드 void _setLoading(bool loading) { _isLoading = loading; notifyListeners(); } void _setError(String message) { _errorMessage = message; notifyListeners(); } void _clearError() { _errorMessage = null; } @override void dispose() { _debounceTimer?.cancel(); _zipcodes = []; _selectedZipcode = null; super.dispose(); } }