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<AlarmaMusical> alarmas;
  final List<RangoVacaciones> vacaciones;
  final List<ExcepcionAlarma> 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<ConfiguracionAlarmas> 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<String, dynamic>;
      return ConfiguracionAlarmas(
        alarmas:
            (data['alarmas'] as List? ?? const [])
                .whereType<Map>()
                .map(
                  (e) => AlarmaMusical.fromJson(Map<String, dynamic>.from(e)),
                )
                .toList(),
        vacaciones:
            (data['vacaciones'] as List? ?? const [])
                .whereType<Map>()
                .map(
                  (e) => RangoVacaciones.fromJson(Map<String, dynamic>.from(e)),
                )
                .toList(),
        excepciones:
            (data['excepciones'] as List? ?? const [])
                .whereType<Map>()
                .map(
                  (e) => ExcepcionAlarma.fromJson(Map<String, dynamic>.from(e)),
                )
                .toList(),
      );
    } catch (_) {
      return const ConfiguracionAlarmas(
        alarmas: [],
        vacaciones: [],
        excepciones: [],
      );
    }
  }

  Future<ConfiguracionAlarmas> guardarAlarma(
    AlarmaMusical alarma, {
    List<RangoVacaciones>? vacaciones,
    List<ExcepcionAlarma>? excepciones,
  }) async {
    final config = await cargar();
    final ahora = _reloj();
    final alarmas = List<AlarmaMusical>.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<ConfiguracionAlarmas> 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<ConfiguracionAlarmas> guardarVacaciones(
    List<RangoVacaciones> 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<ConfiguracionAlarmas> 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<ConfiguracionAlarmas> 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<int> 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<void> _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<RangoVacaciones> vacaciones,
    List<ExcepcionAlarma> excepciones,
  ) {
    return alarma.copyWith(
      proximaEjecucion: _programacion.calcularProxima(
        alarma: alarma,
        desde: _reloj(),
        vacaciones: vacaciones,
        excepciones: excepciones,
      ),
    );
  }

  Future<SharedPreferences> _resolverPrefs() async =>
      _prefs ?? SharedPreferences.getInstance();
}
