"""
Helpers reutilizables para validación de filas CSV (clases de materiales).
"""
from typing import Dict, Any, Optional


def check_required(row: Dict[str, Any], col: str, line_num: int) -> Optional[Dict[str, Any]]:
    val = (row.get(col) or "").strip()
    if not val:
        return {"line": line_num, "col": col, "msg": "Requerido"}
    return None


def check_max_length(
    row: Dict[str, Any],
    col: str,
    max_len: int,
    line_num: int,
    required: bool = False,
) -> Optional[Dict[str, Any]]:
    val = (row.get(col) or "").strip()
    if not val:
        if required:
            return {"line": line_num, "col": col, "msg": "Requerido"}
        return None
    if len(val) > max_len:
        return {"line": line_num, "col": col, "msg": f"Máximo {max_len} caracteres"}
    return None


def check_min_length(
    row: Dict[str, Any],
    col: str,
    min_len: int,
    line_num: int,
    msg: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
    """Solo valida si hay valor; error si longitud menor que min_len."""
    val = (row.get(col) or "").strip()
    if not val:
        return None
    if len(val) < min_len:
        return {
            "line": line_num,
            "col": col,
            "msg": msg or f"Mínimo {min_len} caracteres",
        }
    return None


def check_int_range(
    row: Dict[str, Any],
    col: str,
    line_num: int,
    min_val: int,
    max_val: int,
) -> Optional[Dict[str, Any]]:
    """Solo valida si hay valor; devuelve error si no es int o está fuera de rango."""
    val = row.get(col)
    if val is None or val == "":
        return None
    try:
        v = int(val)
        if v < min_val or v > max_val:
            return {"line": line_num, "col": col, "msg": "Valor fuera de rango"}
    except (ValueError, TypeError):
        return {"line": line_num, "col": col, "msg": "Debe ser número entero"}
    return None


def check_in_set(
    row: Dict[str, Any],
    col: str,
    line_num: int,
    allowed: Optional[set],
    msg: str = "No existe en el catálogo",
) -> Optional[Dict[str, Any]]:
    """Solo valida si hay valor y allowed no es None."""
    val = (row.get(col) or "").strip()
    if not val or allowed is None:
        return None
    if val not in allowed:
        return {"line": line_num, "col": col, "msg": msg}
    return None


def check_decimal_max(
    row: Dict[str, Any],
    col: str,
    line_num: int,
    max_val: float,
    msg: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
    """Solo valida si hay valor; error si no es numérico o si es mayor que max_val (Clarion Col H)."""
    val = row.get(col)
    if val is None or val == "":
        return None
    try:
        v = float(val)
        if v > max_val:
            return {
                "line": line_num,
                "col": col,
                "msg": msg or f"El valor no puede ser mayor a {max_val}.",
            }
    except (ValueError, TypeError):
        return {"line": line_num, "col": col, "msg": "Debe ser un número."}
    return None
