"""
Mapeo fila CSV → datos para Vehicle.
Paridad Clarion LLENA_TRANSPORTE: en actualización, campo vacío en CSV usa valor existente del vehículo.
"""
from typing import Dict, Any, Optional, Union

from .common_validators import (
    MAX_LEN,
    parse_decimal,
    parse_insurance_date,
)


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 _get_existing_val(existing: Union[Any, Dict[str, Any]], key: str) -> Any:
    """Obtiene valor del vehículo existente (modelo ORM o dict)."""
    if existing is None:
        return None
    if isinstance(existing, dict):
        return existing.get(key)
    return getattr(existing, key, None)


def row_to_vehicle_data(row_norm: Dict[str, Any]) -> Dict[str, Any]:
    """Build dict suitable for VehicleCreateDTO / VehicleUpdateDTO from normalized row."""
    vehicle_key = _str_or_none(row_norm.get("CLAVE"), MAX_LEN["vehicle_key"])
    if not vehicle_key:
        return {}
    return {
        "vehicle_key": vehicle_key,
        "ace_vehicle_key": _str_or_none(row_norm.get("CLAVE ACE"), MAX_LEN["ace_vehicle_key"]),
        "transporter_key": _str_or_none(row_norm.get("CLAVE TRANSPORTE"), MAX_LEN["transporter_key"]),
        "series": _str_or_none(row_norm.get("VIN"), MAX_LEN["series"]),
        "transport_type": _str_or_none(row_norm.get("TIPO TRANSPORTE"), MAX_LEN["transport_type"]),
        "entity_code": _str_or_none(row_norm.get("CODIGO DE ENTIDAD"), MAX_LEN["entity_code"]),
        "transponder_number": _str_or_none(row_norm.get("TRANSPONDEDOR"), MAX_LEN["transponder_number"]),
        "dot_number": _str_or_none(row_norm.get("NUMERO DOT"), MAX_LEN["dot_number"]),
        "plate_number": _str_or_none(row_norm.get("PLACAS"), MAX_LEN["plate_number"]),
        "city": _str_or_none(row_norm.get("CIUDAD"), MAX_LEN["city"]),
        "state": _str_or_none(row_norm.get("ESTADO"), MAX_LEN["state"]),
        "country": _str_or_none(row_norm.get("PAIS"), MAX_LEN["country"]),
        "seal": _str_or_none(row_norm.get("PRECINTO"), MAX_LEN["seal"]),
        "insurance_company_name": _str_or_none(row_norm.get("EMPRESA ASEGURADORA"), MAX_LEN["insurance_company_name"]),
        "insurance_number": _str_or_none(row_norm.get("NUM. ASEGURADORA"), MAX_LEN["insurance_number"]),
        "insurance_amount": parse_decimal(row_norm.get("MONTO ASEGURADO") or row_norm.get("MONTO")),
        "insurance_date": parse_insurance_date(row_norm.get("FECHA DE ASEGURADORA") or row_norm.get("FECHA ASEGURADORA")),
    }


def row_to_vehicle_data_for_update(
    row_norm: Dict[str, Any],
    existing_vehicle: Union[Any, Dict[str, Any]],
) -> Dict[str, Any]:
    """
    Build dict for VehicleUpdateDTO: CSV value if non-empty, else existing vehicle value (Clarion VALIDA_PARCIAL / LLENA_TRANSPORTE).
    """
    vehicle_key = _str_or_none(row_norm.get("CLAVE"), MAX_LEN["vehicle_key"]) or _get_existing_val(existing_vehicle, "vehicle_key")
    if not vehicle_key:
        return {}

    def _csv_or_existing(csv_key: str, dto_key: str, max_len: Optional[int] = None):
        v = _str_or_none(row_norm.get(csv_key), max_len)
        if v is not None and v != "":
            return v
        return _get_existing_val(existing_vehicle, dto_key)

    def _csv_decimal_or_existing(csv_key: str, dto_key: str):
        raw = row_norm.get("MONTO ASEGURADO") or row_norm.get("MONTO") if csv_key == "MONTO ASEGURADO" else row_norm.get(csv_key)
        if raw is not None and str(raw).strip():
            d = parse_decimal(raw)
            if d is not None:
                return float(d)
        val = _get_existing_val(existing_vehicle, dto_key)
        if val is not None and hasattr(val, "__float__"):
            try:
                return float(val)
            except (TypeError, ValueError):
                pass
        return val

    def _csv_date_or_existing(csv_key: str, dto_key: str):
        raw = row_norm.get("FECHA DE ASEGURADORA") or row_norm.get("FECHA ASEGURADORA")
        if raw is not None and str(raw).strip():
            d = parse_insurance_date(raw)
            if d is not None:
                return d
        return _get_existing_val(existing_vehicle, dto_key)

    return {
        "vehicle_key": vehicle_key,
        "ace_vehicle_key": _csv_or_existing("CLAVE ACE", "ace_vehicle_key", MAX_LEN["ace_vehicle_key"]),
        "transporter_key": _csv_or_existing("CLAVE TRANSPORTE", "transporter_key", MAX_LEN["transporter_key"]),
        "series": _csv_or_existing("VIN", "series", MAX_LEN["series"]),
        "transport_type": _csv_or_existing("TIPO TRANSPORTE", "transport_type", MAX_LEN["transport_type"]),
        "entity_code": _csv_or_existing("CODIGO DE ENTIDAD", "entity_code", MAX_LEN["entity_code"]),
        "transponder_number": _csv_or_existing("TRANSPONDEDOR", "transponder_number", MAX_LEN["transponder_number"]),
        "dot_number": _csv_or_existing("NUMERO DOT", "dot_number", MAX_LEN["dot_number"]),
        "plate_number": _csv_or_existing("PLACAS", "plate_number", MAX_LEN["plate_number"]),
        "city": _csv_or_existing("CIUDAD", "city", MAX_LEN["city"]),
        "state": _csv_or_existing("ESTADO", "state", MAX_LEN["state"]),
        "country": _csv_or_existing("PAIS", "country", MAX_LEN["country"]),
        "seal": _csv_or_existing("PRECINTO", "seal", MAX_LEN["seal"]),
        "insurance_company_name": _csv_or_existing("EMPRESA ASEGURADORA", "insurance_company_name", MAX_LEN["insurance_company_name"]),
        "insurance_number": _csv_or_existing("NUM. ASEGURADORA", "insurance_number", MAX_LEN["insurance_number"]),
        "insurance_amount": _csv_decimal_or_existing("MONTO ASEGURADO", "insurance_amount"),
        "insurance_date": _csv_date_or_existing("FECHA DE ASEGURADORA", "insurance_date"),
    }
