"""
Servicio de generacion de codigos QR de uso unico para un Grupo:
- N tokens+QR anonimos tipo ESTUDIANTE (N = grupo.cantidad_estudiantes, SIN
  ningun vinculo a identidad real, solo un numero secuencial de etiqueta).
- 1 token+QR de autoevaluacion para el docente (tipo AUTOEVALUACION)
- 1 token+QR para el director de carrera (tipo DIRECTOR)
"""
import io
import re
import zipfile

import qrcode
from django.conf import settings
from django.db import transaction

from evaluacion.models import EncuestaPlantilla, Token


class QRGenerationError(Exception):
    pass


def _slug(texto):
    texto = str(texto).strip().lower()
    texto = re.sub(r"[^a-z0-9]+", "-", texto)
    return texto.strip("-")


def _plantilla_activa(tipo):
    plantilla = EncuestaPlantilla.objects.filter(tipo=tipo, activa=True).order_by("-version").first()
    if plantilla is None:
        raise QRGenerationError(
            f"No hay una encuesta activa configurada para el tipo '{tipo}'. "
            f"Debe crearse/activarse una EncuestaPlantilla primero."
        )
    return plantilla


def imagen_qr_bytes(token_valor):
    url = f"{settings.FRONTEND_ENCUESTA_URL}?token={token_valor}"
    img = qrcode.make(url)
    buf = io.BytesIO()
    img.save(buf, format="PNG")
    buf.seek(0)
    return buf.read()


@transaction.atomic
def generar_qr_para_grupo(grupo, regenerar=False):
    if regenerar:
        grupo.tokens.all().delete()
    elif grupo.tokens.exists():
        raise QRGenerationError(
            "Este grupo ya tiene codigos QR generados. Use 'regenerar=true' para "
            "invalidar los anteriores y crear un nuevo lote."
        )

    plantilla_est = _plantilla_activa(EncuestaPlantilla.TIPO_ESTUDIANTE)
    plantilla_auto = _plantilla_activa(EncuestaPlantilla.TIPO_AUTOEVALUACION)
    plantilla_dir = _plantilla_activa(EncuestaPlantilla.TIPO_DIRECTOR)

    tokens_creados = []

    for i in range(1, grupo.cantidad_estudiantes + 1):
        tok = Token.objects.create(
            grupo=grupo, tipo=EncuestaPlantilla.TIPO_ESTUDIANTE, plantilla=plantilla_est,
            etiqueta=f"Estudiante {i:02d}",
        )
        tokens_creados.append(tok)

    tokens_creados.append(Token.objects.create(
        grupo=grupo, tipo=EncuestaPlantilla.TIPO_AUTOEVALUACION, plantilla=plantilla_auto,
        etiqueta="Autoevaluacion docente",
    ))
    tokens_creados.append(Token.objects.create(
        grupo=grupo, tipo=EncuestaPlantilla.TIPO_DIRECTOR, plantilla=plantilla_dir,
        etiqueta="Director de carrera",
    ))

    return {"grupo_id": grupo.id, "total_qr": len(tokens_creados)}


def generar_zip_de_grupo(grupo):
    prefijo = "_".join([
        _slug(grupo.asignatura.carrera.nombre), _slug(grupo.gestion.nombre),
        _slug(grupo.asignatura.nombre), _slug(grupo.codigo),
    ])

    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        for tok in grupo.tokens.all():
            nombre = f"{prefijo}/{tok.tipo}_{_slug(tok.etiqueta)}.png"
            zf.writestr(nombre, imagen_qr_bytes(tok.token))
    buf.seek(0)
    return buf, f"qr_{prefijo}.zip"
