import 'dart:convert';
import 'dart:math';
import 'package:flutter/services.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';

/// Categorías disponibles en el banco de palabras.
class BancoPalabras {
  final Map<String, List<String>> categorias;
  final Map<String, String> pistasPorCategoria;

  BancoPalabras(this.categorias, {Map<String, String>? pistasPorCategoria})
    : pistasPorCategoria = pistasPorCategoria ?? {};

  static final Map<String, BancoPalabras> _instancias = {};

  static Future<BancoPalabras> cargar({String idioma = 'es'}) async {
    if (_instancias.containsKey(idioma)) return _instancias[idioma]!;

    String jsonStr;
    try {
      jsonStr = await rootBundle.loadString(
        'assets/words/palabras_$idioma.json',
      );
    } catch (_) {
      try {
        final archivoLegacy = idioma == 'es'
            ? 'assets/palabras.json'
            : 'assets/palabras_$idioma.json';
        jsonStr = await rootBundle.loadString(archivoLegacy);
      } catch (_) {
        if (idioma != 'es') return cargar(idioma: 'es');
        rethrow;
      }
    }

    final data = json.decode(jsonStr) as Map<String, dynamic>;
    final cats = data['categorias'] as Map<String, dynamic>;
    final mapa = <String, List<String>>{};
    final pistas = <String, String>{};

    for (final entrada in cats.entries) {
      final valor = entrada.value;
      if (valor is Map<String, dynamic>) {
        mapa[entrada.key] = List<String>.from(valor['palabras'] as List);
        final pista = valor['pista'];
        if (pista is String && pista.isNotEmpty) pistas[entrada.key] = pista;
      } else {
        mapa[entrada.key] = List<String>.from(valor as List);
      }
    }

    _instancias[idioma] = BancoPalabras(mapa, pistasPorCategoria: pistas);
    return _instancias[idioma]!;
  }

  List<String> get nombresCategorias => categorias.keys.toList();

  /// Obtiene una palabra aleatoria de la categoría dada (o de todas si es null).
  String palabraAleatoria(String? categoria) {
    final rng = Random();
    if (categoria == null || categoria == 'todas') {
      final todasPalabras = categorias.values.expand((l) => l).toList();
      return todasPalabras[rng.nextInt(todasPalabras.length)];
    }
    final lista = categorias[categoria]!;
    return lista[rng.nextInt(lista.length)];
  }

  /// Devuelve la categoría a la que pertenece una palabra.
  String? categoriaDepalabra(String palabra) {
    for (final entrada in categorias.entries) {
      if (entrada.value.contains(palabra)) return entrada.key;
    }
    return null;
  }

  /// Devuelve la pista localizada de una categoría si el banco la trae.
  String? pistaDeCategoria(String categoria) => pistasPorCategoria[categoria];

  /// Devuelve el nombre localizado de la categoría usando AppLocalizations.
  static String nombreBonitoCategoria(String clave, [AppLocalizations? l10n]) {
    if (l10n != null) {
      final nombres = {
        'todas': l10n.categoryAll,
        'animales': l10n.categoryAnimals,
        'comida': l10n.categoryFood,
        'paises': l10n.categoryCountries,
        'deportes': l10n.categorySports,
        'profesiones': l10n.categoryProfessions,
        'objetos': l10n.categoryObjects,
        'lugares': l10n.categoryPlaces,
        'peliculas': l10n.categoryMovies,
        'musica': l10n.categoryMusic,
        'tecnologia': l10n.categoryTechnology,
      };
      return nombres[clave] ?? clave;
    }
    const nombres = {
      'todas': 'Todas',
      'animales': 'Animales',
      'comida': 'Comida',
      'paises': 'Países',
      'deportes': 'Deportes',
      'profesiones': 'Profesiones',
      'objetos': 'Objetos',
      'lugares': 'Lugares',
      'peliculas': 'Películas',
      'musica': 'Música',
      'tecnologia': 'Tecnología',
    };
    return nombres[clave] ?? clave;
  }
}

class EntradaPalabraTraducida {
  final String palabra;
  final String pista;

  const EntradaPalabraTraducida({required this.palabra, required this.pista});
}

class BancoPalabrasTraducidas {
  final Map<String, List<EntradaPalabraTraducida>> categorias;

  const BancoPalabrasTraducidas(this.categorias);

  static final Map<String, BancoPalabrasTraducidas> _instancias = {};

  static Future<BancoPalabrasTraducidas> cargar({String idioma = 'es'}) async {
    if (_instancias.containsKey(idioma)) return _instancias[idioma]!;

    final banco = await BancoPalabras.cargar(idioma: idioma);
    final mapa = <String, List<EntradaPalabraTraducida>>{};
    for (final categoria in banco.categorias.entries) {
      final pista = banco.pistaDeCategoria(categoria.key) ?? categoria.key;
      mapa[categoria.key] = categoria.value
          .map((palabra) => EntradaPalabraTraducida(palabra: palabra, pista: pista))
          .toList();
    }

    _instancias[idioma] = BancoPalabrasTraducidas(mapa);
    return _instancias[idioma]!;
  }
}
