import logging
logger = logging.getLogger(__name__)
from datetime import datetime
from decimal import Decimal
from typing import List

from sqlalchemy.orm import Session

from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceStatus
from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate
from core.exceptions import ErrorCollector
from .pre_validators import pre_validators
from .sub_process.review_exchange_rate import review_exchange_rate
from .sub_process.review_class import review_class
from .sub_process.review_qty_vs_weight import review_qty_vs_weight
from .sub_process.review_unit_cost import review_unit_cost
from .sub_process.series.review_qty_series import review_qty_series
from .sub_process.assign_values import assign_values
from .sub_process.assign_no_discharges import assign_no_discharges_items, assign_no_discharges_series

# Parameter Service
from api.v1.modules.a76.app_settings.service import AppSettingsService


from .sub_process.finalize_invoice import finalize_invoice_no_discharge, finalize_invoice_with_discharge
from .sub_process.download_balance_collector import collect_lines_to_discharge
from .sub_process.fill_available_balances import fill_available_balances
from .sub_process.compare_balances import compare_balances
from .sub_process.verify_consolidated import verify_consolidated

def pre_process(db: Session, invoice: InvoiceHeader, tenant_id: str, company_id: str, errors: ErrorCollector) -> list:
    """Validaciones previas y obtención de partidas."""
    lines = pre_validators(db, invoice, tenant_id, company_id, errors)
    if not lines:
        errors.add_error(field="line_items", message="La factura debe contener partidas.", solution=["Agregue partidas."], code="NO_LINE_ITEMS")
    return lines


def main_process(db: Session, invoice: InvoiceHeader, tenant_id: str, company_id: str, username: str = "SYSTEM") -> dict:
    """Proceso principal para facturas de exportación."""
    errors = ErrorCollector()
    lines = pre_process(db, invoice, tenant_id, company_id, errors)
    errors.raise_if_errors()

    review_class(db, lines, errors)
    
    # REVISA_CANT_KG (KGS) and REVISA_CANT_LB (LBS)
    review_qty_vs_weight(lines, "KGS", errors)
    review_qty_vs_weight(lines, "LBS", errors)
    
    # REVISA_LINEA_COSTO
    review_unit_cost(lines, errors)
    
    review_qty_series(db, invoice, lines, tenant_id, company_id, errors)
    errors.raise_if_errors()

    settings = AppSettingsService.get_resolved_settings(db, int(tenant_id), int(company_id))
    q_settings = settings.get("qsisgen", {})
    s_settings = settings.get("ssisgen", {})

    cal_val_base_tc = int(q_settings.get("calvalbasetcpedexpo") or s_settings.get("calvalbasetcpedexpo", 0))
    act_seguridad = int(q_settings.get("actseguridad") or s_settings.get("actseguridad") or 0)
    logger.info(f"AUDIT_DEBUG: act_seguridad resolve result = {act_seguridad} for Invoice={invoice.invoice_number}")

    # El TC ahora se resuelve dentro de assign_values (para per-line) 
    # o dentro de _assign_invoice_totals (para base-pedimento-global).
    # Sin embargo, para mantener compatibilidad con validaciones intermedias, lo dejamos aquí también:
    exchange_rate = invoice.financials.exchange_rate if invoice.financials else 0
    which_exchange_rate = "TCFAC"

    if cal_val_base_tc == 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:
                payment_date = ped.pedimento_dates.payment_date
                from sqlalchemy import select
                stmt = select(ExchangeRate).where(
                    ExchangeRate.tenant_id == int(tenant_id),
                    ExchangeRate.company_id == int(company_id),
                    ExchangeRate.date == payment_date.date())
                ex_rate_row = db.execute(stmt).scalar_one_or_none()
                if ex_rate_row:
                    exchange_rate = float(ex_rate_row.value)
                    which_exchange_rate = "TCPED"

    if invoice.compliance_mx: invoice.compliance_mx.which_exchange_rate = which_exchange_rate
    if invoice.financials: invoice.financials.exchange_rate = exchange_rate
    db.flush()

    review_exchange_rate(db, invoice, cal_val_base_tc, errors)
    errors.raise_if_errors()

    savepoint = db.begin_nested()
    try:
        invoice_type = (invoice.invoice_type or "").strip().upper()
        
        # 1. Lógica específica de NODES
        if invoice_type == "NODES":
            assign_no_discharges_items(lines, errors)
            assign_no_discharges_series(db, lines, errors)
        
        # 2. Asignar valores (costos/pesos) a las partidas
        assign_values(db, invoice, lines, tenant_id, company_id, cal_val_base_tc, errors)
        errors.raise_if_errors()

        # 3. Finalización (Límites, TC global, Auditoría, Descargas A24)
        if invoice_type == "NODES":
            finalize_invoice_no_discharge(db, invoice, lines, tenant_id, company_id, errors, username=username)
        else:
            # Lógica de descarga PEPS
            to_discharge = collect_lines_to_discharge(db, invoice, lines, errors)
            errors.raise_if_errors()

            if to_discharge:
                fill_available_balances(db, invoice, to_discharge, errors)
                errors.raise_if_errors()

                compare_balances(db, invoice, to_discharge, errors)
                errors.raise_if_errors()

                verify_consolidated(db, invoice, to_discharge, errors)
                errors.raise_if_errors()

            finalize_invoice_with_discharge(
                db, invoice, lines, tenant_id, company_id, errors, 
                to_discharge=to_discharge, username=username
            )
        
        errors.raise_if_errors()
        savepoint.commit()

    except Exception as e:
        savepoint.rollback()
        raise e

    db.flush()
    return {"status": "success", "invoice_id": str(invoice.id)}
