import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import '../modelos/preset_ecualizador.dart'; import 'persistencia_tolerante.dart'; /// Persistence for USER-NAMED custom EQ presets (design ADR-5 hazard box, /// `eq-custom-presets` spec — "Custom Preset Save"). /// /// Deliberately its OWN file and OWN SharedPreferences key /// (`eq_custom_presets_v1`), never folded into [ServicioEcualizador]: that /// file has an empty-`git diff` success criterion for this change, so /// nothing custom-preset-related may be added there. A custom preset is /// just a [PresetEcualizador] with a user-supplied `nombre` — the model /// itself needs no change (`toJson`/`desdeJson` already exist). class ServicioPresetsPersonalizados { ServicioPresetsPersonalizados({SharedPreferences? prefs}) : _prefs = prefs; static const _keyPresetsPersonalizados = 'eq_custom_presets_v1'; final SharedPreferences? _prefs; Future _resolverPrefs() async => _prefs ?? SharedPreferences.getInstance(); /// Returns every saved custom preset, in save order. Future> listar() async { final prefs = await _resolverPrefs(); return _leer(prefs); } /// Saves [preset] under its own name. A preset already saved under the /// same name is replaced (last write wins) rather than duplicated. Future guardar(PresetEcualizador preset) async { final prefs = await _resolverPrefs(); final actuales = _leer(prefs) ..removeWhere((p) => p.nombre == preset.nombre) ..add(preset); await _guardarTodos(prefs, actuales); } /// Removes the custom preset named [nombre], if present. A safe no-op /// when no preset with that name exists. Future eliminar(String nombre) async { final prefs = await _resolverPrefs(); final actuales = _leer(prefs)..removeWhere((p) => p.nombre == nombre); await _guardarTodos(prefs, actuales); } /// Reads the persisted list with per-entry tolerance /// (persistence-resilience D1/D6, same shared helper `ServicioEcualizador` /// uses): an entry that fails to parse is skipped and logged, its /// siblings survive. A top-level decode failure degrades to an empty /// list (also logged) — custom presets are explicit-only user writes and /// trivially re-creatable, so there is no flag/quarantine here, matching /// `ServicioEcualizador`'s own documented D6 asymmetry vs. Alarms/Stations. List _leer(SharedPreferences prefs) { final raw = prefs.getString(_keyPresetsPersonalizados); if (raw == null || raw.isEmpty) return []; try { final data = jsonDecode(raw) as List; final resultado = parseListaTolerante( data, (entrada) => PresetEcualizador.desdeJson(entrada), subsistema: 'presets_personalizados', coleccion: _keyPresetsPersonalizados, ); return resultado.validas; } catch (e) { registrarSaltoPersistencia( subsistema: 'presets_personalizados', detalle: _keyPresetsPersonalizados, razon: e.toString(), ); return []; } } Future _guardarTodos( SharedPreferences prefs, List presets, ) async { final serializado = presets.map((p) => p.toJson()).toList(); await prefs.setString(_keyPresetsPersonalizados, jsonEncode(serializado)); } }