from typing import Optional
from datetime import date
from decimal import Decimal

from core.exceptions import ErrorCollector
from ....schemas import InvoiceHeaderUpdate
from ....models import InvoiceHeader
from .common import validate_required_fields_by_operation


# Helper function para limpiar strings (equivalente a Clip())
def clean_str(value: Optional[str]) -> Optional[str]:
    if value is None or value == "":
        return None
    return value.strip()

def validate_update(
    invoice_data: InvoiceHeaderUpdate,
    existing_invoice: InvoiceHeader,
    errors: ErrorCollector,
) -> None:
    """
    Valida y procesa la actualización parcial de una factura de importación temporal.
    
    Lógica: Si un campo viene con valor, se limpia/valida.
           Si no, se mantiene el valor existente de la factura.
    
    Args:
        invoice_data: Datos de la factura a validar/actualizar (modificado in-place)
        existing_invoice: Factura existente en la base de datos
        errors: Colector de errores
    
    Returns:
        None (modifica invoice_data in-place y acumula errores en errors)
    """
    
    # Validar campos requeridos según el tipo de operación
    invoice_dict = {
        'provider_id': invoice_data.compliance_mx.provider_id if invoice_data.compliance_mx else (existing_invoice.compliance_mx.provider_id if existing_invoice.compliance_mx else None),
        'sold_to_id': invoice_data.compliance_mx.sold_to_id if invoice_data.compliance_mx else (existing_invoice.compliance_mx.sold_to_id if existing_invoice.compliance_mx else None),
        'sold_to_header': invoice_data.compliance_mx.sold_to_header if invoice_data.compliance_mx else (existing_invoice.compliance_mx.sold_to_header if existing_invoice.compliance_mx else None),
        'shipped_to_id': invoice_data.compliance_mx.shipped_to_id if invoice_data.compliance_mx else (existing_invoice.compliance_mx.shipped_to_id if existing_invoice.compliance_mx else None),
        'customs_broker_id': invoice_data.compliance_mx.customs_broker_id if invoice_data.compliance_mx else (existing_invoice.compliance_mx.customs_broker_id if existing_invoice.compliance_mx else None),
        'pedimento_id': invoice_data.compliance_mx.pedimento_id if invoice_data.compliance_mx else (existing_invoice.compliance_mx.pedimento_id if existing_invoice.compliance_mx else None),
    }
    
    validate_required_fields_by_operation(
        invoice_data=invoice_dict,
        operation_type=invoice_data.operation_type or (existing_invoice.operation_type or 'imp'),
        errors=errors
    )
    
    # Primero ejecutar validaciones comunes
    # validate_common(invoice_data, errors)

    # Mapeo de columnas CSV a campos de la factura
    # Siguiendo la lógica del código Clarion original
    
    # Columna A: Pedimento (si no viene en CSV, usar el existente)
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.pedimento_id:
            invoice_data.compliance_mx.pedimento_id = invoice_data.compliance_mx.pedimento_id
        else:
            invoice_data.compliance_mx.pedimento_id = existing_invoice.compliance_mx.pedimento_id if existing_invoice.compliance_mx else None
    
    # Columna B: Remesa
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.remesa:
            invoice_data.compliance_mx.remesa = invoice_data.compliance_mx.remesa
        else:
            invoice_data.compliance_mx.remesa = existing_invoice.compliance_mx.remesa if existing_invoice.compliance_mx else None
    
    # Columna C: Factura (OBLIGATORIO)
    if invoice_data.invoice_number is not None:
        invoice_data.invoice_number = clean_str(invoice_data.invoice_number)
        if not invoice_data.invoice_number:
            errors.add_required_error("invoice_number")
    else:
        invoice_data.invoice_number = existing_invoice.invoice_number
    
    # Columna D: Fecha
    if not invoice_data.invoice_date:
        invoice_data.invoice_date = existing_invoice.invoice_date
    
    # Columna E: Tipo Cambio
    if invoice_data.financials:
        if invoice_data.financials.exchange_rate is None:
            if existing_invoice.financials:
                invoice_data.financials.exchange_rate = existing_invoice.financials.exchange_rate
    
    # Columna F: Régimen
    if invoice_data.document_type:
        invoice_data.document_type = clean_str(invoice_data.document_type).upper()
    else:
        invoice_data.document_type = existing_invoice.document_type
    
    # Columna G: Clave Proveedor
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.provider_id is None:
            invoice_data.compliance_mx.provider_id = existing_invoice.compliance_mx.provider_id if existing_invoice.compliance_mx else None
    
    # Columna H: Clave Vendido A
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.sold_to_id is None:
            invoice_data.compliance_mx.sold_to_id = existing_invoice.compliance_mx.sold_to_id if existing_invoice.compliance_mx else None
    
    # Columna I: Clave Enviado A
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.shipped_to_id is None:
            invoice_data.compliance_mx.shipped_to_id = existing_invoice.compliance_mx.shipped_to_id if existing_invoice.compliance_mx else None
    
    # Columna J: Clave A. Aduanal
    if invoice_data.compliance_mx:
        if invoice_data.compliance_mx.customs_broker_id is None:
            invoice_data.compliance_mx.customs_broker_id = existing_invoice.compliance_mx.customs_broker_id if existing_invoice.compliance_mx else None
    
    # Columna K: Clave Transportista
    if invoice_data.logistics:
        # Note: logistics in update schema seems to be a single object, but in model it's a list.
        # This validator seems to expect a single object (InvoiceLogisticsUpdate).
        # We'll stick to the existing logic but make it safe.
        if hasattr(invoice_data.logistics, 'carrier_id') and invoice_data.logistics.carrier_id is None:
            invoice_data.logistics.carrier_id = existing_invoice.logistics.carrier_id if existing_invoice.logistics else None
    
    # Columna L: Nombre Conductor
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'driver_name') and not invoice_data.logistics.driver_name:
            invoice_data.logistics.driver_name = existing_invoice.logistics.driver_name if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'driver_name'):
             invoice_data.logistics.driver_name = clean_str(invoice_data.logistics.driver_name)
    
    # Columna M: Tipo Transporte
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'transport_type') and not invoice_data.logistics.transport_type:
            invoice_data.logistics.transport_type = existing_invoice.logistics.transport_type if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'transport_type'):
            invoice_data.logistics.transport_type = clean_str(invoice_data.logistics.transport_type)
    
    # Columna N: Número de Transporte
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'transport_num') and not invoice_data.logistics.transport_num:
            invoice_data.logistics.transport_num = existing_invoice.logistics.transport_num if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'transport_num'):
            invoice_data.logistics.transport_num = clean_str(invoice_data.logistics.transport_num)
    
    # Columna O: Tipo de Moneda
    if invoice_data.financials:
        if not invoice_data.financials.currency:
            invoice_data.financials.currency = existing_invoice.financials.currency if existing_invoice.financials else None
        else:
            invoice_data.financials.currency = clean_str(invoice_data.financials.currency).lower()
    
    # Columna P: Clave Moneda
    if invoice_data.financials:
        if not invoice_data.financials.currency_type:
            invoice_data.financials.currency_type = existing_invoice.financials.currency_type if existing_invoice.financials else None
        else:
            invoice_data.financials.currency_type = clean_str(invoice_data.financials.currency_type).upper()
    
    # Columna Q: Flete
    if invoice_data.financials:
        if invoice_data.financials.freight is None:
            invoice_data.financials.freight = existing_invoice.financials.freight if existing_invoice.financials else None
    
    # Columna R: Val Seguros
    if invoice_data.financials:
        if invoice_data.financials.insurance_value is None:
            invoice_data.financials.insurance_value = existing_invoice.financials.insurance_value if existing_invoice.financials else None
    
    # Columna S: Seguros
    if invoice_data.financials:
        if invoice_data.financials.insurance is None:
            invoice_data.financials.insurance = existing_invoice.financials.insurance if existing_invoice.financials else None
    
    # Columna T: Embalaje
    if invoice_data.financials:
        if invoice_data.financials.packaging is None:
            invoice_data.financials.packaging = existing_invoice.financials.packaging if existing_invoice.financials else None
    
    # Columna U: Otros Incrementables
    if invoice_data.financials:
        if invoice_data.financials.other_increments is None:
            invoice_data.financials.other_increments = existing_invoice.financials.other_increments if existing_invoice.financials else None
    
    # Columna V: Incoterms
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'incoterm') and not invoice_data.logistics.incoterm:
            invoice_data.logistics.incoterm = existing_invoice.logistics.incoterm if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'incoterm'):
            invoice_data.logistics.incoterm = clean_str(invoice_data.logistics.incoterm).upper()
    
    # Columna W: Precinto
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'seal_number') and not invoice_data.logistics.seal_number:
            invoice_data.logistics.seal_number = existing_invoice.logistics.seal_number if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'seal_number'):
            invoice_data.logistics.seal_number = clean_str(invoice_data.logistics.seal_number)
    
    # Columna X: Fecha de Emisión
    if not invoice_data.emission_date:
        invoice_data.emission_date = existing_invoice.emission_date
    
    # Columna Y: Tipo de Peso (Opcional)
    if invoice_data.logistics:
        if hasattr(invoice_data.logistics, 'weight_type') and not invoice_data.logistics.weight_type:
            invoice_data.logistics.weight_type = existing_invoice.logistics.weight_type if existing_invoice.logistics else None
        elif hasattr(invoice_data.logistics, 'weight_type'):
            invoice_data.logistics.weight_type = clean_str(invoice_data.logistics.weight_type).upper()
    
    # Columna Z: E-Document (Opcional)
    if invoice_data.compliance_mx:
        if not invoice_data.compliance_mx.edocument:
            invoice_data.compliance_mx.edocument = existing_invoice.compliance_mx.edocument if existing_invoice.compliance_mx else None
        else:
            invoice_data.compliance_mx.edocument = clean_str(invoice_data.compliance_mx.edocument)
    
    # Columna AA: Num. Operación (Opcional)
    if invoice_data.compliance_mx:
        if not invoice_data.compliance_mx.vucem_operation_num:
            invoice_data.compliance_mx.vucem_operation_num = existing_invoice.compliance_mx.vucem_operation_num if existing_invoice.compliance_mx else None
        else:
            invoice_data.compliance_mx.vucem_operation_num = clean_str(invoice_data.compliance_mx.vucem_operation_num)
    
    # Columna AB: Aduana (OBLIGATORIO)
    if invoice_data.compliance_mx:
        if not invoice_data.compliance_mx.aduana:
            invoice_data.compliance_mx.aduana = existing_invoice.compliance_mx.aduana if existing_invoice.compliance_mx else None
        else:
            invoice_data.compliance_mx.aduana = clean_str(invoice_data.compliance_mx.aduana)
    
    # Validar que aduana sea obligatorio (excepto para MEX)
    if existing_invoice.invoice_type != "MEX":
        current_aduana = invoice_data.compliance_mx.aduana if invoice_data.compliance_mx else (existing_invoice.compliance_mx.aduana if existing_invoice.compliance_mx else None)
        if not current_aduana:
            errors.add_required_error("aduana")
    
    # Columna AC: Sección de Despacho / Puerto de Entrada (Opcional)
    if invoice_data.compliance_mx:
        if not invoice_data.compliance_mx.port_of_entry:
            invoice_data.compliance_mx.port_of_entry = existing_invoice.compliance_mx.port_of_entry if existing_invoice.compliance_mx else None
        else:
            invoice_data.compliance_mx.port_of_entry = clean_str(invoice_data.compliance_mx.port_of_entry)
    
    # Columna AD: Observación en Español (Opcional)
    if not invoice_data.observation_es:
        invoice_data.observation_es = existing_invoice.observation_es
    else:
        invoice_data.observation_es = clean_str(invoice_data.observation_es)
    
    # Columna AD: Observación en Inglés (Opcional)
    if not invoice_data.observation_en:
        invoice_data.observation_en = existing_invoice.observation_en
    else:
        invoice_data.observation_en = clean_str(invoice_data.observation_en)

