"""
Validadores reutilizables para import CSV de fracciones arancelarias americanas.
Paridad Clarion: Col A max 16, Col C en catálogo U.M., Col E PO/ME o vacío (default PO).
"""
from decimal import Decimal
from typing import Dict, Any, Optional, Set

CODE_MAX = 16
PREFIX_MAX = 10
UNIT_MAX = 10
TYPE_MAX = 10

# Clarion: Tipo Advalorem PO (Porcentaje), ME (Costos Fijo Dlls) o vacío → PO
TIPO_ADVALOREM_VALIDOS = frozenset({"PO", "ME"})


def normalize_code(raw: Optional[str]) -> str:
    """Normalize fraction code: strip and remove dots/dashes, max 16 chars."""
    if not raw:
        return ""
    s = str(raw).strip().replace(".", "").replace("-", "")
    return s[:CODE_MAX] if len(s) > CODE_MAX else s


def check_required_code(row: Dict[str, Any], col: str, line_num: int) -> Optional[Dict[str, Any]]:
    code_raw = (row.get(col) or "").strip()
    if not code_raw:
        return {"line": line_num, "col": col, "msg": "Requerido"}
    code_norm = normalize_code(code_raw)
    if not code_norm:
        return {"line": line_num, "col": col, "msg": "Requerido"}
    if len(code_norm) > CODE_MAX:
        return {"line": line_num, "col": col, "msg": f"Máximo {CODE_MAX} caracteres"}
    return None


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


def parse_float_min_zero(val: Any) -> Optional[float]:
    if val is None or str(val).strip() == "":
        return None
    try:
        v = float(str(val).strip().replace(",", "."))
        return v if v >= 0 else None
    except (ValueError, TypeError):
        return None


def check_optional_decimal_min_zero(row: Dict[str, Any], col: str, line_num: int) -> Optional[Dict[str, Any]]:
    val = row.get(col)
    if val is None or not str(val).strip():
        return None
    try:
        v = float(str(val).strip().replace(",", "."))
        if v < 0:
            return {"line": line_num, "col": col, "msg": "Debe ser >= 0"}
    except ValueError:
        return {"line": line_num, "col": col, "msg": "Debe ser un número"}
    return None


def check_tipo_advalorem(row: Dict[str, Any], col: str, line_num: int) -> Optional[Dict[str, Any]]:
    """Col E: solo PO, ME o vacío. Clarion mensaje."""
    val = (row.get(col) or "").strip().upper()
    if not val:
        return None
    if val in TIPO_ADVALOREM_VALIDOS:
        return None
    return {
        "line": line_num,
        "col": col,
        "msg": (
            f"Error: (Col. E) La opción de Tipo de Advalorem: {row.get(col) or ''} no es valido. "
            "Capturar una opción valida: PO para Porcentaje, ME para Costos Fijo en Dlls o dejar el campo vacio y automaticamente se asigna Porcentaje."
        ),
    }
