"""
Enriquecimiento de partidas de importación cargadas por CSV.
Aplica las mismas reglas que items/imports/validators (create + calculations)
en base a currency_type y peso de la factura; no re-sobrescribe con currency
para evitar discrepancias con el proceso manual.
"""
from decimal import Decimal
from sqlalchemy.orm import Session, joinedload

from api.v1.modules.a76.invoices.models import InvoiceHeader
from api.v1.modules.a76.classes.models import Class
from api.v1.modules.a76.parts.models import Part
from api.v1.modules.a76.general_catalogs.packages.models import Package
from api.v1.modules.sitar.fracciones_usa.catalog_resolve import (
    american_fraction_ad_valorem_from_row,
    resolve_american_fraction_from_sitar,
    store_canonical_american_code,
)
from api.v1.modules.a24.fa.fa_item_lines.dto import FaLineItemCreateDTO

from api.v1.modules.a76.items.schemas import LineItemCreate
from api.v1.modules.a76.items.imports.validators.calculations import (
    apply_calculations_after_values,
)


def _normalize_weight_type(invoice) -> str:
    """Obtiene weight_type de la factura como 'KGS' o 'LBS' (paridad con proceso manual)."""
    wt = getattr(invoice.logistics, "weight_type", None) or "KGS"
    if hasattr(wt, "value"):
        wt = wt.value
    weight_str = str(wt).upper() if wt else "KGS"
    return weight_str if weight_str in ("KGS", "LBS") else "KGS"


