import 'dart:async';

import 'package:pluriwave/modelos/emisora.dart';
import 'package:pluriwave/modelos/preset_ecualizador.dart';
import 'package:pluriwave/servicios/servicio_audio.dart';
import 'package:pluriwave/servicios/servicio_ecualizador.dart';
import 'package:pluriwave/servicios/servicio_favoritos.dart';
import 'package:pluriwave/servicios/servicio_radio.dart';

class FakeServicioAudio extends ServicioAudio {
  FakeServicioAudio({this.ecualizadorActivo = true}) {
    _estadoController.add(EstadoReproduccion.detenido);
  }

  final bool ecualizadorActivo;
  final _estadoController = StreamController<EstadoReproduccion>.broadcast();
  final List<PresetEcualizador> presetsAplicados = [];
  final List<Emisora> emisorasReproducidas = [];
  final List<bool> cambiosEcualizadorActivo = [];
  Emisora? _emisoraActual;
  EstadoReproduccion _estadoActual = EstadoReproduccion.detenido;

  @override
  Emisora? get emisoraActual => _emisoraActual;

  @override
  bool get ecualizadorDisponible => ecualizadorActivo;

  @override
  Stream<EstadoReproduccion> get estadoStream => _estadoController.stream;

  @override
  bool get estaSonando => _estadoActual == EstadoReproduccion.reproduciendo;

  @override
  Future<void> reproducir(Emisora emisora) async {
    _emisoraActual = emisora;
    emisorasReproducidas.add(emisora);
    emitirEstado(EstadoReproduccion.reproduciendo);
  }

  @override
  Future<void> detener() async {
    _emisoraActual = null;
    emitirEstado(EstadoReproduccion.detenido);
  }

  void emitirEstado(EstadoReproduccion estado) {
    _estadoActual = estado;
    _estadoController.add(estado);
  }

  @override
  Future<void> aplicarPreset(PresetEcualizador preset) async {
    presetsAplicados.add(preset);
  }

  @override
  Future<void> setBanda(int index, double db) async {}

  @override
  Future<void> setEcualizadorActivo(bool activo) async {
    cambiosEcualizadorActivo.add(activo);
  }

  @override
  Future<void> dispose() async {
    await _estadoController.close();
  }
}

class FakeServicioFavoritos extends ServicioFavoritos {
  final List<Emisora> _favoritos = [];
  int toggleCalls = 0;

  @override
  Future<List<Emisora>> obtenerTodos() async =>
      _favoritos.map((e) => e.copyWith()).toList();

  @override
  Future<void> agregar(Emisora emisora) async {
    _favoritos.removeWhere((e) => e.uuid == emisora.uuid);
    _favoritos.add(emisora.copyWith(orden: _favoritos.length));
  }

  @override
  Future<void> eliminar(String uuid) async {
    _favoritos.removeWhere((e) => e.uuid == uuid);
    for (var i = 0; i < _favoritos.length; i++) {
      _favoritos[i] = _favoritos[i].copyWith(orden: i);
    }
  }

  @override
  Future<bool> esFavorito(String uuid) async =>
      _favoritos.any((e) => e.uuid == uuid);

  @override
  Future<bool> toggleFavorito(Emisora emisora) async {
    toggleCalls += 1;
    if (_favoritos.any((e) => e.uuid == emisora.uuid)) {
      await eliminar(emisora.uuid);
      return false;
    }
    await agregar(emisora);
    return true;
  }

  @override
  Future<void> reordenar(String uuid, int nuevoOrden) async {
    final oldIndex = _favoritos.indexWhere((e) => e.uuid == uuid);
    if (oldIndex == -1 || _favoritos.isEmpty) return;
    final targetIndex = nuevoOrden.clamp(0, _favoritos.length - 1);
    final moved = _favoritos.removeAt(oldIndex);
    _favoritos.insert(targetIndex, moved);
    for (var i = 0; i < _favoritos.length; i++) {
      _favoritos[i] = _favoritos[i].copyWith(orden: i);
    }
  }
}

