import hashlib
import re
import unicodedata


VARIABLE_REFERENCE_PATTERN = re.compile(
    r"\{\{\s*([a-zA-Z][a-zA-Z0-9_.-]*)\s*\}\}|\[\s*([a-zA-Z][a-zA-Z0-9_.-]*)\s*\]"
)


NATIVE_VARIABLES = {
    "folio": "Folio público de la solicitud",
    "materia.nombre": "Nombre del trámite",
    "documento.fecha": "Fecha del documento en formato DD-MM-AAAA",
    "documento.fecha.larga": "Fecha larga del documento",
    "fecha.hoy": "Fecha actual en formato DD-MM-AAAA",
    "ot.fecha": "Fecha del documento en formato DD-MM-AAAA",
    "ot.fecha.larga": "Fecha larga del documento",
    "notaria.nombre": "Nombre de la entidad",
    "notaria.direccion": "Dirección de la entidad",
    "notaria.comuna": "Comuna de la entidad",
    "notaria.ciudad": "Ciudad de la entidad",
    "notaria.region": "Región de la entidad",
    "entidad.nombre": "Nombre de la entidad",
    "entidad.direccion": "Dirección de la entidad",
    "entidad.comuna": "Comuna de la entidad",
    "entidad.ciudad": "Ciudad de la entidad",
    "entidad.region": "Región de la entidad",
    "cliente.nombres": "Nombres del cliente",
    "cliente.apellidos": "Apellidos del cliente",
    "cliente.nombre_completo": "Nombre completo del cliente",
    "cliente.rut": "RUT del cliente",
    "cliente.email": "Correo del cliente",
    "cliente.telefono": "Teléfono del cliente",
    "campo.cliente_nombre_completo": "Nombre completo del cliente",
    "campo.cliente_rut": "RUT del cliente",
    "campo.cliente_email": "Correo del cliente",
    "campo.cliente_telefono": "Teléfono del cliente",
    "compareciente.1.nombre": "Nombre completo del cliente",
    "compareciente.1.nombreInformal": "Nombre completo del cliente",
    "compareciente.1.nombres": "Nombres del cliente",
    "compareciente.1.apellidos": "Apellidos del cliente",
    "compareciente.1.rut": "RUT del cliente",
    "compareciente.1.email": "Correo del cliente",
    "compareciente.1.telefono": "Teléfono del cliente",
}

RESERVED_PREFIXES = ("ot.", "notaria.", "entidad.", "documento.", "fecha.", "materia.")

LABEL_WORDS = {
    "rut": "RUT",
    "nombre": "nombre",
    "nombres": "nombres",
    "informal": "informal",
    "apellido": "apellido",
    "apellidos": "apellidos",
    "estado": "estado",
    "civil": "civil",
    "profesion": "profesión",
    "direccion": "dirección",
    "comuna": "comuna",
    "ciudad": "ciudad",
    "region": "región",
    "nacionalidad": "nacionalidad",
    "telefono": "teléfono",
    "correo": "correo",
    "email": "correo",
    "fecha": "fecha",
    "firma": "firma",
    "huella": "huella",
}


def extract_template_variables(content: str) -> list[str]:
    result: list[str] = []
    seen: set[str] = set()
    for match in VARIABLE_REFERENCE_PATTERN.finditer(content or ""):
        variable = match.group(1) or match.group(2)
        if variable not in seen:
            seen.add(variable)
            result.append(variable)
    return result


def _snake_case(value: str) -> str:
    value = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", value)
    normalized = unicodedata.normalize("NFKD", value)
    normalized = "".join(char for char in normalized if not unicodedata.combining(char))
    return re.sub(r"[^a-zA-Z0-9]+", "_", normalized).strip("_").lower()


def field_code_for_variable(variable: str) -> str:
    raw = variable[6:] if variable.startswith("campo.") else variable
    code = _snake_case(raw)
    if not code or not re.fullmatch(r"[a-z][a-z0-9_]*", code):
        raise ValueError("La variable no puede convertirse en un código de campo")
    if len(code) > 80:
        digest = hashlib.sha1(code.encode("utf-8")).hexdigest()[:8]
        code = f"{code[:71]}_{digest}"
    return code