def apply_import_defaults_and_calculations_for_csv(
    db: Session,
    line_data: LineItemCreate,
    tenant_id: int,
    company_id: int,
    line_number: int,
) -> bool:
    """
    Aplica defaults y cálculos de partida de importación según reglas del proceso manual
    (currency_type, peso, descripciones). Se invoca tras armar LineItemCreate desde el CSV.
    Devuelve False si faltan factura/financials/logistics; True en caso contrario.
    """
    invoice: InvoiceHeader = (
        db.query(InvoiceHeader)
        .options(
            joinedload(InvoiceHeader.financials),
            joinedload(InvoiceHeader.logistics),
        )
        .filter(
            InvoiceHeader.id == line_data.invoice_id,
            InvoiceHeader.tenant_id == tenant_id,
            InvoiceHeader.company_id == company_id,
        )
        .first()
    )
    if not invoice or not invoice.financials or not invoice.logistics:
        return False

    class_info: Class | None = None
    if line_data.class_id:
        class_info = (
            db.query(Class)
            .filter(
                Class.id == line_data.class_id,
                Class.tenant_id == tenant_id,
                Class.company_id == company_id,
            )
            .first()
        )

    part: Part | None = None
    if line_data.part_number_id:
        part = (
            db.query(Part)
            .filter(
                Part.id == line_data.part_number_id,
                Part.tenant_id == tenant_id,
                Part.company_id == company_id,
            )
            .first()
        )

    if not line_data.fa_data:
        line_data.fa_data = FaLineItemCreateDTO(
            is_subitem=False,
            contains_subitems=False,
        )

    exchange_rate = invoice.financials.exchange_rate or Decimal("1.0")
    if not line_data.unit_of_measure and class_info:
        line_data.unit_of_measure = class_info.unit_of_measure

    # Reglas de moneda igual que proceso manual (create.py): currency_type
    currency_type = getattr(invoice.financials, "currency_type", None) or "USD"
    unit_cost_capture = line_data.financial.unit_cost_capture or Decimal("0")

    if currency_type in ("USD", "ME"):
        line_data.financial.unit_cost_capture = unit_cost_capture
        line_data.financial.unit_cost_usd = unit_cost_capture
        line_data.financial.unit_cost_mxn = unit_cost_capture * exchange_rate
    elif currency_type in ("MXN", "MN"):
        line_data.financial.unit_cost_capture = unit_cost_capture
        line_data.financial.unit_cost_usd = (
            unit_cost_capture / exchange_rate if exchange_rate else Decimal("0")
        )
        line_data.financial.unit_cost_mxn = unit_cost_capture
    else:
        line_data.financial.unit_cost_capture = unit_cost_capture
        line_data.financial.unit_cost_usd = unit_cost_capture
        line_data.financial.unit_cost_mxn = unit_cost_capture * exchange_rate

    quantity = line_data.quantity.quantity or Decimal("0")
    if line_data.financial.unit_cost_usd is not None:
        line_data.financial.value_usd = line_data.financial.unit_cost_usd * quantity
    if line_data.financial.unit_cost_mxn is not None:
        line_data.financial.value_mxn = line_data.financial.unit_cost_mxn * quantity
    line_data.financial.customs_value_usd = line_data.financial.value_usd
    line_data.financial.customs_value_mxn = line_data.financial.value_mxn
    line_data.financial.value_temp_material_usd = line_data.financial.value_usd
    line_data.financial.value_temp_material_mxn = line_data.financial.value_mxn

    # value_mc según currency de la factura (paridad con calculate_values manual)
    currency = getattr(invoice.financials, "currency", None)
    if currency == "foreign":
        line_data.financial.value_mc = line_data.financial.value_usd
    elif currency == "local":
        line_data.financial.value_mc = line_data.financial.value_usd
    elif currency == "manual":
        line_data.financial.value_mc = unit_cost_capture * quantity
    else:
        line_data.financial.value_mc = line_data.financial.value_usd

    # Peso: misma lógica que proceso manual (create.py) con weight_type normalizado
    weight_str = _normalize_weight_type(invoice)
    quantity = line_data.quantity.quantity or Decimal("0")
    net_weight_input = line_data.quantity.net_weight or Decimal("0")
    uom = line_data.unit_of_measure
    unit_is_kgs = uom is not None and (str(uom) == "24" or uom == 24)
    unit_is_lbs = uom is not None and (str(uom) == "25" or uom == 25)

    if unit_is_kgs:
        if weight_str == "KGS":
            line_data.quantity.net_weight = quantity
        else:
            line_data.quantity.net_weight = quantity * Decimal("2.204624")
    elif unit_is_lbs:
        if weight_str == "KGS":
            line_data.quantity.net_weight = quantity / Decimal("2.204624")
        else:
            line_data.quantity.net_weight = quantity
    else:
        if weight_str == "KGS":
            line_data.quantity.net_weight = net_weight_input
        else:
            line_data.quantity.net_weight = net_weight_input / Decimal("2.204624")

    gross_weight_input = line_data.quantity.gross_weight
    package_quantity = line_data.quantity.package_quantity or 0
    package_weight_unit = Decimal("0")

    if line_data.quantity.package_id:
        package = (
            db.query(Package)
            .filter(
                Package.id == line_data.quantity.package_id,
                Package.tenant_id == tenant_id,
                Package.company_id == company_id,
            )
            .first()
        )
        if package and package.weight_unit:
            package_weight_unit = package.weight_unit

    if not gross_weight_input or gross_weight_input == 0:
        if weight_str == "KGS":
            line_data.quantity.gross_weight = line_data.quantity.net_weight + (
                package_weight_unit * package_quantity
            )
        else:
            line_data.quantity.gross_weight = line_data.quantity.net_weight + (
                (package_weight_unit * Decimal("2.204624")) * package_quantity
            )
    else:
        if weight_str == "KGS":
            line_data.quantity.gross_weight = gross_weight_input
        else:
            line_data.quantity.gross_weight = gross_weight_input / Decimal("2.204624")

    if line_data.quantity.gross_weight < line_data.quantity.net_weight:
        line_data.quantity.gross_weight = line_data.quantity.net_weight + (
            package_weight_unit * package_quantity
        )

    if package_quantity and package_quantity > 0 and line_data.quantity.package_id:
        package = (
            db.query(Package)
            .filter(
                Package.id == line_data.quantity.package_id,
                Package.tenant_id == tenant_id,
                Package.company_id == company_id,
            )
            .first()
        )
        if package:
            line_data.description.package_description = package.description_es
    else:
        line_data.quantity.package_quantity = 0
        line_data.quantity.package_id = None
        line_data.description.package_description = None

    if not line_data.customs.american_fraction and class_info and class_info.us_fraction:
        line_data.customs.american_fraction = class_info.us_fraction

    if line_data.customs.american_fraction:
        resolved = resolve_american_fraction_from_sitar(
            str(line_data.customs.american_fraction)
        )
        if resolved:
            sitar_row, canon = resolved
            line_data.customs.american_fraction = store_canonical_american_code(canon)
            line_data.customs.advalorem_american = american_fraction_ad_valorem_from_row(
                sitar_row
            )

    # Fallback explícito: primero descripción de parte (si existe), luego clase.
    if not line_data.description.description_spanish and part and part.description_spanish:
        line_data.description.description_spanish = part.description_spanish
    if not line_data.description.description_english and part and part.description_english:
        line_data.description.description_english = part.description_english
    if not line_data.description.description_spanish and class_info:
        line_data.description.description_spanish = class_info.description_es
    if not line_data.description.description_english and class_info:
        line_data.description.description_english = class_info.description_en

    if line_data.description.brand:
        line_data.description.brand = line_data.description.brand.upper().strip()
    if line_data.description.model:
        line_data.description.model = line_data.description.model.upper().strip()

    # Solo depreciation_date, descripción part/class y subitem_number (sin re-calcular moneda)
    apply_calculations_after_values(db, line_data, tenant_id, company_id, line_number)
    return True
