"""
Validaciones CSV para Encabezados de Facturas de Importación Definitiva.
Paridad Clarion: VALIDA_TODA_FACIMPO_DEF, VALIDA_PARCIAL_FACIMPO_DEF, VALIDACIONES_FACIMPO_DEF.
Reutiliza de encabezados_impo_temp: claves, short names y validaciones de catálogos, transporte,
moneda, tipo peso y tipo cambio (misma lógica que TEM).
"""
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple
from .pedimento_resolution import resolve_pedimento_candidates

# Reutilizar del TEM: helpers y validaciones de catálogos (claves/short names), transporte, moneda, tipo peso, tipo cambio
from .encabezados_impo_temp import (
    _clip,
    _err,
    _get,
    _parse_int,
    _pedimento_key_from_parsed,
    _patente_from_agente_aduanal,
    _validaciones_catalogos,
    _validaciones_factura_longitud,
    _validaciones_moneda,
    _validaciones_tipo_cambio,
    _validaciones_tipo_peso,
    _validaciones_transporte,
)

# Impo Def: régimen único y pedimento formato ##-####-####### (15 chars)
REGIMEN_IMD = "IMD"
MAX_LEN_PEDIMENTO_DEF = 15
MAX_LEN_FACTURA = 15  # mismo que TEM


def parse_pedimento_col_a_impo_def(pedimento_str: str) -> Optional[Tuple[str, str, str]]:
    """
    Parsea Col A (PEDIMENTO) formato ##-####-####### (15 caracteres).
    Guiones en posiciones 3 y 8 (1-based): índices 2 y 7. Retorna (aduana_2, patente_4, numero_7) o None.
    Ejemplo válido: 01-1234-2312412
    """
    if not pedimento_str or not isinstance(pedimento_str, str):
        return None
    s = (pedimento_str or "").strip()
    if len(s) != MAX_LEN_PEDIMENTO_DEF:
        return None
    if s[2:3] != "-" or s[7:8] != "-":
        return None
    part0, part1, part2 = s[0:2], s[3:7], s[8:15]
    if not part0.isdigit() or not part1.isdigit() or not part2.isdigit():
        return None
    return (part0, part1, part2)


def _validaciones_obligatorios_toda_def(
    row: Dict[str, Any],
    line_num: int,
    tiene_pedimento: bool,
) -> Optional[Dict[str, Any]]:
    """Obligatorios VALIDA_TODA para Impo Def: C, D, F, G, H, I, J. Aduana de Cruce (AB) no obligatoria."""
    obligatorios: List[str] = []
    if not _get(row, "NUMERO FACTURA", "NUM FACTURA", "FACTURA"):
        obligatorios.append("(Col.C) Factura")
    if not _get(row, "FECHA FACTURA", "FECHA"):
        obligatorios.append("(Col.D) Fecha de la Factura")
    if not _get(row, "REGIMEN", "CLAVEDOCUMENTO"):
        obligatorios.append("(Col.F) Clave Régimen")
    if not _get(row, "CLAVE PROVEEDOR"):
        obligatorios.append("(Col.G) Clave del Proveedor")
    if not _get(row, "CLAVE VENDIDO A"):
        obligatorios.append("(Col.H) Clave del Vendido A")
    if not _get(row, "CLAVE ENVIADO A"):
        obligatorios.append("(Col.I) Clave del Enviado A")
    if not _get(row, "AGENTE ADUANAL"):
        obligatorios.append("(Col.J) Clave del Agente Aduanal")
    if obligatorios:
        return _err(
            line_num,
            "ARCHIVO CSV",
            f"Existen campos vacíos que son obligatorios: {', '.join(obligatorios)}. Revisar para Importación Definitiva.",
        )
    return None