def field_code_candidates(variable: str) -> list[str]:
    """Return compatible field codes, preferring the exact template path.

    Existing matters may use ``cliente_profesion`` or
    ``direccion_compareciente`` for a token such as
    ``compareciente.1.profesion``.  These aliases prevent duplicate fields and
    keep old catalogues usable by the new template syntax.
    """
    canonical = field_code_for_variable(variable)
    candidates = [canonical]
    parts = variable.split(".")
    if len(parts) >= 3 and parts[0] == "compareciente" and parts[1].isdigit():
        attribute = _snake_case("_".join(parts[2:]))
        candidates.extend((
            f"cliente_{attribute}",
            f"{attribute}_cliente",
            f"{attribute}_compareciente",
            f"compareciente_{attribute}",
        ))
    result: list[str] = []
    for code in candidates:
        if code not in result and len(code) <= 80:
            result.append(code)
    return result


def suggested_label(variable: str) -> str:
    parts = variable.split(".")
    suffix = parts
    qualifier = ""
    if len(parts) >= 3 and parts[0] == "compareciente" and parts[1].isdigit():
        suffix = parts[2:]
        qualifier = f" del compareciente {parts[1]}"
    elif parts[0] == "campo":
        suffix = parts[1:]
    words = _snake_case("_".join(suffix)).split("_")
    translated = [LABEL_WORDS.get(word, word) for word in words]
    label = " ".join(translated).strip() or "Campo de la plantilla"
    if label != "RUT":
        label = label[0].upper() + label[1:]
    return f"{label}{qualifier}"[:180]


def suggested_type(variable: str) -> str:
    code = field_code_for_variable(variable)
    tokens = set(code.split("_"))
    if "rut" in tokens:
        return "RUT"
    if tokens.intersection({"email", "correo"}):
        return "EMAIL"
    if tokens.intersection({"telefono", "celular", "fono"}):
        return "TELEFONO"
    if "comuna" in tokens:
        return "COMUNA"
    if "fecha" in tokens:
        return "FECHA"
    if tokens.intersection({"observacion", "descripcion", "detalle"}):
        return "TEXTO_LARGO"
    return "TEXTO"


def suggested_required(variable: str) -> bool:
    code = field_code_for_variable(variable)
    return not any(word in code.split("_") for word in ("firma", "huella"))


def analyze_template_variables(content: str, fields: list[dict]) -> dict:
    configured = {str(field["codigo"]): field for field in fields}
    variables = []
    counters = {"NATIVA": 0, "CAMPO_EXISTENTE": 0, "CAMPO_FALTANTE": 0,
                "CAMPO_INACTIVO": 0, "RESERVADA": 0}
    for variable in extract_template_variables(content):
        token = f"{{{{{variable}}}}}" if f"{{{{{variable}}}}}" in content else f"[{variable}]"
        if variable in NATIVE_VARIABLES:
            item = {"variable": variable, "token": token, "estado": "NATIVA",
                    "descripcion": NATIVE_VARIABLES[variable], "creable": False}
        elif variable.startswith(RESERVED_PREFIXES):
            item = {"variable": variable, "token": token, "estado": "RESERVADA",
                    "descripcion": "La familia es nativa, pero esta variable no está implementada.",
                    "creable": False}
        else:
            try:
                code = field_code_for_variable(variable)
            except ValueError:
                item = {"variable": variable, "token": token, "estado": "RESERVADA",
                        "descripcion": "La variable no puede convertirse en un campo seguro.",
                        "creable": False}
            else:
                field = next((configured.get(candidate) for candidate in field_code_candidates(variable)
                              if configured.get(candidate)), None)
                if field:
                    state = "CAMPO_EXISTENTE" if field.get("activo", True) else "CAMPO_INACTIVO"
                    matched_code = str(field.get("codigo") or code)
                    item = {"variable": variable, "token": token, "estado": state,
                            "descripcion": str(field.get("etiqueta") or matched_code), "codigo": matched_code,
                            "campo_id": field.get("id"), "creable": False}
                else:
                    item = {"variable": variable, "token": token, "estado": "CAMPO_FALTANTE",
                            "descripcion": "No existe un campo que solicite este dato.", "codigo": code,
                            "etiqueta_sugerida": suggested_label(variable),
                            "tipo_sugerido": suggested_type(variable),
                            "requerido_sugerido": suggested_required(variable), "creable": True}
        counters[item["estado"]] += 1
        variables.append(item)
    return {"variables": variables, "resumen": {"total": len(variables),
            "nativas": counters["NATIVA"], "existentes": counters["CAMPO_EXISTENTE"],
            "faltantes": counters["CAMPO_FALTANTE"], "inactivas": counters["CAMPO_INACTIVO"],
            "reservadas": counters["RESERVADA"]}}

