from decimal import Decimal
from typing import List

from sqlalchemy.orm import Session

from api.v1.modules.a76.invoices.models import Currency, InvoiceHeader
from api.v1.modules.a76.items.models import LineItem


def assign_values_lines(
    invoice: InvoiceHeader,
    lines: List[LineItem],
) -> None:
    """
    Calculates and assigns unit costs and values in all currency types for every
    line item, based on the invoice currency and exchange rates.

    Legacy returned/existence counters were removed from item_line_quantities;
    balances are now derived from a24.balance_movement and discharges.

    Currency mapping (legacy -> current enum):
        ME (moneda extranjera / foreign)  -> Currency.FOREIGN
        MN (moneda nacional / local)      -> Currency.LOCAL
        MC (moneda de cuenta / manual)    -> Currency.MANUAL
    """
    currency = invoice.financials.currency
    tc = Decimal(str(invoice.financials.exchange_rate or 0))
    tc_mm = Decimal(str(invoice.financials.exchange_rate_mm or 0))

    for line in lines:        
        if line.financial is None:
            continue

        capture = line.financial.unit_cost_capture or Decimal(0)
        qty = (line.quantity.quantity or Decimal(0)) if line.quantity else Decimal(0)

        if currency == Currency.FOREIGN:  # ME
            line.financial.unit_cost_usd = capture
            line.financial.value_usd = capture * qty
            line.financial.unit_cost_mxn = capture * tc
            line.financial.value_mxn = capture * tc * qty
            line.financial.value_mc = capture * qty
        elif currency == Currency.LOCAL:  # MN
            line.financial.unit_cost_mxn = capture
            line.financial.value_mxn = capture * qty
            line.financial.unit_cost_usd = (capture / tc) if tc else Decimal(0)
            line.financial.value_usd = (capture / tc * qty) if tc else Decimal(0)
            line.financial.value_mc = capture * qty
        elif currency == Currency.MANUAL:  # MC
            line.financial.unit_cost_usd = capture * tc_mm
            line.financial.value_usd = capture * tc_mm * qty
            line.financial.unit_cost_mxn = capture * tc_mm * tc
            line.financial.value_mxn = capture * tc_mm * tc * qty
            line.financial.value_mc = capture * qty

        # NOTE: no legacy returned/existence counters to reset.


def assign_values_invoice(
    invoice: InvoiceHeader,
    lines: List[LineItem],
) -> None:
    """
    Totals quantity, weight, and value fields from all line items and writes the
    aggregated results to invoice.financials.

    Ported from legacy ASIGNAVALORES_FACTURA routine.
    """
    total_quantity = Decimal(0)
    total_net_weight = Decimal(0)
    total_gross_weight = Decimal(0)
    total_packages = 0
    total_value_mxn = Decimal(0)
    total_value_usd = Decimal(0)
    total_value_mc = Decimal(0)

    for line in lines:
        if line.quantity:
            total_quantity += line.quantity.quantity or Decimal(0)
            total_net_weight += line.quantity.net_weight or Decimal(0)
            total_gross_weight += line.quantity.gross_weight or Decimal(0)
            total_packages += line.quantity.package_quantity or 0
        if line.financial:
            total_value_mxn += line.financial.value_mxn or Decimal(0)
            total_value_usd += line.financial.value_usd or Decimal(0)
            total_value_mc += line.financial.value_mc or Decimal(0)
    
    if invoice.financials is not None:
        invoice.financials.value_mn = float(total_value_mxn)
        invoice.financials.value_me = float(total_value_usd)
        invoice.financials.value_mc = float(total_value_mc)        
        invoice.financials.total_quantity = float(total_quantity)
        invoice.financials.net_weight = float(total_net_weight)
        invoice.financials.gross_weight = float(total_gross_weight)
        invoice.financials.total_packages = total_packages
