#!/usr/bin/env python3
"""
logros_kids.py - Sistema de logros/gamificacion para SiPi Kids.

Independiente de la interfaz (se puede usar desde editor_sipi_kids.py
o desde cualquier otro front-end que se agregue mas adelante). Guarda
el progreso de forma persistente en el perfil del usuario, asi los
logros sobreviven entre sesiones, y expone una API simple:

    import logros_kids
    nuevos = logros_kids.registrar_evento("bloque_usado", bloque="hablar")
    for logro in nuevos:
        mostrar_notificacion(logro)

Cada logro tiene una condicion (funcion que mira las estadisticas
acumuladas) en vez de un contador hardcodeado, para que agregar un
logro nuevo sea declarativo y no haya que tocar la logica de eventos.
"""
import os
import json

def _carpeta_config_usuario():
    if os.name == "nt":
        base = os.environ.get("APPDATA") or os.path.expanduser("~")
    else:
        base = os.environ.get("XDG_CONFIG_HOME") or os.path.join(os.path.expanduser("~"), ".config")
    carpeta = os.path.join(base, "sipi")
    try:
        os.makedirs(carpeta, exist_ok=True)
    except OSError:
        pass
    return carpeta


ARCHIVO_PROGRESO = os.path.join(_carpeta_config_usuario(), "sipi_kids_progreso.json")

ESTADISTICAS_POR_DEFECTO = {
    "programas_ejecutados": 0,
    "programas_ejecutados_sin_error": 0,
    "programas_guardados": 0,
    "bloques_usados": {},  # {"hablar": 3, "repetir": 1, ...}
    "bloques_distintos_usados": [],
    "pedidos_ia": 0,
    "racha_sin_error": 0,  # ejecuciones seguidas sin error (se resetea al fallar)
    "desafios_vistos": 0,
    "logros_desbloqueados": [],
}


def _cargar():
    if os.path.exists(ARCHIVO_PROGRESO):
        try:
            with open(ARCHIVO_PROGRESO, "r", encoding="utf-8") as f:
                datos = json.load(f)
                fusion = dict(ESTADISTICAS_POR_DEFECTO)
                fusion.update(datos)
                return fusion
        except (json.JSONDecodeError, OSError):
            pass
    return dict(ESTADISTICAS_POR_DEFECTO)


def _guardar(estadisticas):
    try:
        with open(ARCHIVO_PROGRESO, "w", encoding="utf-8") as f:
            json.dump(estadisticas, f, ensure_ascii=False, indent=2)
    except OSError:
        pass


# ------- Catalogo de logros -------
# Cada logro: id, titulo, descripcion, icono, y una funcion condicion(stats) -> bool

def _condicion_primer_programa(stats):
    return stats["programas_ejecutados"] >= 1


def _condicion_diez_programas(stats):
    return stats["programas_ejecutados"] >= 10


def _condicion_sin_errores_5_seguidas(stats):
    return stats["racha_sin_error"] >= 5


def _condicion_todos_los_bloques(stats):
    from editor_sipi_kids import BLOQUES  # import diferido: evita ciclo de imports en modulos standalone
    nombres = {b["icono"] for b in BLOQUES}
    return nombres.issubset(set(stats["bloques_distintos_usados"]))


def _condicion_uso_repetir(stats):
    return stats["bloques_usados"].get("repetir", 0) >= 1


def _condicion_uso_decision(stats):
    return stats["bloques_usados"].get("decision", 0) >= 1


def _condicion_primer_guardado(stats):
    return stats["programas_guardados"] >= 1


def _condicion_uso_ia(stats):
    return stats["pedidos_ia"] >= 1


def _condicion_maestro_bloques(stats):
    return sum(stats["bloques_usados"].values()) >= 25


def _condicion_explorador_desafios(stats):
    return stats.get("desafios_vistos", 0) >= 5


