"""Dominio puro del modulo "Estequiometria" (4.1.5-4.1.7).

Sin dependencias de Flask. Define la clave de respuestas y la calificacion por
tema, con normalizacion de respuestas y descuento por incidencias.
"""

import re
from estequiometria_config import DESCUENTO_INCIDENTE, TEMAS

# Clave de respuestas correctas por tema.
#  - 4.1.5: Seleccion multiple (A/B/C/D).
#  - 4.1.6: Coeficientes de balanceo (numeros enteros separados por coma).
#  - 4.1.7: Resultados numericos (acepta decimales con punto).
#  - ACT: Ejercicios practicos (seleccion multiple).
CLAVE = {
    "4.1.5": {"0": "B", "1": "A", "2": "C", "3": "D", "4": "B"},
    "4.1.6": {
        "0": "1,3,2",      # N₂ + 3H₂ → 2NH₃
        "1": "4,3,2",      # 4Fe + 3O₂ → 2Fe₂O₃
        "2": "1,5,3,4",    # C₃H₈ + 5O₂ → 3CO₂ + 4H₂O
        "3": "2,2,2,1",    # 2Na + 2H₂O → 2NaOH + H₂
        "4": "1,2,1,1",    # Mg + 2HCl → MgCl₂ + H₂
        "5": "4,3,2",      # 4Al + 3O₂ → 2Al₂O₃
        "6": "1,1,1",      # CaCO₃ → CaO + CO₂
        "7": "2,7,4,6",    # 2C₂H₆ + 7O₂ → 4CO₂ + 6H₂O
        "8": "1,2,1,1",    # Zn + 2HCl → ZnCl₂ + H₂
        "9": "2,13,8,10",  # 2C₄H₁₀ + 13O₂ → 8CO₂ + 10H₂O
    },
    "4.1.7": {
        "0": "4",          # 4 mol H₂ → 4 mol H₂O
        "1": "4",          # 2 mol CH₄ → 4 mol O₂
        "2": "117",        # 80 g NaOH → 117 g NaCl
        "3": "324",        # 36 g H₂ → 324 g H₂O
        "4": "5",          # 2.5 mol C₂H₆ → 5 mol CO₂
        "5": "10.8",       # 18 g MgO → 10.8 g Mg
        "6": "34.6",       # 22 g CO → 34.6 g CO₂
        "7": "0.5",        # 0.25 mol N₂ → 0.5 mol NH₃
        "8": "302.2",      # 160 g Al → 302.2 g Al₂O₃
        "9": "7.45",       # 272 g HCl → 7.45 g H₂
    },
    "ACT": {"0": "C", "1": "A", "2": "B", "3": "D", "4": "C"},
}

_MAP_ACENTOS = str.maketrans(
    "áéíóúüñÁÉÍÓÚÜÑ", "aeiouunAEIOUUN"
)


def normalizar(valor):
    """Normaliza una respuesta: minusculas, sin acentos, sin espacios extra."""
    if not isinstance(valor, str):
        return ""
    texto = valor.replace(" ", "").replace("_", "")
    texto = texto.translate(_MAP_ACENTOS).lower()
    return texto.strip()


def normalizar_coeficientes(valor):
    """Normaliza coeficientes: elimina espacios, paréntesis, guiones y consolidar comas."""
    if not isinstance(valor, str):
        return ""
    texto = valor.strip()
    # Reemplazar guiones por comas
    texto = texto.replace('-', ',')
    # Eliminar parentesis
    texto = texto.replace('(', '').replace(')', '')
    # Eliminar todos los espacios
    texto = texto.replace(' ', '')
    # Consolidar comas múltiples en una sola
    texto = re.sub(r',+', ',', texto)
    # Eliminar comas al inicio y final
    texto = texto.strip(',')
    return texto.lower()


def normalizar_numero(valor):
    """Normaliza un resultado numerico: elimina espacios y unidades."""
    if not isinstance(valor, str):
        return ""
    texto = valor.strip()
    # Eliminar unidades al final (incluyendo g/mol, g, mol, etc.)
    texto = re.sub(r'\s*(g|mol|l|mL|kg|g/mol|mol/L)\s*$', '', texto, flags=re.IGNORECASE)
    texto = texto.replace(",", ".")
    return texto.strip()


def puntos_posibles(tema):
    """Puntos posibles del tema (segun TEMAS)."""
    return TEMAS[tema][2]


def calificar_tema(tema, respuestas):
    """Califica un tema y devuelve {puntos, posibles, nota_100}."""
    if tema not in CLAVE:
        raise ValueError(f"Tema desconocido: {tema}")
    clave = CLAVE[tema]
    posibles = puntos_posibles(tema)
    puntos = 0
    for indice, correcto in clave.items():
        valor = respuestas.get(indice)
        if valor is None:
            continue
        if tema == "4.1.6":
            if normalizar_coeficientes(valor) == normalizar_coeficientes(correcto):
                puntos += 1
        elif tema == "4.1.7":
            if normalizar_numero(valor) == normalizar_numero(correcto):
                puntos += 1
        else:
            if normalizar(valor) == normalizar(correcto):
                puntos += 1
    nota_100 = round(puntos / posibles * 100) if posibles else 0
    return {"puntos": puntos, "posibles": posibles, "nota_100": nota_100}


def nota_final(nota_100, n_incidentes):
    """Aplica descuento por incidencias a una nota sobre 100 (piso en 0)."""
    return max(0, nota_100 - DESCUENTO_INCIDENTE * n_incidentes)


def nota_modulo(respuestas_por_tema, n_incidentes):
    """Calcula la nota final unificada del modulo (sobre 100)."""
    total_puntos = 0
    total_posibles = 0
    for tema in TEMAS:
        resumen = calificar_tema(tema, respuestas_por_tema.get(tema, {}))
        total_puntos += resumen["puntos"]
        total_posibles += resumen["posibles"]
    if total_posibles == 0:
        return 0
    nota_bruta = round(total_puntos / total_posibles * 100)
    return nota_final(nota_bruta, n_incidentes)


def calificar_examen(respuestas_por_tema, n_incidentes):
    """Califica todos los temas y aplica el descuento global."""
    notas = {}
    notas_brutas = {}
    for tema in TEMAS:
        resumen = calificar_tema(tema, respuestas_por_tema.get(tema, {}))
        notas_brutas[tema] = resumen["nota_100"]
        notas[tema] = nota_final(resumen["nota_100"], n_incidentes)
    nota_final_modulo = nota_modulo(respuestas_por_tema, n_incidentes)
    return {
        "nota_final": nota_final_modulo,
        "notas": notas,
        "notas_brutas": notas_brutas,
        "incidentes": n_incidentes,
        "descuento_total": DESCUENTO_INCIDENTE * n_incidentes,
    }