def _validaciones_regimen_imd(row: Dict[str, Any], line_num: int) -> Optional[Dict[str, Any]]:
    """Col F: solo IMD válido para Impo Def."""
    f = _get(row, "REGIMEN", "CLAVEDOCUMENTO")
    if f and f.upper() != REGIMEN_IMD:
        return _err(
            line_num,
            "REGIMEN",
            f"Error: (Celda F{line_num}) El Régimen Aduanero: {f} no es válido para este tipo de movimiento. Los válidos son: IMD.",
        )
    return None


def _validaciones_pedimento_remesa_def(
    row: Dict[str, Any],
    line_num: int,
    pedimento_rows: List[Dict[str, Any]],
    remesa_por_pedimento_bd: Dict[str, Set[int]],
    remesa_por_pedimento_csv: Dict[str, Dict[int, str]],
    autonumerar_remesas: bool,
    control_remesa: bool,
    remesa_inicio: Optional[int],
    remesa_fin: Optional[int],
    invoice_date_parsed: Optional[datetime],
) -> Optional[Dict[str, Any]]:
    """Pedimento Col A: max 15 chars, formato ##-####-#######; catálogo con regime IMD. Remesa igual que TEM."""
    col_a = _get(row, "PEDIMENTO")
    col_b_raw = row.get("REMESA")
    col_b = _clip(col_b_raw)
    col_f = _get(row, "REGIMEN", "CLAVEDOCUMENTO").upper()

    if not col_a:
        if col_b:
            return _err(
                line_num,
                "REMESA",
                f"Error: (Celda B{line_num}) Está asignado el número de Remesa y no se tiene un pedimento en (Celda A{line_num}).",
            )
        return None

    if len(col_a) > MAX_LEN_PEDIMENTO_DEF:
        return _err(
            line_num,
            "PEDIMENTO",
            f"Error: (Celda A{line_num}) El Pedimento: {col_a} supera la longitud de caracteres. Use formato ##-####-#######.",
        )
    parsed = parse_pedimento_col_a_impo_def(col_a)
    if not parsed:
        return _err(
            line_num,
            "PEDIMENTO",
            f"Error: (Celda A{line_num}) El Formato del Pedimento: {col_a} es incorrecto. Use ##-####-#######.",
        )

    customs_office, license_val, pedimento_number = parsed
    key = _pedimento_key_from_parsed(customs_office, license_val, pedimento_number)
    patente_lookup = _patente_from_agente_aduanal(row, license_val)
    ped_resolved = resolve_pedimento_candidates(
        customs_office=customs_office,
        license_val=license_val,
        pedimento_number=pedimento_number,
        patente_lookup=patente_lookup,
        pedimento_rows=pedimento_rows,
    )
    if ped_resolved["status"] == "not_found":
        return _err(
            line_num,
            "PEDIMENTO",
            f"Error: (Celda A{line_num}) El Número de Pedimento: {col_a} no existe en el Catálogo de Pedimentos. "
            "Darlo de alta como pedimento de Importación Definitiva.",
        )
    if ped_resolved["status"] == "ambiguous":
        return _err(
            line_num,
            "PEDIMENTO",
            f"Error: (Celda A{line_num}) El Número de Pedimento: {col_a} coincide con múltiples registros. "
            "Valide aduana/patente para identificar un único pedimento.",
        )

    ped_info = ped_resolved["pedimento"]
    regimen_ped = (ped_info.get("regime") or "").strip().upper()
    if regimen_ped != REGIMEN_IMD:
        return _err(
            line_num,
            "PEDIMENTO",
            f"Error: (Celda A{line_num}) El Número de Pedimento: {col_a} tiene el Régimen {regimen_ped}, no válido para este tipo de movimiento. Válidos: IMD.",
        )
    if col_f and col_f != REGIMEN_IMD:
        return _err(
            line_num,
            "REGIMEN",
            f"Error: (Celda F{line_num}) El Régimen Aduanero: {col_f} no coincide con el del Pedimento. Debe ser IMD.",
        )
    if col_f and col_f != regimen_ped:
        return _err(
            line_num,
            "REGIMEN",
            f"Error: (Celda F{line_num}) El Régimen Aduanero: {col_f} capturado es diferente al del Pedimento: {regimen_ped}. Debe ser IMD.",
        )

    pedimento_type = (ped_info.get("pedimento_type") or "").strip().lower()
    if pedimento_type == "consolidated" and invoice_date_parsed and ped_info.get("entry_date") and ped_info.get("end_date"):
        entry = ped_info["entry_date"]
        end = ped_info["end_date"]
        if hasattr(entry, "date"):
            entry = entry.date()
        if hasattr(end, "date"):
            end = end.date()
        inv_d = invoice_date_parsed.date() if hasattr(invoice_date_parsed, "date") else invoice_date_parsed
        if inv_d < entry or inv_d > end:
            return _err(
                line_num,
                "FECHA FACTURA",
                f"Error: (Celda D{line_num} y A{line_num}) La Fecha de la Factura no corresponde al rango de fechas del Pedimento {col_a}.",
            )

    if not autonumerar_remesas and not col_b:
        return _err(
            line_num,
            "REMESA",
            f"Error: (Celda B{line_num}) El Número de Remesa está vacío y se tiene un Pedimento en la Celda A{line_num}.",
        )
    remesa_int = _parse_int(col_b_raw)
    if col_b and remesa_int is not None and remesa_int == 0:
        return _err(
            line_num,
            "REMESA",
            f"Error: (Celda B{line_num}) El Número de Remesa no puede ser 0.",
        )
    if control_remesa and remesa_int is not None and remesa_inicio is not None and remesa_fin is not None:
        if remesa_int < remesa_inicio or remesa_int > remesa_fin:
            return _err(
                line_num,
                "REMESA",
                f"Error: (Celda B{line_num}) El Número de Remesa: {col_b} está fuera del rango configurado ({remesa_inicio}-{remesa_fin}).",
            )

    factura_actual = _get(row, "NUMERO FACTURA", "NUM FACTURA", "FACTURA")
    if remesa_int is not None and key in remesa_por_pedimento_csv:
        other = remesa_por_pedimento_csv[key].get(remesa_int)
        if other and other != factura_actual:
            return _err(
                line_num,
                "REMESA",
                f"Error: (Celda B{line_num}) El Número de Remesa ya está asignado a la factura {other} en este archivo CSV.",
            )
    return None


