"""
Helpers reutilizables para validación de filas CSV (BOMs).
"""
import re
from decimal import Decimal, InvalidOperation
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_decimal_required_min(
    row: Dict[str, Any], col: str, line_num: int, min_val: Decimal
) -> Optional[Dict[str, Any]]:
    val = row.get(col)
    if val is None or val == "":
        return {"line": line_num, "col": col, "msg": "Requerido"}
    try:
        v = Decimal(str(val))
        if v < min_val:
            return {"line": line_num, "col": col, "msg": "Debe ser mayor o igual a cero"}
    except (InvalidOperation, ValueError, TypeError):
        return {"line": line_num, "col": col, "msg": "Debe ser número"}
    return None


def _optional_number(val: Any) -> bool:
    if val is None:
        return True
    s = re.sub(r"\s+", "", str(val).strip())
    if not s:
        return True
    try:
        float(s.replace(",", "."))
        return True
    except (ValueError, TypeError):
        return False


def check_optional_number(row: Dict[str, Any], col: str, line_num: int) -> Optional[Dict[str, Any]]:
    if not _optional_number(row.get(col)):
        return {"line": line_num, "col": col, "msg": "Debe ser número"}
    return None


def check_in_set(
    row: Dict[str, Any],
    col: str,
    line_num: int,
    allowed: Optional[set],
    msg: str,
) -> Optional[Dict[str, Any]]:
    val = (row.get(col) or "").strip()
    if not val or allowed is None or len(allowed) == 0:
        return None
    if val not in allowed:
        return {"line": line_num, "col": col, "msg": msg}
    return None
