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<int> 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<int>? 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<String, dynamic> 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<String, dynamic> 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<int>()
              .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<String, dynamic>.from(raw));
  }

  static DateTime? _dateFromJson(Object? raw) =>
      raw is String ? DateTime.tryParse(raw) : null;

  static T _enumFromName<T extends Enum>(
    List<T> 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<String, dynamic> toJson() => {
    'id': id,
    'nombre': nombre,
    'inicio': inicio.toIso8601String(),
    'fin': fin.toIso8601String(),
    'activo': activo,
  };

  factory RangoVacaciones.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() => {
    'alarmaId': alarmaId,
    'ejecucion': ejecucion.toIso8601String(),
    'tipo': tipo,
  };

  factory ExcepcionAlarma.fromJson(Map<String, dynamic> json) {
    return ExcepcionAlarma(
      alarmaId: json['alarmaId'] as String,
      ejecucion: DateTime.parse(json['ejecucion'] as String),
      tipo: json['tipo'] as String? ?? 'skipNext',
    );
  }
}
