"""
Mapeo fila CSV → datos para PedimentosCreate.
Soporta layout Clarion (PEDIMENTO, TIPO_OPERACION, CLAVE_PEDIMENTO, etc.) y legacy (AÑO, ADUANA, PATENTE, NUMERO).
"""
from decimal import Decimal, InvalidOperation
from typing import Dict, Any, Optional

from ..template_config import is_clarion_layout, parse_pedimento_col_a
from ..common.common_validators import parse_date


def parse_decimal(val: Any) -> Optional[Decimal]:
    if val is None or (isinstance(val, str) and not val.strip()):
        return None
    try:
        return Decimal(str(val).strip().replace(",", "."))
    except (InvalidOperation, ValueError):
        return None


def resolve_client_id_from_short_name(
    valor: str, short_name_to_id: Dict[str, int]
) -> Optional[int]:
    """Resuelve short_name (string) a client_id. Vacío → None. Misma normalización que fk_loader (strip + upper)."""
    if not valor or not str(valor).strip():
        return None
    key = str(valor).strip().upper()
    return short_name_to_id.get(key)


def _row_to_pedimento_data_clarion(
    row_norm: Dict[str, Any],
    short_name_to_id: Dict[str, int],
    date_format_preference: Optional[str] = None,
) -> Dict[str, Any]:
    """Desde layout Clarion: Cols 1-3 (AÑO, PATENTE, NUMERO) o Col A (PEDIMENTO); Col H → customs_office; B,C,D,J y fechas. CLIENTE_SHORT_NAME opcional."""
    año = (row_norm.get("AÑO") or "").strip()
    patente = (row_norm.get("PATENTE") or "").strip()
    numero = (row_norm.get("NUMERO") or "").strip()
    if año and patente and numero:
        year = año[:2]
        license_val = patente[:4]
        pedimento_number = numero[:7]
    else:
        ped = (row_norm.get("PEDIMENTO") or "").strip()
        parsed = parse_pedimento_col_a(ped)
        if not parsed:
            raise ValueError("PEDIMENTO inválido o vacío")
        year, license_val, pedimento_number = parsed
    aduana = (row_norm.get("ADUANA_SECCION_CRUCE") or "").strip()[:3]
    clave = (row_norm.get("CLAVE_PEDIMENTO") or "").strip()[:2]
    regime = (row_norm.get("REGIMEN") or "").strip()[:3]
    client_short_name = (row_norm.get("CLIENTE_SHORT_NAME") or "").strip()
    client_id = resolve_client_id_from_short_name(client_short_name, short_name_to_id)

    data: Dict[str, Any] = {
        "year": year,
        "customs_office": aduana,
        "license": license_val,
        "pedimento_number": pedimento_number,
        "pedimento_code": clave,
        "regime": regime,
    }
    if client_id is not None:
        data["client_id"] = client_id

    tipo = (row_norm.get("TIPO_OPERACION") or "").strip().upper()
    if tipo == "I":
        data["operation_type"] = "imp"
    elif tipo == "E":
        data["operation_type"] = "exp"

    ind_con = (row_norm.get("INDIVIDUAL_CONSOLIDADO") or "").strip().upper()
    if ind_con == "IND":
        data["pedimento_type"] = "normal"
    elif ind_con == "CON":
        data["pedimento_type"] = "consolidated"

    status = (row_norm.get("ESTATUS") or "").strip()
    if status:
        data["status"] = status[:30]

    data["usd_value"] = parse_decimal(row_norm.get("VALOR_USD"))
    data["paid_price"] = parse_decimal(row_norm.get("PRECIO_PAGADO"))
    data["gross_weight"] = parse_decimal(row_norm.get("PESO_BRUTO"))
    data["exchange_rate"] = parse_decimal(row_norm.get("TIPO_CAMBIO"))
    obs = (row_norm.get("OBSERVACIONES") or "").strip()
    if obs:
        data["observations"] = obs

    # Fechas E, F, G → pedimento_dates
    # Paridad legacy: Col.G es la fecha de referencia operativa (pago/entrada).
    start_str = (row_norm.get("FECHA_INICIO") or "").strip()
    end_str = (row_norm.get("FECHA_FINAL") or "").strip()
    payment_str = (row_norm.get("FECHA_PAGO") or "").strip()
    start_dt = parse_date(start_str, date_format_preference) if start_str else None
    end_dt = parse_date(end_str, date_format_preference) if end_str else None
    payment_dt = parse_date(payment_str, date_format_preference) if payment_str else None
    if start_dt and end_dt:
        entry_dt = payment_dt or start_dt
        data["pedimento_dates"] = {
            "entry_date": entry_dt,
            "end_date": end_dt,
            "start_date": start_dt,
            "payment_date": payment_dt or entry_dt,
        }
    return data


