import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../modelos/emisora.dart';
import '../modelos/preset_ecualizador.dart';
import '../servicios/servicio_audio.dart';
import '../servicios/servicio_ecualizador.dart';
import '../servicios/servicio_favoritos.dart';
import '../servicios/servicio_grabacion_radio.dart';
import '../servicios/servicio_radio.dart';
import '../servicios/servicio_timer.dart';

/// Estado global de la app con ChangeNotifier (Provider).
class EstadoRadio extends ChangeNotifier {
  EstadoRadio({
    ServicioAudio? audio,
    ServicioFavoritos? favoritos,
    ServicioRadio? radio,
    ServicioEcualizador? servicioEcualizador,
    ServicioGrabacionRadio? servicioGrabacion,
    Future<File> Function()? resolverArchivoCustom,
    bool iniciarAutomaticamente = true,
  }) : audio = audio ?? ServicioAudio(),
       favoritos = favoritos ?? ServicioFavoritos(),
       radio = radio ?? ServicioRadio(),
       servicioEcualizador = servicioEcualizador ?? ServicioEcualizador(),
       grabacion = servicioGrabacion ?? ServicioGrabacionRadio(),
       _resolverArchivoCustom = resolverArchivoCustom {
    timer = ServicioTimer(this.audio);
    _escucharErroresReproduccion();
    _escucharGrabacion();
    if (iniciarAutomaticamente) {
      _initFuture = _init();
    }
  }

  final ServicioAudio audio;
  final ServicioFavoritos favoritos;
  final ServicioRadio radio;
  final ServicioEcualizador servicioEcualizador;
  final ServicioGrabacionRadio grabacion;
  final Future<File> Function()? _resolverArchivoCustom;

  late final ServicioTimer timer;
  StreamSubscription<EstadoReproduccion>? _suscripcionEstadoAudio;
  StreamSubscription<EstadoGrabacionRadio>? _suscripcionGrabacion;
  Future<void>? _initFuture;
  int _revisionReproduccion = 0;
  Emisora? _emisoraSeleccionada;
  String? _emisoraPreferidaUuid;

  // Errores de reproducción → SnackBar.
  final _errorController = StreamController<String>.broadcast();
  Stream<String> get errorStream => _errorController.stream;

  List<Emisora> _populares = [];
  List<Emisora> _tendencias = [];
  List<Emisora> _resultadosBusqueda = [];
  List<Emisora> _emisorasCercanas = [];
  List<Emisora> _listaFavoritos = [];
  List<Emisora> _emisorasCustom = [];

  // Presets EQ guardados por uuid de emisora.
  final Map<String, PresetEcualizador> _presetsEmisoraMap = {};
  PresetEcualizador _presetPrincipal = PresetEcualizador.flat;
  PresetEcualizador _presetActual = PresetEcualizador.flat;
  bool _ecualizadorActivo = true;

  bool _cargandoPopulares = false;
  bool _cargandoBusqueda = false;
  bool _cargandoMasBusqueda = false;
  bool _hayMasBusqueda = true;
  bool _cargandoCercanas = false;
  String? _paisCercanoDetectado;
  String? _errorCercanas;
  int _offsetBusqueda = 0;
  String? _ultimoNombreBusqueda;
  String? _ultimoPaisBusqueda;
  String? _ultimoIdiomaBusqueda;
  String? _ultimoTagBusqueda;
  String? _errorCarga;
  static const _keyEmisoraPreferida = 'emisora_preferida_uuid_v1';
  static const _keyTimerSuenoPresets = 'timer_sueno_presets_segundos_v1';
  static const _timerSuenoPresetsDefecto = <int>[
    180,
    300,
    600,
    900,
    1800,
    3600,
    5400,
    7200,
    10800,
  ];
  List<int> _timerSuenoPresetsSegundos = List<int>.from(
    _timerSuenoPresetsDefecto,
  );

