import 'dart:async';

import 'package:flutter/foundation.dart';

import '../modelos/alarma_musical.dart';
import '../servicios/servicio_alarmas.dart';
import '../servicios/servicio_alarmas_android.dart';

class EstadoAlarmas extends ChangeNotifier {
  EstadoAlarmas({
    ServicioAlarmas? servicio,
    ServicioAlarmasAndroid? android,
    bool iniciarAutomaticamente = true,
  }) : servicio = servicio ?? ServicioAlarmas(),
       android = android ?? ServicioAlarmasAndroid() {
    if (iniciarAutomaticamente) {
      inicializar();
    }
  }

  final ServicioAlarmas servicio;
  final ServicioAlarmasAndroid android;

  List<AlarmaMusical> _alarmas = [];
  List<RangoVacaciones> _vacaciones = [];
  List<ExcepcionAlarma> _excepciones = [];
  DiagnosticoAlarmasAndroid? _diagnostico;
  Timer? _refresco;
  Timer? _vigilancia;
  final _alarmasVencidasController = StreamController<AlarmaMusical>.broadcast();
  final Set<String> _ejecucionesEmitidas = {};
  bool _cargando = false;
  String? _error;

  List<AlarmaMusical> get alarmas => List.unmodifiable(_alarmas);
  List<RangoVacaciones> get vacaciones => List.unmodifiable(_vacaciones);
  List<ExcepcionAlarma> get excepciones => List.unmodifiable(_excepciones);
  DiagnosticoAlarmasAndroid? get diagnostico => _diagnostico;
  bool get cargando => _cargando;
  String? get error => _error;
  Stream<AlarmaMusical> get alarmasVencidasStream =>
      _alarmasVencidasController.stream;

  AlarmaMusical? get proximaAlarma {
    final candidatas =
        _alarmas.where((a) => a.activa && a.proximaEjecucion != null).toList()
          ..sort((a, b) => a.proximaEjecucion!.compareTo(b.proximaEjecucion!));
    return candidatas.isEmpty ? null : candidatas.first;
  }

  Future<void> inicializar() async {
    _cargando = true;
    _error = null;
    notifyListeners();
    try {
      final config = await servicio.cargar();
      _aplicar(config);
      await _sincronizarTodas();
      await cargarDiagnostico();
      _activarRefresco();
    } catch (e) {
      _error = 'No se pudieron cargar las alarmas: $e';
    } finally {
      _cargando = false;
      notifyListeners();
    }
  }

  Future<void> guardarAlarma(AlarmaMusical alarma) async {
    final config = await servicio.guardarAlarma(alarma);
    _aplicar(config);
    try {
      await android.programar(_alarmas.firstWhere((a) => a.id == alarma.id));
    } catch (e) {
      _error =
          'Alarma guardada, pero Android no pudo programarla todavía: $e';
    }
    notifyListeners();
  }

  Future<void> refrescarProgramacion() async {
    final config = await servicio.recalcularTodas();
    _aplicar(config);
    await _sincronizarTodas();
    notifyListeners();
  }

  Future<void> eliminarAlarma(String id) async {
    final config = await servicio.eliminarAlarma(id);
    _aplicar(config);
    await android.cancelar(id);
    notifyListeners();
  }

  Future<void> cambiarActiva(AlarmaMusical alarma, bool activa) async {
    await guardarAlarma(alarma.copyWith(activa: activa));
  }

  Future<void> saltarProxima(String alarmaId) async {
    final config = await servicio.saltarProxima(alarmaId);
    _aplicar(config);
    AlarmaMusical? alarma;
    for (final item in _alarmas) {
      if (item.id == alarmaId) {
        alarma = item;
        break;
      }
    }
    if (alarma != null) {
      await android.programar(alarma);
    }
    notifyListeners();
  }

  Future<void> guardarVacaciones(List<RangoVacaciones> vacaciones) async {
    final config = await servicio.guardarVacaciones(vacaciones);
    _aplicar(config);
    await _sincronizarTodas();
    notifyListeners();
  }

  Future<void> posponerAlarma(AlarmaMusical alarma, int minutos) async {
    final proxima = DateTime.now().add(Duration(minutes: minutos));
    await android.ocultarNotificacionAlarma(alarma.id);
    await android.programar(alarma.copyWith(proximaEjecucion: proxima));
  }

  Future<void> finalizarEjecucion(String alarmaId) async {
    await android.ocultarNotificacionAlarma(alarmaId);
    await refrescarProgramacion();
  }

  Future<void> crearRangoVacaciones(RangoVacaciones rango) async {
    final nuevos = [..._vacaciones, rango];
    await guardarVacaciones(nuevos);
  }

  Future<void> eliminarRangoVacaciones(String id) async {
    final nuevos = _vacaciones.where((v) => v.id != id).toList();
    await guardarVacaciones(nuevos);
  }

  ExcepcionAlarma? ultimaExcepcionPara(String alarmaId) {
    final candidatas =
        _excepciones.where((e) => e.alarmaId == alarmaId).toList()
          ..sort((a, b) => b.ejecucion.compareTo(a.ejecucion));
    return candidatas.isEmpty ? null : candidatas.first;
  }

  Future<void> cargarDiagnostico() async {
    try {
      _diagnostico = await android.diagnostico();
    } catch (_) {
      _diagnostico = null;
    }
    notifyListeners();
  }

  Future<void> _sincronizarTodas() async {
    for (final alarma in _alarmas) {
      await android.programar(alarma);
    }
  }

  void _aplicar(ConfiguracionAlarmas config) {
    _alarmas = config.alarmas;
    _vacaciones = config.vacaciones;
    _excepciones = config.excepciones;
  }

  void _activarRefresco() {
    _refresco?.cancel();
    _refresco = Timer.periodic(const Duration(minutes: 1), (_) {
      refrescarProgramacion();
    });
    _vigilarAlarmasVencidas();
    _vigilancia?.cancel();
    _vigilancia = Timer.periodic(const Duration(seconds: 10), (_) {
      _vigilarAlarmasVencidas();
    });
  }

  void _vigilarAlarmasVencidas() {
    final ahora = DateTime.now();
    for (final alarma in _alarmas) {
      final proxima = alarma.proximaEjecucion;
      if (!alarma.activa || proxima == null) continue;
      if (proxima.isAfter(ahora)) continue;
      final key = '${alarma.id}:${proxima.millisecondsSinceEpoch}';
      if (_ejecucionesEmitidas.add(key)) {
        _alarmasVencidasController.add(alarma);
      }
    }
  }

  @override
  void dispose() {
    _refresco?.cancel();
    _vigilancia?.cancel();
    _alarmasVencidasController.close();
    super.dispose();
  }
}
