"""
Generador de códigos de familia alfanuméricos (mismo formato que hec-photo-app).

Formato: {PREFIJO}-{3 caracteres}-{3 caracteres}   →  ej: MAR-P8Y-D23

Charset sin caracteres ambiguos (0/O/1/I/L), pensado para leerse a ojo y para que
el OCR de rescate del organizer pueda recuperar un código cuando el QR no
descodifica. 31^6 ≈ 887 millones de combinaciones por prefijo: adivinar el código
de otra familia deja de ser viable (el formato antiguo, 3 letras + 4 dígitos,
tenía solo 10 000 por prefijo).
"""
import logging
import random
import re
import unicodedata

logger = logging.getLogger(__name__)

CHARSET = "23456789ABCDEFGHJKMNPQRSTUVWXYZ"
MAX_RETRIES = 8
PREFIX_RE = re.compile(r"^[A-Z]{2,6}$")

# Formato nuevo con o sin prefijo, y el antiguo AAA-0000 (tarjetas ya repartidas).
CODE_RE = re.compile(r"^[A-Z0-9][A-Z0-9-]{1,23}$")


def _segment(length: int = 3) -> str:
    return "".join(random.SystemRandom().choices(CHARSET, k=length))


def build_code(prefix: str) -> str:
    """Código candidato sin verificar unicidad."""
    prefix = prefix.upper().strip("-")
    return f"{prefix}-{_segment()}-{_segment()}"


def normalize_code(raw: str) -> str:
    """'MAR-P8Y-D23' → 'MARP8YD23'. Para comparar sin guiones ni espacios."""
    return raw.upper().replace("-", "").replace(" ", "")


def format_code_display(code: str) -> str:
    """'MARP8YD23' → 'MAR-P8Y-D23' (lo que se enseña y se imprime)."""
    code = code.upper().replace(" ", "")
    if "-" in code:
        return code
    m = re.match(r"^([A-Z]{2,6})([23456789A-HJKMNPQRSTUVWXYZ]{3})([23456789A-HJKMNPQRSTUVWXYZ]{3})$", code)
    return f"{m.group(1)}-{m.group(2)}-{m.group(3)}" if m else code


def prefix_from_name(name: str) -> str:
    """Prefijo por defecto de un colegio: sus 3 primeras letras (sin acentos), MAYÚSCULAS."""
    base = "".join(c for c in unicodedata.normalize("NFD", name or "") if unicodedata.category(c) != "Mn")
    letters = "".join(c for c in base.upper() if "A" <= c <= "Z")
    return (letters[:3] or "FOT").ljust(3, "X")


def generate_unique_code(conn, prefix: str) -> str:
    """Código único (global: el código identifica familia Y colegio) contra `families`."""
    for attempt in range(1, MAX_RETRIES + 1):
        code = build_code(prefix)
        taken = conn.execute(
            "SELECT 1 FROM families WHERE code=? OR REPLACE(code,'-','')=?",
            (code, normalize_code(code))).fetchone()
        if not taken:
            return code
        logger.warning("Colisión de código en el intento %d: %s", attempt, code)
    raise RuntimeError(f"Could not generate a unique code with prefix {prefix} after {MAX_RETRIES} attempts")