  List<Emisora> get populares => _populares;
  List<Emisora> get tendencias => _tendencias;
  List<Emisora> get resultadosBusqueda => _resultadosBusqueda;
  List<Emisora> get emisorasCercanas => _emisorasCercanas;
  List<Emisora> get listaFavoritos => _listaFavoritos;
  List<Emisora> get emisorasCustom => _emisorasCustom;
  bool get cargandoPopulares => _cargandoPopulares;
  bool get cargandoBusqueda => _cargandoBusqueda;
  bool get cargandoMasBusqueda => _cargandoMasBusqueda;
  bool get hayMasBusqueda => _hayMasBusqueda;
  bool get cargandoCercanas => _cargandoCercanas;
  String? get paisCercanoDetectado => _paisCercanoDetectado;
  String? get errorCercanas => _errorCercanas;
  String? get error => _errorCarga;
  Emisora? get emisoraActual => _emisoraSeleccionada ?? audio.emisoraActual;
  Emisora? get emisoraPreferida => _resolverEmisoraPreferida();
  String? get emisoraPreferidaUuid => emisoraPreferida?.uuid;
  Stream<EstadoReproduccion> get estadoStream => audio.estadoStream;
  PresetEcualizador get presetEcualizador => _presetActual;
  PresetEcualizador get presetPrincipalEcualizador => _presetPrincipal;
  bool get ecualizadorActivo => _ecualizadorActivo;
  bool get ecualizadorDisponible => audio.ecualizadorDisponible;
  List<int> get timerSuenoPresetsSegundos =>
      List<int>.unmodifiable(_timerSuenoPresetsSegundos);

  bool get emisoraActualEsFavorita {
    final actual = emisoraActual;
    if (actual == null) return false;
    return _listaFavoritos.any((e) => e.uuid == actual.uuid);
  }

  bool get emisoraActualTienePresetPropio {
    final actual = emisoraActual;
    if (actual == null) return false;
    return tienePresetEcualizadorPorEmisora(actual.uuid);
  }

  EstadoGrabacionRadio get estadoGrabacion => grabacion.estado;
  bool get grabacionActiva => grabacion.estado.activa;
  String? get directorioGrabacion => grabacion.directorioConfigurado;

  /// Lista principal (home): custom + populares, sin duplicados.
  List<Emisora> get emisorasInicio {
    final mapa = <String, Emisora>{};
    for (final emisora in _emisorasCustom) {
      mapa[emisora.uuid] = emisora;
    }
    for (final emisora in _populares) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    return mapa.values.toList();
  }

  List<Emisora> get emisorasDisponiblesPreferencia {
    final mapa = <String, Emisora>{};
    for (final emisora in _listaFavoritos) {
      mapa[emisora.uuid] = emisora;
    }
    for (final emisora in _emisorasCustom) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    for (final emisora in _populares) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    for (final emisora in _tendencias) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    for (final emisora in _resultadosBusqueda) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    for (final emisora in _emisorasCercanas) {
      mapa.putIfAbsent(emisora.uuid, () => emisora);
    }
    return mapa.values.toList();
  }

  Future<void> inicializar() {
    _initFuture ??= _init();
    return _initFuture!;
  }

  Future<void> _init() async {
    await grabacion.inicializar();
    await _cargarEcualizadorPersistido();
    await _cargarEmisoraPreferida();
    await _cargarTimerSuenoPresets();
    await Future.wait([
      cargarPopulares(),
      cargarFavoritos(),
      _cargarEmisorasCustom(),
    ]);
    await _normalizarEmisoraPreferida();
  }

  /// Escucha el stream de estado del audio y gestiona errores de reproducción.
  void _escucharErroresReproduccion() {
    _suscripcionEstadoAudio = audio.estadoStream.listen((estado) {
      if (estado == EstadoReproduccion.error && timer.activo) {
        unawaited(timer.cancelar());
      }
      notifyListeners();
    });
  }

  void _escucharGrabacion() {
    _suscripcionGrabacion = grabacion.estadoStream.listen((estado) {
      if (estado.tipo == EstadoGrabacionRadioTipo.error &&
          estado.error != null) {
        _errorController.add('Error al grabar la radio: ${estado.error}');
      }
      notifyListeners();
    });
  }

