import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; import '../modelos/alarma_musical.dart'; import '../modelos/emisora.dart'; import 'servicio_programacion_alarmas.dart'; class ConfiguracionAlarmas { const ConfiguracionAlarmas({ required this.alarmas, required this.vacaciones, required this.excepciones, }); final List alarmas; final List vacaciones; final List excepciones; } class ServicioAlarmas { ServicioAlarmas({ ServicioProgramacionAlarmas? programacion, SharedPreferences? prefs, DateTime Function()? reloj, }) : _programacion = programacion ?? ServicioProgramacionAlarmas(), _prefs = prefs, _reloj = reloj ?? DateTime.now; static const _keyConfig = 'alarmas_musicales_v1'; final ServicioProgramacionAlarmas _programacion; final SharedPreferences? _prefs; final DateTime Function() _reloj; final _uuid = const Uuid(); Future cargar() async { final prefs = await _resolverPrefs(); final raw = prefs.getString(_keyConfig); if (raw == null || raw.trim().isEmpty) { return const ConfiguracionAlarmas( alarmas: [], vacaciones: [], excepciones: [], ); } try { final data = jsonDecode(raw) as Map; return ConfiguracionAlarmas( alarmas: (data['alarmas'] as List? ?? const []) .whereType() .map( (e) => AlarmaMusical.fromJson(Map.from(e)), ) .toList(), vacaciones: (data['vacaciones'] as List? ?? const []) .whereType() .map( (e) => RangoVacaciones.fromJson(Map.from(e)), ) .toList(), excepciones: (data['excepciones'] as List? ?? const []) .whereType() .map( (e) => ExcepcionAlarma.fromJson(Map.from(e)), ) .toList(), ); } catch (_) { return const ConfiguracionAlarmas( alarmas: [], vacaciones: [], excepciones: [], ); } } Future guardarAlarma( AlarmaMusical alarma, { List? vacaciones, List? excepciones, }) async { final config = await cargar(); final ahora = _reloj(); final alarmas = List.from(config.alarmas); final index = alarmas.indexWhere((a) => a.id == alarma.id); final normalizada = _recalcular( alarma.copyWith(creadaEn: alarma.creadaEn ?? ahora, actualizadaEn: ahora), vacaciones ?? config.vacaciones, excepciones ?? config.excepciones, ); if (index >= 0) { alarmas[index] = normalizada; } else { alarmas.add(normalizada); } final nuevo = ConfiguracionAlarmas( alarmas: alarmas, vacaciones: vacaciones ?? config.vacaciones, excepciones: excepciones ?? config.excepciones, ); await _guardar(nuevo); return nuevo; } Future eliminarAlarma(String id) async { final config = await cargar(); final nuevo = ConfiguracionAlarmas( alarmas: config.alarmas.where((a) => a.id != id).toList(), vacaciones: config.vacaciones, excepciones: config.excepciones.where((e) => e.alarmaId != id).toList(), ); await _guardar(nuevo); return nuevo; } Future guardarVacaciones( List vacaciones, ) async { final config = await cargar(); final normalizadas = vacaciones .map((v) => v.normalizado()) .toList() ..sort((a, b) => a.inicioDia.compareTo(b.inicioDia)); final alarmas = config.alarmas .map((a) => _recalcular(a, normalizadas, config.excepciones)) .toList(); final nuevo = ConfiguracionAlarmas( alarmas: alarmas, vacaciones: normalizadas, excepciones: config.excepciones, ); await _guardar(nuevo); return nuevo; } RangoVacaciones crearRangoVacaciones({ required DateTime inicio, required DateTime fin, String? nombre, }) { final rango = RangoVacaciones( id: _uuid.v4(), nombre: (nombre == null || nombre.trim().isEmpty) ? 'Vacaciones' : nombre.trim(), inicio: inicio, fin: fin, ); return rango.normalizado(); } Future recalcularTodas() async { final config = await cargar(); final alarmas = config.alarmas .map((a) => _recalcular(a, config.vacaciones, config.excepciones)) .toList(); final nuevo = ConfiguracionAlarmas( alarmas: alarmas, vacaciones: config.vacaciones, excepciones: config.excepciones, ); await _guardar(nuevo); return nuevo; } Future saltarProxima(String alarmaId) async { final config = await cargar(); final alarma = config.alarmas.firstWhere((a) => a.id == alarmaId); final proxima = alarma.proximaEjecucion; if (proxima == null) return config; final excepciones = [ ...config.excepciones, ExcepcionAlarma(alarmaId: alarmaId, ejecucion: proxima, tipo: 'skipNext'), ]; final alarmas = config.alarmas .map( (a) => a.id == alarmaId ? _recalcular(a, config.vacaciones, excepciones) : a, ) .toList(); final nuevo = ConfiguracionAlarmas( alarmas: alarmas, vacaciones: config.vacaciones, excepciones: excepciones, ); await _guardar(nuevo); return nuevo; } AlarmaMusical crearAlarma({ required String nombre, required int hora, required int minuto, required TipoProgramacionAlarma tipoProgramacion, required List diasSemana, DateTime? fechaUnica, Emisora? emisora, Emisora? emisoraFallback, bool sonarEnVacaciones = true, int snoozeMinutos = 5, double volumen = 0.85, SonidoInternoAlarma sonidoInterno = SonidoInternoAlarma.amanecer, }) { final ahora = _reloj(); return AlarmaMusical( id: _uuid.v4(), nombre: nombre, hora: hora, minuto: minuto, tipoProgramacion: tipoProgramacion, diasSemana: diasSemana, fechaUnica: fechaUnica, emisora: emisora, emisoraFallback: emisoraFallback, sonarEnVacaciones: sonarEnVacaciones, snoozeMinutos: snoozeMinutos, volumen: volumen, sonidoInterno: sonidoInterno, creadaEn: ahora, actualizadaEn: ahora, ); } Future _guardar(ConfiguracionAlarmas config) async { final prefs = await _resolverPrefs(); await prefs.setString( _keyConfig, jsonEncode({ 'alarmas': config.alarmas.map((a) => a.toJson()).toList(), 'vacaciones': config.vacaciones.map((v) => v.toJson()).toList(), 'excepciones': config.excepciones.map((e) => e.toJson()).toList(), }), ); } AlarmaMusical _recalcular( AlarmaMusical alarma, List vacaciones, List excepciones, ) { final proxima = _programacion.calcularProxima( alarma: alarma, desde: _reloj(), vacaciones: vacaciones, excepciones: excepciones, ); return alarma.copyWith( proximaEjecucion: proxima, limpiarProximaEjecucion: true, ); } Future _resolverPrefs() async => _prefs ?? SharedPreferences.getInstance(); }