CATALOGO_LOGROS = [
    {
        "id": "primer_programa", "titulo": "¡Primer programa!",
        "descripcion": "Ejecutaste tu primer programa de SiPi.",
        "icono": "insignia", "condicion": _condicion_primer_programa,
    },
    {
        "id": "primer_guardado", "titulo": "Guardado seguro",
        "descripcion": "Guardaste un programa por primera vez.",
        "icono": "insignia", "condicion": _condicion_primer_guardado,
    },
    {
        "id": "uso_repetir", "titulo": "Maestro de la repeticion",
        "descripcion": "Usaste el bloque 'Repetir varias veces'.",
        "icono": "insignia", "condicion": _condicion_uso_repetir,
    },
    {
        "id": "uso_decision", "titulo": "Tomando decisiones",
        "descripcion": "Usaste el bloque 'Si pasa esto...'.",
        "icono": "insignia", "condicion": _condicion_uso_decision,
    },
    {
        "id": "racha_5", "titulo": "Sin errores",
        "descripcion": "Ejecutaste 5 programas seguidos sin ningun error.",
        "icono": "trofeo", "condicion": _condicion_sin_errores_5_seguidas,
    },
    {
        "id": "diez_programas", "titulo": "Programador constante",
        "descripcion": "Ejecutaste 10 programas en total.",
        "icono": "trofeo", "condicion": _condicion_diez_programas,
    },
    {
        "id": "uso_ia", "titulo": "Trabajo en equipo con la IA",
        "descripcion": "Le pediste ayuda a SiPi IA por primera vez.",
        "icono": "insignia", "condicion": _condicion_uso_ia,
    },
    {
        "id": "maestro_bloques", "titulo": "Explorador de bloques",
        "descripcion": "Usaste bloques 25 veces en total.",
        "icono": "trofeo", "condicion": _condicion_maestro_bloques,
    },
    {
        "id": "todos_los_bloques", "titulo": "¡Coleccion completa!",
        "descripcion": "Probaste todos los tipos de bloque al menos una vez.",
        "icono": "trofeo", "condicion": _condicion_todos_los_bloques,
    },
    {
        "id": "explorador_desafios", "titulo": "Buscador de ideas",
        "descripcion": "Miraste 5 desafios del dia distintos.",
        "icono": "insignia", "condicion": _condicion_explorador_desafios,
    },
]


def _evaluar_nuevos_logros(stats):
    """Revisa el catalogo completo y devuelve la lista de logros que
    recien se cumplieron en esta llamada (no estaban desbloqueados
    antes, y ahora la condicion da True). Los marca como desbloqueados
    en 'stats' antes de devolver."""
    nuevos = []
    for logro in CATALOGO_LOGROS:
        if logro["id"] in stats["logros_desbloqueados"]:
            continue
        try:
            cumplido = logro["condicion"](stats)
        except Exception:
            cumplido = False
        if cumplido:
            stats["logros_desbloqueados"].append(logro["id"])
            nuevos.append(logro)
    return nuevos


def registrar_evento(tipo, **datos):
    """Punto de entrada principal. tipo puede ser:
      - 'programa_ejecutado' (kwargs: ok=True/False)
      - 'programa_guardado'
      - 'bloque_usado' (kwargs: bloque="hablar")
      - 'pedido_ia'
      - 'desafio_visto'
    Devuelve la lista de logros nuevos desbloqueados por este evento
    (vacia la mayoria de las veces)."""
    stats = _cargar()

    if tipo == "programa_ejecutado":
        stats["programas_ejecutados"] += 1
        if datos.get("ok"):
            stats["programas_ejecutados_sin_error"] += 1
            stats["racha_sin_error"] += 1
        else:
            stats["racha_sin_error"] = 0
    elif tipo == "programa_guardado":
        stats["programas_guardados"] += 1
    elif tipo == "bloque_usado":
        nombre = datos.get("bloque", "desconocido")
        stats["bloques_usados"][nombre] = stats["bloques_usados"].get(nombre, 0) + 1
        if nombre not in stats["bloques_distintos_usados"]:
            stats["bloques_distintos_usados"].append(nombre)
    elif tipo == "pedido_ia":
        stats["pedidos_ia"] += 1
    elif tipo == "desafio_visto":
        stats["desafios_vistos"] = stats.get("desafios_vistos", 0) + 1

    nuevos = _evaluar_nuevos_logros(stats)
    _guardar(stats)
    return nuevos


# ------- Sistema de niveles (XP) -------
# Cada logro desbloqueado vale 100 XP. El nivel sube cada 250 XP -- no
# depende de contadores separados, se calcula siempre a partir de
# 'stats' para no tener que mantener dos fuentes de verdad sincronizadas.
XP_POR_LOGRO = 100
XP_POR_NIVEL = 250


def calcular_nivel(stats):
    """Devuelve (nivel, xp_total, xp_en_nivel_actual, xp_para_subir).
    Nivel 1 arranca en 0 XP. Pensado para mostrarse como una barra de
    progreso simple en la pantalla de logros."""
    xp_total = len(stats.get("logros_desbloqueados", [])) * XP_POR_LOGRO
    nivel = (xp_total // XP_POR_NIVEL) + 1
    xp_en_nivel_actual = xp_total % XP_POR_NIVEL
    return nivel, xp_total, xp_en_nivel_actual, XP_POR_NIVEL


def obtener_progreso():
    """Devuelve (logros_desbloqueados, logros_totales, stats) para
    mostrar una pantalla de progreso completa."""
    stats = _cargar()
    desbloqueados = [l for l in CATALOGO_LOGROS if l["id"] in stats["logros_desbloqueados"]]
    return desbloqueados, CATALOGO_LOGROS, stats


def reiniciar_progreso():
    _guardar(dict(ESTADISTICAS_POR_DEFECTO))
