import 'dart:convert'; import 'package:flutter/services.dart' show rootBundle; import '../models/country.dart'; /// Lädt und cacht die statische Notrufnummern-Datenbasis aus /// `assets/data/emergency_numbers.json`. /// /// Die Datei wird nur beim ersten [load]-Aufruf eingelesen und geparst; /// danach liefert [lookup] aus dem In-Memory-Cache. class EmergencyNumberRepository { EmergencyNumberRepository({this.assetPath = 'assets/data/emergency_numbers.json'}); final String assetPath; Map? _countriesByIsoCode; /// True, sobald [load] erfolgreich durchgelaufen ist. bool get isLoaded => _countriesByIsoCode != null; /// Liest und parst die JSON-Datei einmalig ein. /// /// Wird [load] mehrfach aufgerufen, geschieht nach dem ersten /// erfolgreichen Durchlauf nichts mehr (Cache bleibt bestehen). /// /// Wirft eine [FormatException], wenn das JSON fehlerhaft ist – das ist /// ein Programmierfehler (kaputtes Asset) und soll beim Start auffallen, /// nicht still verschluckt werden. Future load() async { if (isLoaded) return; final raw = await rootBundle.loadString(assetPath); final decoded = jsonDecode(raw); if (decoded is! Map) { throw const FormatException( 'emergency_numbers.json: erwartetes Top-Level-Objekt fehlt', ); } final parsed = {}; for (final entry in decoded.entries) { final isoCode = entry.key.toUpperCase(); parsed[isoCode] = Country.fromJson(isoCode, entry.value as Map); } _countriesByIsoCode = parsed; } /// Sucht ein Land anhand seines ISO 3166-1 alpha-2 Codes (case-insensitive). /// /// Gibt `null` zurück, wenn [load] noch nicht aufgerufen wurde oder das /// Land nicht in der Datenbasis vorhanden ist – bewusst keine Exception, /// damit Aufrufer den Fallback-Pfad (z. B. globaler 112/911-Button) ohne /// try/catch abbilden können. Country? lookup(String isoCode) { final countries = _countriesByIsoCode; if (countries == null) return null; return countries[isoCode.toUpperCase()]; } /// Alle Länder der Datenbasis, alphabetisch nach [Country.nameDe] sortiert /// (`docs/plan.md`, Abschnitt 8, AP6: Grundlage für den `CountryPickerScreen`). /// /// Liefert eine leere Liste, wenn [load] noch nicht (erfolgreich) /// durchgelaufen ist – bewusst kein Fehler, analog zu [lookup]. List get allCountries { final countries = _countriesByIsoCode; if (countries == null) return const []; final sorted = countries.values.toList()..sort((a, b) => a.nameDe.compareTo(b.nameDe)); return sorted; } }