  Future<void> _cargarEcualizadorPersistido() async {
    try {
      final config = await servicioEcualizador.cargar();
      _presetPrincipal = config.principal;
      _presetActual = config.principal;
      _ecualizadorActivo = config.activo;
      _presetsEmisoraMap
        ..clear()
        ..addAll(config.porEmisora);
      await audio.setEcualizadorActivo(_ecualizadorActivo);
      await audio.aplicarPreset(_presetPrincipal);
    } catch (_) {
      _presetPrincipal = PresetEcualizador.flat;
      _presetActual = PresetEcualizador.flat;
      _ecualizadorActivo = true;
      _presetsEmisoraMap.clear();
    }
  }

  Future<void> cargarPopulares() async {
    _cargandoPopulares = true;
    _errorCarga = null;
    notifyListeners();
    try {
      final results = await Future.wait([
        radio.obtenerPopulares(limit: 30),
        radio.obtenerTendencias(limit: 20),
      ]);
      _populares = results[0];
      _tendencias = results[1];
    } catch (_) {
      _errorCarga = 'Sin conexión a la API de radio';
    } finally {
      _cargandoPopulares = false;
      notifyListeners();
    }
  }

  Future<void> cargarFavoritos() async {
    _listaFavoritos = await favoritos.obtenerTodos();
    await _normalizarEmisoraPreferida();
    notifyListeners();
  }

  Future<void> cambiarEmisoraPreferida(Emisora? emisora) async {
    _emisoraPreferidaUuid = emisora?.uuid;
    final prefs = await SharedPreferences.getInstance();
    if (_emisoraPreferidaUuid == null) {
      await prefs.remove(_keyEmisoraPreferida);
    } else {
      await prefs.setString(_keyEmisoraPreferida, _emisoraPreferidaUuid!);
    }
    notifyListeners();
  }

  Future<void> reproducirEmisoraPreferida() async {
    final preferida = emisoraPreferida;
    if (preferida == null) return;
    await reproducir(preferida);
  }

