from sqlalchemy.orm import Session
from api.v1.modules.a76.invoices.models import InvoiceFinancials, InvoiceHeader
from core.exceptions import ErrorCollector

from ...models import LineItem
from api.v1.modules.a76.classes.models import Class
from api.v1.modules.a76.app_settings.service import AppSettingsService
from decimal import Decimal


def apply_calculations(
    db: Session, line: LineItem, tenant_id: int, company_id: int, line_number: int
):
    # 0. Obtener parámetros de configuración para validaciones dinámicas
    settings = AppSettingsService.get_resolved_settings(db, int(tenant_id), int(company_id))
    
    def find_in_obj(obj, target_key):
        if isinstance(obj, dict):
            for k, v in obj.items():
                if k.lower() == target_key.lower():
                    return v
                res = find_in_obj(v, target_key)
                if res is not None: return res
        elif hasattr(obj, '__dict__'):
            dict_rep = obj.__dict__
            for k, v in dict_rep.items():
                if k.startswith('_'): continue
                if k.lower() == target_key.lower():
                    return v
                res = find_in_obj(v, target_key)
                if res is not None: return res
        return None

    # Parámetro SCAF: Calcular costo unitario en base a valor total
    calc_costo_unit = find_in_obj(settings, 'calcularcostounitarioenbaseavalortotal') or \
                      find_in_obj(settings, 'calcularcostounitarioenbaseavalortotalscaf')

    if str(calc_costo_unit) == "1" and line.financial and (line.financial.unit_cost_capture or 0) == 0:
        qty = line.quantity.quantity or Decimal("0")
        if qty > 0:
            total_val = line.financial.value_usd or line.financial.value_mxn or Decimal("0")
            if total_val > 0:
                line.financial.unit_cost_capture = total_val / qty

    calculate_values(db, line, tenant_id, company_id)

    # ==========================================
    # VALORES POR DEFECTO DE PREFERENCIAS
    # ==========================================
    if line.tax_payment is None:
        pref_pago = find_in_obj(settings, 'pagoimpuesto')
        if pref_pago:
            line.tax_payment = True if str(pref_pago).lower() == 'si' else False

    if not line.payment_method:
        line.payment_method = find_in_obj(settings, 'formapago')

    if not line.valuation_method:
        line.valuation_method = find_in_obj(settings, 'metvalor')

    apply_calculations_after_values(db, line, tenant_id, company_id, line_number)


def apply_calculations_after_values(
    db: Session, line: LineItem, tenant_id: int, company_id: int, line_number: int
):
    """
    Aplica solo depreciation_date, descripción desde part/class y subitem_number.
    Usado por el flujo CSV para no sobrescribir los valores ya calculados por currency_type.
    """
    fa_data = getattr(line, "fa_data", None)
    if fa_data is not None and not getattr(fa_data, "is_subitem", True):
        fa_data.subitem_number = 0

    invoice_date = db.query(InvoiceHeader.invoice_date).filter(
        InvoiceHeader.id == line.invoice_id,
        InvoiceHeader.tenant_id == tenant_id,
        InvoiceHeader.company_id == company_id,
    ).scalar()
    if invoice_date is not None:
        line.depreciation_date = invoice_date

    if not getattr(line.description, "description_spanish", None) and not getattr(
        line.description, "description_english", None
    ):
        part_info = getattr(line, "part_info", None)
        if part_info and getattr(part_info, "description_spanish", None) and getattr(part_info, "description_english", None):
            line.description.description_spanish = part_info.description_spanish
            line.description.description_english = part_info.description_english
        else:
            if not getattr(line.description, "description_spanish", None):
                class_desc = (
                    db.query(Class.description_es, Class.description_en)
                    .filter(
                        Class.id == line.class_id,
                        Class.tenant_id == tenant_id,
                        Class.company_id == company_id,
                    )
                    .first()
                )
                if class_desc:
                    line.description.description_spanish, line.description.description_english = class_desc


def calculate_values(
    db: Session, line: LineItem, tenant_id: int, company_id: int
):
    """
    Una sola fuente de verdad: usa currency_type (USD/ME, MXN/MN) cuando está presente,
    para paridad con create.py y cargas CSV. Si no hay currency_type, usa currency (foreign/local/manual).
    """
    result = (
        db.query(
            InvoiceFinancials.currency,
            InvoiceFinancials.currency_type,
            InvoiceFinancials.exchange_rate,
        )
        .filter(
            InvoiceFinancials.invoice_id == line.invoice_id,
            InvoiceFinancials.tenant_id == tenant_id,
            InvoiceFinancials.company_id == company_id,
        )
        .first()
    )
    if not result:
        return

    currency, currency_type, exchange_rate = result
    
    # Safety guard: Ensure nested objects exist before calculating
    if not line.financial or not line.quantity:
        return
        
    # Prioridad: currency_type para alinear con create.py y CSV
    if currency_type in ("USD", "ME"):
        currency = "foreign"
    elif currency_type in ("MXN", "MN"):
        currency = "local"
    # si currency_type es otro o None, se usa currency tal cual

    qty = (
        line.quantity.quantity
        if line.quantity.quantity is not None
        else Decimal("0")
    )
    capture = (
        line.financial.unit_cost_capture
        if line.financial.unit_cost_capture is not None
        else Decimal("0")
    )

    if currency == "foreign":
        line.financial.unit_cost_usd = capture
        line.financial.value_usd = capture * qty
        line.financial.unit_cost_mxn = capture * (exchange_rate or 1)
        line.financial.value_mxn = line.financial.unit_cost_mxn * qty
        line.financial.value_mc = capture * qty
    elif currency == "local":
        line.financial.unit_cost_mxn = capture
        line.financial.value_mxn = line.financial.unit_cost_mxn * qty
        line.financial.unit_cost_usd = capture / (exchange_rate or 1)
        line.financial.value_usd = line.financial.unit_cost_usd * qty
        line.financial.value_mc = line.financial.unit_cost_usd * qty
    elif currency == "manual":
        line.financial.unit_cost_usd = capture / (exchange_rate or 1)
        line.financial.value_usd = line.financial.unit_cost_usd * qty
        line.financial.unit_cost_mxn = line.financial.unit_cost_usd * (exchange_rate or 1)
        line.financial.value_mxn = line.financial.unit_cost_mxn * qty
        line.financial.value_mc = capture * qty