"""
Mapeo fila CSV → datos para USTariffFraction.
"""
from decimal import Decimal
from typing import Dict, Any, Optional

from .common_validators import (
    normalize_code,
    parse_float_min_zero,
)

MAX_LEN = {"prefix": 10, "unit_of_measure": 10, "type_code": 10}


def _str_or_none(val: Any, max_len: Optional[int] = None) -> Optional[str]:
    if val is None:
        return None
    s = str(val).strip()
    if not s:
        return None
    if max_len and len(s) > max_len:
        return s[:max_len]
    return s


def row_to_us_tariff_fraction_data(
    row_norm: Dict[str, Any], tenant_id: int, company_id: int, existing: Optional[Any] = None
) -> Dict[str, Any]:
    """
    Build dict for USTariffFraction model.
    Si existing está presente (modo Actualizar), campos vacíos en row_norm se rellenan desde existing (VALIDA_PARCIAL Clarion).
    Col E vacío → type_code 'PO'.
    """
    code = normalize_code(row_norm.get("FRACCION_ARANCELARIA"))
    if not code:
        return {}

    def _prefijo() -> Optional[str]:
        v = _str_or_none(row_norm.get("PREFIJO"), MAX_LEN["prefix"])
        if v is not None:
            return v
        return getattr(existing, "prefix", None) if existing else None

    def _unit() -> Optional[str]:
        v = _str_or_none(row_norm.get("UNIDAD_DE_MEDIDA"), MAX_LEN["unit_of_measure"])
        if v is not None:
            return v
        return getattr(existing, "unit_of_measure", None) if existing else None

    def _desc() -> Optional[str]:
        v = _str_or_none(row_norm.get("DESCRIPCION"))
        if v is not None:
            return v
        return getattr(existing, "description", None) if existing else None

    def _type_code() -> Optional[str]:
        v = _str_or_none(row_norm.get("TIPO_DE_ADVALOREM"), MAX_LEN["type_code"])
        if v is not None:
            return v.upper() if v else "PO"
        if existing is not None:
            return getattr(existing, "type_code", None) or "PO"
        return "PO"

    def _adv() -> Optional[float]:
        x = parse_float_min_zero(row_norm.get("ADVALOREM_PCT"))
        if x is not None:
            return x
        return getattr(existing, "ad_valorem", None) if existing else None

    def _fixed() -> Optional[Decimal]:
        fixed_cost_raw = parse_float_min_zero(row_norm.get("ADVALOREM_DLLS"))
        if fixed_cost_raw is not None:
            return Decimal(str(round(fixed_cost_raw, 8)))
        if existing is not None:
            v = getattr(existing, "fixed_cost", None)
            return Decimal(str(v)) if v is not None else None
        return None

    fixed_cost = _fixed()
    return {
        "tenant_id": tenant_id,
        "company_id": company_id,
        "code": code,
        "prefix": _prefijo(),
        "type_code": _type_code(),
        "ad_valorem": _adv(),
        "fixed_cost": fixed_cost,
        "unit_of_measure": _unit(),
        "description": _desc(),
    }