  Future<void> _cargarTimerSuenoPresets() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final raw = prefs.getString(_keyTimerSuenoPresets);
      if (raw == null) return;
      final decoded = jsonDecode(raw);
      if (decoded is! List) return;
      final presets =
          decoded
              .whereType<num>()
              .map((n) => n.toInt())
              .where((s) => s > 0)
              .toSet()
              .toList()
            ..sort();
      if (presets.isNotEmpty) {
        _timerSuenoPresetsSegundos = presets.take(12).toList();
      }
    } catch (_) {
      _timerSuenoPresetsSegundos = List<int>.from(_timerSuenoPresetsDefecto);
    }
  }

  Future<void> _cargarEmisoraPreferida() async {
    final prefs = await SharedPreferences.getInstance();
    _emisoraPreferidaUuid = prefs.getString(_keyEmisoraPreferida);
  }

  Future<void> _normalizarEmisoraPreferida() async {
    final preferida = _resolverEmisoraPreferida();
    if (preferida?.uuid == _emisoraPreferidaUuid) return;
    _emisoraPreferidaUuid = preferida?.uuid;
    final prefs = await SharedPreferences.getInstance();
    if (_emisoraPreferidaUuid == null) {
      await prefs.remove(_keyEmisoraPreferida);
    } else {
      await prefs.setString(_keyEmisoraPreferida, _emisoraPreferidaUuid!);
    }
  }

  Emisora? _resolverEmisoraPreferida() {
    final uuid = _emisoraPreferidaUuid;
    if (uuid != null) {
      for (final emisora in _listaFavoritos) {
        if (emisora.uuid == uuid) return emisora;
      }
    }
    if (_listaFavoritos.isNotEmpty) return _listaFavoritos.first;
    if (uuid != null) {
      for (final emisora in emisorasDisponiblesPreferencia) {
        if (emisora.uuid == uuid) return emisora;
      }
    }
    final disponibles = emisorasDisponiblesPreferencia;
    return disponibles.isEmpty ? null : disponibles.first;
  }

  static const int _tamanoPaginaBusqueda = 30;
  static const int _maxResultadosBusquedaEnMemoria = 180;

  Future<void> buscar({
    String? nombre,
    String? pais,
    String? idioma,
    String? tag,
  }) async {
    _ultimoNombreBusqueda = nombre;
    _ultimoPaisBusqueda = pais;
    _ultimoIdiomaBusqueda = idioma;
    _ultimoTagBusqueda = tag;
    _offsetBusqueda = 0;
    _hayMasBusqueda = true;
    _cargandoBusqueda = true;
    _resultadosBusqueda = [];
    notifyListeners();
    try {
      final pagina = await radio.buscar(
        nombre: nombre,
        pais: pais,
        idioma: idioma,
        tag: tag,
        limit: _tamanoPaginaBusqueda,
        offset: _offsetBusqueda,
      );
      _resultadosBusqueda = pagina;
      _offsetBusqueda = pagina.length;
      _hayMasBusqueda = pagina.length == _tamanoPaginaBusqueda;
    } catch (_) {
      _errorController.add('Error en la busqueda. Comprueba tu conexion.');
    } finally {
      _cargandoBusqueda = false;
      notifyListeners();
    }
  }

  Future<void> cargarMasBusqueda() async {
    if (_cargandoBusqueda || _cargandoMasBusqueda || !_hayMasBusqueda) return;
    _cargandoMasBusqueda = true;
    notifyListeners();
    try {
      final pagina = await radio.buscar(
        nombre: _ultimoNombreBusqueda,
        pais: _ultimoPaisBusqueda,
        idioma: _ultimoIdiomaBusqueda,
        tag: _ultimoTagBusqueda,
        limit: _tamanoPaginaBusqueda,
        offset: _offsetBusqueda,
      );
      final porUuid = <String, Emisora>{
        for (final emisora in _resultadosBusqueda) emisora.uuid: emisora,
      };
      for (final emisora in pagina) {
        porUuid[emisora.uuid] = emisora;
      }
      var nuevaLista = porUuid.values.toList();
      if (nuevaLista.length > _maxResultadosBusquedaEnMemoria) {
        nuevaLista = nuevaLista.sublist(
          nuevaLista.length - _maxResultadosBusquedaEnMemoria,
        );
      }
      _resultadosBusqueda = nuevaLista;
      _offsetBusqueda += pagina.length;
      _hayMasBusqueda = pagina.length == _tamanoPaginaBusqueda;
    } catch (_) {
      _errorController.add('No se pudieron cargar mas emisoras.');
    } finally {
      _cargandoMasBusqueda = false;
      notifyListeners();
    }
  }

  Future<void> cargarEmisorasCercanas() async {
    _cargandoCercanas = true;
    _errorCercanas = null;
    notifyListeners();
    try {
      var pais = PlatformDispatcher.instance.locale.countryCode;
      final servicioActivo = await Geolocator.isLocationServiceEnabled();
      if (servicioActivo) {
        var permiso = await Geolocator.checkPermission();
        if (permiso == LocationPermission.denied) {
          permiso = await Geolocator.requestPermission();
        }
        if (permiso == LocationPermission.always ||
            permiso == LocationPermission.whileInUse) {
          final posicion = await Geolocator.getCurrentPosition(
            locationSettings: const LocationSettings(
              accuracy: LocationAccuracy.low,
              timeLimit: Duration(seconds: 8),
            ),
          );
          final marcas = await placemarkFromCoordinates(
            posicion.latitude,
            posicion.longitude,
          );
          if (marcas.isNotEmpty) {
            pais = marcas.first.isoCountryCode ?? pais;
          }
        }
      }

      if (pais == null || pais.isEmpty) {
        throw Exception('No se pudo detectar tu region');
      }
      _paisCercanoDetectado = pais;
      _emisorasCercanas = await radio.buscar(pais: pais, limit: 30);
    } catch (_) {
      _errorCercanas =
          'No pudimos detectar emisoras cercanas. Usa filtros por pais.';
      _emisorasCercanas = [];
    } finally {
      _cargandoCercanas = false;
      notifyListeners();
    }
  }

  Future<void> reproducir(Emisora emisora) async {
    final revision = ++_revisionReproduccion;
    _emisoraSeleccionada = emisora;
    notifyListeners();
    try {
      await audio.reproducir(emisora);
      if (revision != _revisionReproduccion) return;
      unawaited(radio.registrarClick(emisora.uuid));
      await _aplicarPresetActivo(_presetParaEmisora(emisora.uuid));
      if (revision != _revisionReproduccion) return;
      notifyListeners();
    } catch (e) {
      if (revision != _revisionReproduccion) return;
      if (timer.activo) {
        unawaited(timer.cancelar());
      }
      final mensajeError = e.toString().replaceFirst('Exception: ', '');
      _emisoraSeleccionada = audio.emisoraActual;
      _errorController.add(
        mensajeError.isNotEmpty && mensajeError != 'Exception'
            ? mensajeError
            : 'No se puede reproducir "${emisora.nombre}"',
      );
      notifyListeners();
    }
  }

  Future<void> iniciarGrabacion({Duration? duracion}) async {
    final actual = emisoraActual;
    if (actual == null) {
      _errorController.add('Primero selecciona una emisora para grabar.');
      return;
    }
    try {
      await grabacion.iniciar(actual, duracion: duracion);
    } catch (e) {
      _errorController.add('No se pudo iniciar la grabación: $e');
    }
  }

  Future<void> detenerGrabacion() => grabacion.detener();

  Future<void> cambiarDirectorioGrabacion(String path) async {
    await grabacion.guardarDirectorio(path);
    notifyListeners();
  }

  Future<void> restaurarDirectorioGrabacion() async {
    await grabacion.limpiarDirectorioConfigurado();
    notifyListeners();
  }

  Future<String> directorioGrabacionEfectivo() =>
      grabacion.directorioEfectivo();

  Future<void> togglePlay() async {
    await audio.togglePlay();
    notifyListeners();
  }

  Future<bool> toggleFavorito(Emisora emisora) async {
    final esFav = await favoritos.toggleFavorito(emisora);
    if (!esFav) {
      await deshabilitarPresetEcualizadorPorEmisora(
        emisora.uuid,
        notificar: false,
      );
    }
    await cargarFavoritos();
    return esFav;
  }

  Future<bool> esFavorito(String uuid) => favoritos.esFavorito(uuid);

  // ── Ecualizador ───────────────────────────────────────────────────────────

  bool tienePresetEcualizadorPorEmisora(String uuid) =>
      _presetsEmisoraMap.containsKey(uuid);

  PresetEcualizador? presetEcualizadorPorEmisora(String uuid) =>
      _presetsEmisoraMap[uuid];

  PresetEcualizador _presetParaEmisora(String uuid) =>
      _presetsEmisoraMap[uuid] ?? _presetPrincipal;

  Future<void> _aplicarPresetActivo(PresetEcualizador preset) async {
    _presetActual = preset;
    await audio.aplicarPreset(preset);
  }

  Future<void> cambiarPresetPrincipalEcualizador(
    PresetEcualizador preset, {
    bool notificar = true,
  }) async {
    _presetPrincipal = preset;
    await servicioEcualizador.guardarPrincipal(preset);

    final actual = emisoraActual;
    final puedeAplicarAhora =
        actual == null || !_presetsEmisoraMap.containsKey(actual.uuid);
    if (puedeAplicarAhora) {
      await _aplicarPresetActivo(preset);
    }

    if (notificar) notifyListeners();
  }

  Future<void> guardarPresetEcualizadorPorEmisora(
    String uuid,
    PresetEcualizador preset, {
    bool notificar = true,
  }) async {
    _presetsEmisoraMap[uuid] = preset;
    await servicioEcualizador.guardarPorEmisora(uuid, preset);
    if (emisoraActual?.uuid == uuid) {
      await _aplicarPresetActivo(preset);
    }
    if (notificar) notifyListeners();
  }

  Future<void> habilitarPresetEcualizadorPorEmisora(
    String uuid, {
    PresetEcualizador? base,
    bool notificar = true,
  }) async {
    final presetBase = base ?? _presetsEmisoraMap[uuid] ?? _presetPrincipal;
    await guardarPresetEcualizadorPorEmisora(
      uuid,
      presetBase,
      notificar: notificar,
    );
  }

  Future<void> deshabilitarPresetEcualizadorPorEmisora(
    String uuid, {
    bool notificar = true,
  }) async {
    _presetsEmisoraMap.remove(uuid);
    await servicioEcualizador.eliminarPorEmisora(uuid);
    if (emisoraActual?.uuid == uuid) {
      await _aplicarPresetActivo(_presetPrincipal);
    }
    if (notificar) notifyListeners();
  }

  Future<void> cambiarModoEcualizadorEmisoraActual({
    required bool usarPropio,
  }) async {
    final actual = emisoraActual;
    if (actual == null) return;
    if (usarPropio) {
      await habilitarPresetEcualizadorPorEmisora(actual.uuid);
    } else {
      await deshabilitarPresetEcualizadorPorEmisora(actual.uuid);
    }
  }

  Future<void> cambiarEcualizadorActivo(bool activo) async {
    _ecualizadorActivo = activo;
    await servicioEcualizador.guardarActivo(activo);
    await audio.setEcualizadorActivo(activo);
    if (activo) {
      await audio.aplicarPreset(_presetActual);
    }
    notifyListeners();
  }

  Future<void> cambiarPresetEcualizador(
    PresetEcualizador preset, {
    bool guardarPorEmisora = true,
  }) async {
    final actual = emisoraActual;
    final usarPresetPropio =
        guardarPorEmisora &&
        actual != null &&
        _presetsEmisoraMap.containsKey(actual.uuid);

    if (usarPresetPropio) {
      await guardarPresetEcualizadorPorEmisora(actual.uuid, preset);
      return;
    }
    await cambiarPresetPrincipalEcualizador(preset);
  }

  Future<void> cambiarBandaEcualizador(int index, double db) async {
    final bandas = List<double>.from(_presetActual.bandas);
    if (index < 0 || index >= bandas.length) return;

    bandas[index] = db;
    final modificado = PresetEcualizador(
      nombre: 'Personalizado',
      bandas: bandas,
    );
    await cambiarPresetEcualizador(modificado);
  }

  // ── Emisoras personalizadas ───────────────────────────────────────────────

  Future<File> _archivoCustom() async {
    if (_resolverArchivoCustom != null) {
      return _resolverArchivoCustom();
    }
    final dir = await getApplicationDocumentsDirectory();
    return File('${dir.path}/emisoras_custom.json');
  }

  Future<void> _cargarEmisorasCustom() async {
    try {
      final archivo = await _archivoCustom();
      if (!await archivo.exists()) {
        _emisorasCustom = [];
        notifyListeners();
        return;
      }

      final data = jsonDecode(await archivo.readAsString()) as List;
      _emisorasCustom =
          data
              .map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
              .toList();
    } catch (_) {
      _emisorasCustom = [];
    }
    notifyListeners();
  }

  Future<void> _guardarEmisorasCustom() async {
    final archivo = await _archivoCustom();
    await archivo.writeAsString(
      jsonEncode(_emisorasCustom.map((e) => e.toMap()).toList()),
    );
  }

  Future<void> agregarEmisoraCustom(Emisora emisora) async {
    _emisorasCustom.removeWhere((e) => e.uuid == emisora.uuid);
    _emisorasCustom.add(emisora);
    await _guardarEmisorasCustom();
    notifyListeners();
  }

  // Compatibilidad con el nombre histórico (typo original).
  Future<void> agregarEmitoraCustom(Emisora emisora) =>
      agregarEmisoraCustom(emisora);

  Future<void> eliminarEmisoraCustom(String uuid) async {
    _emisorasCustom.removeWhere((e) => e.uuid == uuid);
    await _guardarEmisorasCustom();
    notifyListeners();
  }

  // Compatibilidad con el nombre histórico (typo original).
  Future<void> eliminarEmitoraCustom(String uuid) =>
      eliminarEmisoraCustom(uuid);

  // ── Export / Import ───────────────────────────────────────────────────────

  /// Genera el JSON de toda la configuración.
  Future<Map<String, dynamic>> exportarConfig() async {
    final favs = await favoritos.obtenerTodos();
    return {
      'version': 1,
      'exportedAt': DateTime.now().toIso8601String(),
      'favoritos': favs.map((e) => e.toMap()).toList(),
      'emisorasCustom': _emisorasCustom.map((e) => e.toMap()).toList(),
      'presetPrincipalEcualizador': _presetPrincipal.toJson(),
      'presetsEcualizador': _presetsEmisoraMap.map(
        (uuid, preset) => MapEntry(uuid, preset.toJson()),
      ),
    };
  }

  /// Importa configuración desde un JSON exportado previamente.
  Future<void> importarConfig(Map<String, dynamic> data) async {
    final version = data['version'] as int? ?? 1;
    if (version != 1) throw Exception('Versión de configuración no compatible');

    final favRaw = data['favoritos'] as List? ?? [];
    for (final raw in favRaw) {
      final emisora = Emisora.fromMap(Map<String, dynamic>.from(raw as Map));
      await favoritos.agregar(emisora);
    }

    final customRaw = data['emisorasCustom'] as List? ?? [];
    _emisorasCustom =
        customRaw
            .map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
            .toList();
    await _guardarEmisorasCustom();

    final principalRaw = data['presetPrincipalEcualizador'];
    if (principalRaw is Map) {
      _presetPrincipal = PresetEcualizador.desdeJson(
        Map<String, dynamic>.from(principalRaw),
      );
    } else {
      _presetPrincipal = PresetEcualizador.flat;
    }

    final presetsRaw = data['presetsEcualizador'] as Map? ?? {};
    _presetsEmisoraMap
      ..clear()
      ..addAll(
        presetsRaw.map<String, PresetEcualizador>(
          (uuid, presetJson) => MapEntry(
            uuid as String,
            PresetEcualizador.desdeJson(
              Map<String, dynamic>.from(presetJson as Map),
            ),
          ),
        ),
      );

    await servicioEcualizador.guardarConfiguracion(
      ConfiguracionEcualizador(
        principal: _presetPrincipal,
        porEmisora: _presetsEmisoraMap,
        activo: _ecualizadorActivo,
      ),
    );

    final actual = emisoraActual;
    final presetActivo =
        actual == null ? _presetPrincipal : _presetParaEmisora(actual.uuid);
    await _aplicarPresetActivo(presetActivo);

    await cargarFavoritos();
    notifyListeners();
  }

  // ── Timer ─────────────────────────────────────────────────────────────────

  void iniciarTimer(int minutos) {
    timer.iniciar(minutos);
    notifyListeners();
  }

  void iniciarTimerDuracion(Duration duracion) {
    timer.iniciarDuracion(duracion);
    notifyListeners();
  }

  void cancelarTimer() {
    unawaited(timer.cancelar());
    notifyListeners();
  }

  Future<void> guardarTimerSuenoPresetsSegundos(List<int> segundos) async {
    final normalizados =
        segundos
            .where((s) => s > 0)
            .map((s) => s.clamp(1, const Duration(hours: 23).inSeconds))
            .toSet()
            .toList()
          ..sort();
    _timerSuenoPresetsSegundos =
        normalizados.isEmpty
            ? List<int>.from(_timerSuenoPresetsDefecto)
            : normalizados.take(12).toList();
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(
      _keyTimerSuenoPresets,
      jsonEncode(_timerSuenoPresetsSegundos),
    );
    notifyListeners();
  }

  Future<void> agregarTimerSuenoPreset(Duration duracion) async {
    await guardarTimerSuenoPresetsSegundos([
      ..._timerSuenoPresetsSegundos,
      duracion.inSeconds,
    ]);
  }

  Future<void> eliminarTimerSuenoPreset(int segundos) async {
    await guardarTimerSuenoPresetsSegundos(
      _timerSuenoPresetsSegundos.where((s) => s != segundos).toList(),
    );
  }

  Future<void> restaurarTimerSuenoPresets() async {
    _timerSuenoPresetsSegundos = List<int>.from(_timerSuenoPresetsDefecto);
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_keyTimerSuenoPresets);
    notifyListeners();
  }

  @override
  void dispose() {
    _suscripcionEstadoAudio?.cancel();
    _suscripcionGrabacion?.cancel();
    _errorController.close();
    audio.dispose();
    unawaited(grabacion.dispose());
    timer.dispose();
    super.dispose();
  }
}
