import 'emisora.dart'; enum TipoProgramacionAlarma { unica, diaria, diasSemana } enum SonidoInternoAlarma { amanecer, campanaSuave, pulsoDigital } class AlarmaMusical { const AlarmaMusical({ required this.id, required this.nombre, required this.hora, required this.minuto, required this.tipoProgramacion, required this.diasSemana, this.fechaUnica, this.emisora, this.emisoraFallback, this.activa = true, this.sonarEnVacaciones = true, this.snoozeMinutos = 5, this.volumen = 0.85, this.sonidoInterno = SonidoInternoAlarma.amanecer, this.proximaEjecucion, this.creadaEn, this.actualizadaEn, }); final String id; final String nombre; final bool activa; final int hora; final int minuto; final TipoProgramacionAlarma tipoProgramacion; final List diasSemana; final DateTime? fechaUnica; final Emisora? emisora; final Emisora? emisoraFallback; final bool sonarEnVacaciones; final int snoozeMinutos; final double volumen; final SonidoInternoAlarma sonidoInterno; final DateTime? proximaEjecucion; final DateTime? creadaEn; final DateTime? actualizadaEn; AlarmaMusical copyWith({ String? id, String? nombre, bool? activa, int? hora, int? minuto, TipoProgramacionAlarma? tipoProgramacion, List? diasSemana, DateTime? fechaUnica, bool limpiarFechaUnica = false, Emisora? emisora, Emisora? emisoraFallback, bool? sonarEnVacaciones, int? snoozeMinutos, double? volumen, SonidoInternoAlarma? sonidoInterno, DateTime? proximaEjecucion, DateTime? creadaEn, DateTime? actualizadaEn, }) { return AlarmaMusical( id: id ?? this.id, nombre: nombre ?? this.nombre, activa: activa ?? this.activa, hora: hora ?? this.hora, minuto: minuto ?? this.minuto, tipoProgramacion: tipoProgramacion ?? this.tipoProgramacion, diasSemana: diasSemana ?? this.diasSemana, fechaUnica: limpiarFechaUnica ? null : fechaUnica ?? this.fechaUnica, emisora: emisora ?? this.emisora, emisoraFallback: emisoraFallback ?? this.emisoraFallback, sonarEnVacaciones: sonarEnVacaciones ?? this.sonarEnVacaciones, snoozeMinutos: snoozeMinutos ?? this.snoozeMinutos, volumen: volumen ?? this.volumen, sonidoInterno: sonidoInterno ?? this.sonidoInterno, proximaEjecucion: proximaEjecucion ?? this.proximaEjecucion, creadaEn: creadaEn ?? this.creadaEn, actualizadaEn: actualizadaEn ?? this.actualizadaEn, ); } Map toJson() => { 'id': id, 'nombre': nombre, 'activa': activa, 'hora': hora, 'minuto': minuto, 'tipoProgramacion': tipoProgramacion.name, 'diasSemana': diasSemana, 'fechaUnica': fechaUnica?.toIso8601String(), 'emisora': emisora?.toMap(), 'emisoraFallback': emisoraFallback?.toMap(), 'sonarEnVacaciones': sonarEnVacaciones, 'snoozeMinutos': snoozeMinutos, 'volumen': volumen, 'sonidoInterno': sonidoInterno.name, 'proximaEjecucion': proximaEjecucion?.toIso8601String(), 'creadaEn': creadaEn?.toIso8601String(), 'actualizadaEn': actualizadaEn?.toIso8601String(), }; factory AlarmaMusical.fromJson(Map json) { return AlarmaMusical( id: json['id'] as String, nombre: json['nombre'] as String? ?? 'Alarma musical', activa: json['activa'] as bool? ?? true, hora: json['hora'] as int? ?? 7, minuto: json['minuto'] as int? ?? 0, tipoProgramacion: _enumFromName( TipoProgramacionAlarma.values, json['tipoProgramacion'] as String?, TipoProgramacionAlarma.unica, ), diasSemana: (json['diasSemana'] as List? ?? const []) .whereType() .where((d) => d >= DateTime.monday && d <= DateTime.sunday) .toList(), fechaUnica: _dateFromJson(json['fechaUnica']), emisora: _emisoraFromJson(json['emisora']), emisoraFallback: _emisoraFromJson(json['emisoraFallback']), sonarEnVacaciones: json['sonarEnVacaciones'] as bool? ?? true, snoozeMinutos: json['snoozeMinutos'] as int? ?? 5, volumen: (json['volumen'] as num?)?.toDouble() ?? 0.85, sonidoInterno: _enumFromName( SonidoInternoAlarma.values, json['sonidoInterno'] as String?, SonidoInternoAlarma.amanecer, ), proximaEjecucion: _dateFromJson(json['proximaEjecucion']), creadaEn: _dateFromJson(json['creadaEn']), actualizadaEn: _dateFromJson(json['actualizadaEn']), ); } static Emisora? _emisoraFromJson(Object? raw) { if (raw is! Map) return null; return Emisora.fromMap(Map.from(raw)); } static DateTime? _dateFromJson(Object? raw) => raw is String ? DateTime.tryParse(raw) : null; static T _enumFromName( List values, String? name, T fallback, ) { for (final value in values) { if (value.name == name) return value; } return fallback; } } class RangoVacaciones { const RangoVacaciones({ required this.id, required this.nombre, required this.inicio, required this.fin, this.activo = true, }); final String id; final String nombre; final DateTime inicio; final DateTime fin; final bool activo; DateTime get inicioDia => DateTime(inicio.year, inicio.month, inicio.day); DateTime get finDia => DateTime(fin.year, fin.month, fin.day); bool contiene(DateTime fecha) { final dia = DateTime(fecha.year, fecha.month, fecha.day); final desde = inicioDia; final hasta = finDia; return activo && !dia.isBefore(desde) && !dia.isAfter(hasta); } RangoVacaciones normalizado() { if (!finDia.isBefore(inicioDia)) return this; return RangoVacaciones( id: id, nombre: nombre, inicio: finDia, fin: inicioDia, activo: activo, ); } Map toJson() => { 'id': id, 'nombre': nombre, 'inicio': inicio.toIso8601String(), 'fin': fin.toIso8601String(), 'activo': activo, }; factory RangoVacaciones.fromJson(Map json) { return RangoVacaciones( id: json['id'] as String, nombre: json['nombre'] as String? ?? 'Vacaciones', inicio: DateTime.parse(json['inicio'] as String), fin: DateTime.parse(json['fin'] as String), activo: json['activo'] as bool? ?? true, ); } } class ExcepcionAlarma { const ExcepcionAlarma({ required this.alarmaId, required this.ejecucion, required this.tipo, }); final String alarmaId; final DateTime ejecucion; final String tipo; Map toJson() => { 'alarmaId': alarmaId, 'ejecucion': ejecucion.toIso8601String(), 'tipo': tipo, }; factory ExcepcionAlarma.fromJson(Map json) { return ExcepcionAlarma( alarmaId: json['alarmaId'] as String, ejecucion: DateTime.parse(json['ejecucion'] as String), tipo: json['tipo'] as String? ?? 'skipNext', ); } }