from sqlalchemy import func
from sqlalchemy.orm import Session, joinedload
from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceStatus
from api.v1.modules.a76.items.models import LineItem
from api.v1.modules.a76.parts.models import Part
from api.v1.modules.a76.general_catalogs.fractions.warning_fractions.models import WarningFraction
from api.v1.modules.a76.clients_and_providers.models import ClientProvider
from api.v1.modules.a76.app_settings.service import AppSettingsService
from core.exceptions import ErrorCollector
from api.v1.modules.a76.invoices.common.common_validators import validate_invoice_items_decimals

def pre_validators(db: Session, invoice: InvoiceHeader, tenant_id: str, company_id: str,  errors: ErrorCollector):
    if invoice.status == InvoiceStatus.PROCESSED:
        errors.add_error(
            "status",
            "La factura ya fue procesada y no puede volver a actualizarse. Desactualícela primero.",
            solution=["Use el botón 'Desactualizar' antes de volver a procesar la factura."],
            code="ALREADY_PROCESSED",
            value=invoice.status,
        )
        errors.raise_if_errors()
        return

    if not invoice.invoice_date:
        errors.add_required_error("invoice_date")
        
    if invoice.invoice_type != "VEMEX":
        if not invoice.document_type:
            errors.add_required_error("document_type")
        
    if not invoice.compliance_mx.provider_id:
        errors.add_required_error("compliance_mx.provider_id")
    
    if not invoice.compliance_mx.sold_to_id:
        errors.add_required_error("compliance_mx.sold_to_id")  
        
    if not invoice.compliance_mx.shipped_to_id:
        errors.add_required_error("compliance_mx.shipped_to_id")
    else:
        shipped_to_exists = db.query(ClientProvider).filter(
            ClientProvider.id == invoice.compliance_mx.shipped_to_id,
            ClientProvider.tenant_id == tenant_id,
            ClientProvider.company_id == company_id,
        ).first()
        if not shipped_to_exists:
            errors.add_error(
                field="compliance_mx.shipped_to_id",
                message="El Destinatario no existe en el Catálogo de Clientes y Proveedores.",
                solution=["Verifica el ID del Destinatario", "Revisa el catálogo"],
                code="NOT_FOUND",
                value=invoice.compliance_mx.shipped_to_id,
            )
        if not shipped_to_exists.address or not shipped_to_exists.address.country:
            errors.add_error(
                field="compliance_mx.shipped_to_id",
                message="El Destinatario no tiene capturado el pais.",
                solution=["Captura el pais de envío del Destinatario", "Revisa el catálogo"],
                code="MISSING_COUNTRY",
                value=invoice.compliance_mx.shipped_to_id,
            )
    
    if invoice.invoice_type != "VEMEX":
        if not invoice.compliance_mx.customs_broker_id:
            errors.add_required_error("compliance_mx.customs_broker_id")    
                
    
    if not invoice.financials.exchange_rate or invoice.financials.exchange_rate <= 0:
        errors.add_range_error(
            "financials.exchange_rate",
            min_value=0.0001,
        )
       
    if not invoice.financials.currency:
        errors.add_required_error("El Tipo de Moneda esta vacio no se puede actualizar")        
    elif invoice.financials.currency == "manual" and not invoice.financials.currency_type:
        errors.add_required_error("financials.currency_type")
        
    # Validación de estatus de pedimento
    # Validation of Pedimento status (CERRADO / PAGADO)
    settings = AppSettingsService.get_resolved_settings(db, int(tenant_id), int(company_id))
    expo_params = settings.get("ssisexpo", {})
    # Resolver validarestatusped (podría estar en ssisexpo o qsisgen según el tipo de factura)
    valida_estatus = int(expo_params.get("validarestatusped", 0))

    if valida_estatus == 1:
        if invoice.compliance_mx and invoice.compliance_mx.pedimento:
            ped = invoice.compliance_mx.pedimento
            if ped.pedimento_dates and ped.pedimento_dates.payment_date:
                errors.add_error(
                    field="compliance_mx.pedimento",
                    message=f"No se puede procesar la factura porque el pedimento {ped.pedimento_number} ya se encuentra pagado el {ped.pedimento_dates.payment_date.date()}.",
                    solution=["Desactive el parámetro 'validarestatusped' o rectifique el pedimento si requiere cambios."],
                    code="PEDIMENTO_ALREADY_PAID"
                )
                errors.raise_if_errors()

    # 3.- Validacion que deber de existir un pedimento cuando es requerido
    if not invoice.compliance_mx.is_pedimento_pending and not invoice.compliance_mx.pedimento_id:
        errors.add_required_error("compliance_mx.pedimento_number")
        
    # 4.- Verificacion de que existan partidas para la factura, si no hay partidas no se puede procesar    
    item_count = (
        db.query(func.count(LineItem.id))
        .filter(
            LineItem.invoice_id == invoice.id,
            LineItem.tenant_id == invoice.tenant_id,
            LineItem.company_id == invoice.company_id,
        )
        .scalar()
    )
    if item_count == 0:
        errors.add_error(
            field="items",
            message="La factura no tiene partidas capturadas.",
            solution=["Capture al menos una partida antes de procesar la factura."],
            code="NO_ITEMS_FOUND",
        )
        
    # Advertencias para las fracciones y su horario
    lines = (
        db.query(LineItem)
        .options(
            joinedload(LineItem.fa_data),
            joinedload(LineItem.unit_of_measure_info),
            joinedload(LineItem.quantity),
            joinedload(LineItem.part_info).joinedload(Part.unit_of_measure_info)
        )
        .filter(
            LineItem.invoice_id == invoice.id,
            LineItem.tenant_id == tenant_id,
            LineItem.company_id == company_id,
        )
        .all()
    )
    
    # Validar decimales en piezas (Parámetro validadecencant)
    validate_invoice_items_decimals(db, lines, int(tenant_id), int(company_id), errors)
                
    return lines
    
            
        
        
        
        
        
    