def _normalize_tipo_transporte_ferro(row: Dict[str, Any]) -> Dict[str, Any]:
    """Clarion DEF acepta 'FERRO BARCAZA' (con espacio). Normaliza a FERROBARCAZA para reutilizar validación TEM."""
    out = dict(row)
    m = out.get("TIPO TRANSPORTE")
    if m is not None and str(m).strip().upper().replace(" ", "") == "FERROBARCAZA":
        out["TIPO TRANSPORTE"] = "FERROBARCAZA"
    return out


def validate_row_encabezados_impo_def(
    row: Dict[str, Any],
    line_num: int,
    actualizar: bool,
    invoice_exists_by_number: Dict[str, bool],
    invoice_processed_by_number: Dict[str, bool],
    pedimento_rows: List[Dict[str, Any]],
    remesa_por_pedimento_bd: Dict[str, Set[int]],
    remesa_por_pedimento_csv: Dict[str, Dict[int, str]],
    valid_provider_ids: Set[int],
    valid_sold_to_ids: Set[int],
    valid_shipped_to_ids: Set[int],
    valid_broker_ids: Set[int],
    valid_transporter_keys: Set[str],
    valid_incoterms: Set[str],
    valid_aduana_codes: Set[str],
    valid_currency_codes: Set[str],
    valid_provider_short_names: Optional[Set[str]] = None,
    valid_sold_to_short_names: Optional[Set[str]] = None,
    valid_shipped_to_short_names: Optional[Set[str]] = None,
    valid_broker_claves: Optional[Set[str]] = None,
    exchange_rate_by_date: Optional[Dict[str, Any]] = None,
    invoice_has_partidas_by_number: Optional[Dict[str, bool]] = None,
    existing_tipo_moneda_by_number: Optional[Dict[str, str]] = None,
    autonumerar_remesas: bool = False,
    control_remesa: bool = False,
    remesa_inicio: Optional[int] = None,
    remesa_fin: Optional[int] = None,
    date_format: Optional[str] = None,
    parse_date_fn=None,
    warnings: Optional[List[Dict[str, Any]]] = None,
) -> List[Dict[str, Any]]:
    """
    Valida una fila de CSV de Encabezados de Importación Definitiva.
    Clarion: VALIDA_TODA_FACIMPO_DEF vs VALIDA_PARCIAL_FACIMPO_DEF.
    Reutiliza de encabezados_impo_temp las validaciones de catálogos (claves y short names),
    transporte, moneda, tipo peso y tipo cambio.
    """
    errors: List[Dict[str, Any]] = []

    factura = _get(row, "NUMERO FACTURA", "NUM FACTURA", "FACTURA")
    if not factura:
        errors.append(_err(
            line_num,
            "NUMERO FACTURA",
            "Error: (Col.C) La columna de Número de Factura está vacía y no se pueden hacer las validaciones.",
        ))
        return errors

    if invoice_processed_by_number.get(factura.strip(), False):
        errors.append(_err(
            line_num,
            "NUMERO FACTURA",
            f"Error: (Celda C{line_num}) El Número de Factura: {factura} ya existe y está Actualizada, no se puede hacer cambios a las facturas actualizadas.",
        ))
        return errors

    if actualizar and factura.strip() not in invoice_exists_by_number:
        errors.append(_err(
            line_num,
            "NUMERO FACTURA",
            f"Error: (Col.C) Factura de importación Definitiva no existe (modo Actualizar).",
        ))
        return errors

    use_partial = actualizar and invoice_exists_by_number.get(factura.strip(), False)
    tiene_pedimento = bool(_get(row, "PEDIMENTO"))

    if not use_partial:
        err = _validaciones_obligatorios_toda_def(row, line_num, tiene_pedimento)
        if err:
            errors.append(err)

    invoice_date_parsed = None
    if parse_date_fn:
        date_str = _get(row, "FECHA FACTURA", "FECHA")
        if date_str:
            invoice_date_parsed = parse_date_fn(date_str, date_format)

    row_transport = _normalize_tipo_transporte_ferro(row)
    has_partidas = invoice_has_partidas_by_number.get(factura.strip(), False) if invoice_has_partidas_by_number else False
    existing_moneda = existing_tipo_moneda_by_number.get(factura.strip()) if existing_tipo_moneda_by_number else None

    for err in [
        _validaciones_pedimento_remesa_def(
            row,
            line_num,
            pedimento_rows,
            remesa_por_pedimento_bd,
            remesa_por_pedimento_csv,
            autonumerar_remesas,
            control_remesa,
            remesa_inicio,
            remesa_fin,
            invoice_date_parsed,
        ),
        _validaciones_factura_longitud(row, line_num),
        _validaciones_regimen_imd(row, line_num),
        _validaciones_transporte(row_transport, line_num),
        _validaciones_moneda(
            row,
            line_num,
            valid_currency_codes or set(),
            has_partidas if use_partial else None,
            existing_moneda if use_partial else None,
        ),
        _validaciones_tipo_peso(row, line_num),
        _validaciones_catalogos(
            row,
            line_num,
            valid_provider_ids or set(),
            valid_sold_to_ids or set(),
            valid_shipped_to_ids or set(),
            valid_provider_short_names or set(),
            valid_sold_to_short_names or set(),
            valid_shipped_to_short_names or set(),
            valid_broker_ids or set(),
            valid_broker_claves or set(),
            valid_transporter_keys or set(),
            valid_incoterms or set(),
            valid_aduana_codes or set(),
        ),
        _validaciones_tipo_cambio(
            row, line_num, invoice_date_parsed, exchange_rate_by_date or {}, warnings
        ),
    ]:
        if err:
            errors.append(err)

    return errors