class FakeServicioRadio extends ServicioRadio {
  FakeServicioRadio({
    List<Emisora>? populares,
    List<Emisora>? tendencias,
    List<Emisora>? busqueda,
    List<List<Emisora>>? popularesPorLlamada,
    List<List<Emisora>>? tendenciasPorLlamada,
    List<Object>? erroresPopularesPorLlamada,
    List<Object>? erroresTendenciasPorLlamada,
  }) : _populares = populares ?? [],
       _tendencias = tendencias ?? [],
       _busqueda = busqueda ?? [],
       _popularesPorLlamada = popularesPorLlamada ?? const [],
       _tendenciasPorLlamada = tendenciasPorLlamada ?? const [],
       _erroresPopularesPorLlamada = erroresPopularesPorLlamada ?? const [],
       _erroresTendenciasPorLlamada = erroresTendenciasPorLlamada ?? const [];

  final List<Emisora> _populares;
  final List<Emisora> _tendencias;
  final List<Emisora> _busqueda;
  final List<List<Emisora>> _popularesPorLlamada;
  final List<List<Emisora>> _tendenciasPorLlamada;
  final List<Object> _erroresPopularesPorLlamada;
  final List<Object> _erroresTendenciasPorLlamada;

  int obtenerPopularesCalls = 0;
  int obtenerTendenciasCalls = 0;
  int registrarClickCalls = 0;
  String? ultimoUuidClick;

  Exception _normalizarError(Object error) =>
      error is Exception ? error : Exception(error.toString());

  @override
  Future<List<Emisora>> obtenerPopulares({
    int limit = 30,
    int offset = 0,
  }) async {
    final llamada = obtenerPopularesCalls++;
    if (llamada < _erroresPopularesPorLlamada.length) {
      throw _normalizarError(_erroresPopularesPorLlamada[llamada]);
    }
    final data =
        llamada < _popularesPorLlamada.length
            ? _popularesPorLlamada[llamada]
            : _populares;
    return data.take(limit).toList();
  }

  @override
  Future<List<Emisora>> obtenerTendencias({int limit = 20}) async {
    final llamada = obtenerTendenciasCalls++;
    if (llamada < _erroresTendenciasPorLlamada.length) {
      throw _normalizarError(_erroresTendenciasPorLlamada[llamada]);
    }
    final data =
        llamada < _tendenciasPorLlamada.length
            ? _tendenciasPorLlamada[llamada]
            : _tendencias;
    return data.take(limit).toList();
  }

  @override
  Future<List<Emisora>> buscar({
    String? nombre,
    String? pais,
    String? idioma,
    String? tag,
    int limit = 30,
    int offset = 0,
  }) async {
    return _busqueda.skip(offset).take(limit).toList();
  }

  @override
  Future<void> registrarClick(String uuid) async {
    registrarClickCalls += 1;
    ultimoUuidClick = uuid;
  }
}

class FakeServicioEcualizador extends ServicioEcualizador {
  FakeServicioEcualizador({
    PresetEcualizador? principal,
    Map<String, PresetEcualizador>? porEmisora,
    bool activo = true,
  }) : _config = ConfiguracionEcualizador(
         principal: principal ?? PresetEcualizador.flat,
         porEmisora: porEmisora ?? {},
         activo: activo,
       );

  ConfiguracionEcualizador _config;
  ConfiguracionEcualizador get config => _config;

  @override
  Future<ConfiguracionEcualizador> cargar() async => _config;

  @override
  Future<void> guardarPrincipal(PresetEcualizador preset) async {
    _config = ConfiguracionEcualizador(
      principal: preset,
      porEmisora: _config.porEmisora,
      activo: _config.activo,
    );
  }

  @override
  Future<void> guardarActivo(bool activo) async {
    _config = ConfiguracionEcualizador(
      principal: _config.principal,
      porEmisora: _config.porEmisora,
      activo: activo,
    );
  }

  @override
  Future<void> guardarPorEmisora(String uuid, PresetEcualizador preset) async {
    final mapa = Map<String, PresetEcualizador>.from(_config.porEmisora);
    mapa[uuid] = preset;
    _config = ConfiguracionEcualizador(
      principal: _config.principal,
      porEmisora: mapa,
      activo: _config.activo,
    );
  }

  @override
  Future<void> eliminarPorEmisora(String uuid) async {
    final mapa = Map<String, PresetEcualizador>.from(_config.porEmisora);
    mapa.remove(uuid);
    _config = ConfiguracionEcualizador(
      principal: _config.principal,
      porEmisora: mapa,
      activo: _config.activo,
    );
  }
}

Emisora emisoraDemo({
  required String uuid,
  required String nombre,
  String url = 'https://stream.demo/radio',
}) {
  return Emisora(uuid: uuid, nombre: nombre, url: url);
}