def _row_to_pedimento_data_legacy(
    row_norm: Dict[str, Any], short_name_to_id: Dict[str, int]
) -> Dict[str, Any]:
    """Layout legacy: AÑO, ADUANA, PATENTE, NUMERO, CLIENTE_SHORT_NAME, CODIGO_PEDIMENTO/CLAVE_PEDIMENTO, REGIMEN."""
    year = (row_norm.get("AÑO") or "").strip()[:2]
    customs_office = (row_norm.get("ADUANA_SECCION_CRUCE") or row_norm.get("ADUANA") or "").strip()[:3]
    license_val = (row_norm.get("PATENTE") or "").strip()[:4]
    pedimento_number = (row_norm.get("NUMERO") or "").strip()[:7]
    client_short_name = (row_norm.get("CLIENTE_SHORT_NAME") or "").strip()
    client_id = resolve_client_id_from_short_name(client_short_name, short_name_to_id)
    pedimento_code = (row_norm.get("CLAVE_PEDIMENTO") or row_norm.get("CODIGO_PEDIMENTO") or "").strip()[:2]
    regime = (row_norm.get("REGIMEN") or "").strip()[:3]

    data = {
        "year": year,
        "customs_office": customs_office,
        "license": license_val,
        "pedimento_number": pedimento_number,
        "client_id": client_id,
        "pedimento_code": pedimento_code,
        "regime": regime,
    }

    op = (row_norm.get("TIPO_OPERACION") or "").strip().lower()
    if op in ("imp", "exp"):
        data["operation_type"] = op
    ptype = (row_norm.get("TIPO_PEDIMENTO") or "").strip().lower()
    if ptype in ("normal", "consolidated", "complementary", "automobile"):
        data["pedimento_type"] = ptype
    status = (row_norm.get("ESTATUS") or "").strip()
    if status:
        data["status"] = status[:30]
    data["usd_value"] = parse_decimal(row_norm.get("VALOR_USD"))
    data["paid_price"] = parse_decimal(row_norm.get("PRECIO_PAGADO"))
    data["gross_weight"] = parse_decimal(row_norm.get("PESO_BRUTO"))
    data["exchange_rate"] = parse_decimal(row_norm.get("TIPO_CAMBIO"))
    obs = (row_norm.get("OBSERVACIONES") or "").strip()
    if obs:
        data["observations"] = obs
    return data


def row_to_pedimento_data(
    row_norm: Dict[str, Any],
    short_name_to_id: Dict[str, int],
    date_format_preference: Optional[str] = None,
) -> Dict[str, Any]:
    """
    Build dict for PedimentosCreate from normalized CSV row.
    Usa layout Clarion si existe PEDIMENTO; si no, layout legacy. Cliente por short_name → client_id.
    """
    if is_clarion_layout(row_norm):
        return _row_to_pedimento_data_clarion(
            row_norm, short_name_to_id, date_format_preference
        )
    return _row_to_pedimento_data_legacy(row_norm, short_name_to_id)


def row_to_pedimento_data_merge_existing(
    row_norm: Dict[str, Any],
    existing: Dict[str, Any],
    short_name_to_id: Dict[str, int],
    date_format_preference: Optional[str] = None,
) -> Dict[str, Any]:
    """
    Para modo actualizar: campos vacíos en la fila se rellenan con el pedimento existente.
    """
    if is_clarion_layout(row_norm):
        new_data = _row_to_pedimento_data_clarion(
            row_norm, short_name_to_id, date_format_preference
        )
    else:
        new_data = _row_to_pedimento_data_legacy(row_norm, short_name_to_id)

    for key, val in new_data.items():
        if val is None or (isinstance(val, str) and not val.strip()):
            if key in existing and existing[key] is not None:
                new_data[key] = existing[key]
    if not new_data.get("pedimento_dates") and existing.get("pedimento_dates"):
        new_data["pedimento_dates"] = existing["pedimento_dates"]
    return new_data
