import 'package:flutter/foundation.dart'; import '../models/country.dart'; import '../repositories/emergency_number_repository.dart'; import '../services/location_service.dart'; import 'emergency_status.dart'; /// Orchestriert Repository + Ländererkennung für die UI /// (`docs/plan.md`, Abschnitt 5 + 6 + 8, AP5). /// /// Kennt keinen `BuildContext`; die UI (`HomeScreen`) beobachtet [status]/ /// [country]/[errorMessage] über `ChangeNotifier` und ruft [init]/[retry] auf. /// /// **Entscheidung zu `EmergencyStatus.permissionDenied`:** `LocationService` /// liefert bewusst nur `String? isoCode` ohne Fehlergrund (AP4-Entscheidung, /// um die Ermittlungskette einfach zu halten). Eine zusätzliche, direkte /// Abfrage z. B. von `Geolocator.checkPermission()` hier im Provider würde /// eine plattformnahe Geolocator-Abhängigkeit in die State-Schicht ziehen, /// obwohl das reine Wissen um "GPS-Berechtigung verweigert" den eigentlichen /// Ermittlungs-Flow gar nicht beeinflusst (SIM/Cache können trotzdem einen /// Treffer liefern → dann ist der Status weiterhin `ready`, nicht /// `permissionDenied`). Das würde Zuständigkeiten vermischen, ohne den /// Nutzer:innen im Ladezustand einen Mehrwert zu bieten. Deshalb bleibt /// `permissionDenied` für AP5 unbenutzt; der Fall "GPS verweigert und keine /// andere Quelle liefert einen Code" wird korrekt (und ausreichend) über /// `countryUnknown` abgebildet. Eine gezieltere Unterscheidung kann bei /// Bedarf in einem späteren AP (Banner/UX-Feinschliff) ergänzt werden. class EmergencyProvider extends ChangeNotifier { // Bewusst keine initializing formals (`this._repository`): Die externen // Konstruktorparameter sollen lesbar `repository`/`locationService` // heißen, die Felder aber `_repository`/`_locationService` (privat). EmergencyProvider({required EmergencyNumberRepository repository, required LocationService locationService}) : _repository = repository, // ignore: prefer_initializing_formals _locationService = locationService; // ignore: prefer_initializing_formals final EmergencyNumberRepository _repository; final LocationService _locationService; EmergencyStatus _status = EmergencyStatus.initializing; Country? _country; String? _errorMessage; bool _disposed = false; EmergencyStatus get status => _status; Country? get country => _country; String? get errorMessage => _errorMessage; /// Exponiert das Repository für die UI (`docs/plan.md`, Abschnitt 8, AP6): /// Der `CountryPickerScreen` braucht Zugriff auf die bereits geladene /// Länderliste (`EmergencyNumberRepository.allCountries`), ohne eine /// zweite, unabhängige Repository-Instanz zu erzeugen oder das Asset /// erneut zu laden (`load()` ist idempotent, aber unnötig). EmergencyNumberRepository get repository => _repository; @override void dispose() { _disposed = true; super.dispose(); } /// Wie [notifyListeners], aber ein no-op, wenn der Provider inzwischen /// disposed wurde – kann passieren, wenn [init]/[retry] noch auf eine /// echte Asset-/Plugin-I/O wartet (z. B. asset load, GPS), während die /// UI (z. B. `HomeScreen`) längst nicht mehr im Widgetbaum ist. void _notifyIfActive() { if (!_disposed) notifyListeners(); } /// Startet die Ermittlungskette: Datenbasis laden → Ländercode ermitteln /// → Land nachschlagen. Wirft nie; Fehler landen im `error`-Status. Future init() async { _status = EmergencyStatus.initializing; _country = null; _errorMessage = null; _notifyIfActive(); try { await _repository.load(); } catch (e) { _status = EmergencyStatus.error; _errorMessage = e.toString(); _notifyIfActive(); return; } _status = EmergencyStatus.locating; _notifyIfActive(); final isoCode = await _locationService.resolveCountryCode(); if (isoCode == null) { _status = EmergencyStatus.countryUnknown; _country = null; _notifyIfActive(); return; } // country bleibt `null`, wenn der ermittelte isoCode nicht in der // Datenbasis vorhanden ist (Plan Abschnitt 7) – Status ist dennoch // `ready`, die UI fällt dann auf den globalen Panik-Button zurück. _country = _repository.lookup(isoCode); _status = EmergencyStatus.ready; _notifyIfActive(); } /// Wiederholt die komplette Ermittlungskette (kein separater Zustand nötig). Future retry() => init(); /// Setzt die manuelle Länderauswahl des Nutzers (`docs/plan.md`, /// Abschnitt 8, AP6: `CountryPickerScreen`) und stößt die /// Ermittlungskette danach sofort erneut an, damit [isoCode] unmittelbar /// wirksam wird (die manuelle Auswahl hat in [LocationService] höchste /// Priorität vor SIM/GPS/Cache). Future selectCountryManually(String isoCode) async { await _locationService.setManualCountryCode(isoCode); await init(); } /// Löscht die manuelle Länderauswahl wieder und ermittelt das Land danach /// erneut automatisch (SIM → GPS → Cache) – erlaubt dem Nutzer, zur /// automatischen Erkennung zurückzukehren (`docs/plan.md`, Abschnitt 8, AP6). Future clearManualSelection() async { await _locationService.setManualCountryCode(null); await init(); } }