from datetime import date
from decimal import Decimal

from api.v1.modules.a24.balance_movements.models import BalanceMovement, MovementType
from api.v1.modules.a76.invoices.exports.process.sub_process.compare_balances import compare_balances
from api.v1.modules.a76.invoices.exports.process.sub_process.discharge_types import (
    AvailableLot,
    DownloadEntry,
)
from api.v1.modules.a76.invoices.exports.process.sub_process.fill_available_balances import (
    _net_balance_for_lot,
)
from core.exceptions import ErrorCollector
from tests.fixtures.builders import (
    create_business_catalogs,
    create_import_invoice_with_line,
    ensure_reference_data,
)


def test_net_balance_accounts_for_returns_and_entry_void(db_session, test_tenant):
    """
    Balance neto esperado:
      entry 10 - consumption 4 + return 1 - entry_void 2 = 5
    """
    tid, cid = test_tenant.tenant_id, test_tenant.company_id
    ensure_reference_data(db_session)
    catalogs = create_business_catalogs(db_session, tenant_id=tid, company_id=cid)
    import_invoice, import_line = create_import_invoice_with_line(
        db_session,
        tid,
        cid,
        catalogs,
        invoice_type="TEM",
        invoice_number="IMP-UNIT-01",
        qty=Decimal("10"),
    )

    line_id = import_line.id
    tenant_id = import_invoice.tenant_id
    company_id = import_invoice.company_id
    import_invoice_id = import_invoice.id
    for idx, (mtype, qty) in enumerate(
        [
            (MovementType.ENTRY, Decimal("10")),
            (MovementType.CONSUMPTION, Decimal("4")),
            (MovementType.RETURN, Decimal("1")),
            (MovementType.ENTRY_VOID, Decimal("2")),
        ],
        start=1,
    ):
        db_session.add(
            BalanceMovement(
                id=10000 + idx,
                tenant_id=tenant_id,
                company_id=company_id,
                import_invoice_id=import_invoice_id,
                import_item_line_id=line_id,
                movement_type=mtype,
                quantity=qty,
                order_peps=10000 + idx,
                operation_date=date(2026, 3, 20),
            )
        )
    db_session.flush()

    balance = _net_balance_for_lot(db_session, line_id, date(2026, 3, 21))
    assert balance == Decimal("5")


def test_fifo_consumption_algorithm_uses_oldest_lots_first(db_session, monkeypatch):
    """
    Verifica distribución FIFO (PEPS):
    - lote1 (order=1, qty=3), lote2 (order=2, qty=5), demanda=6
    - resultado: lote1 consume 3, lote2 consume 3, faltante 0
    """
    monkeypatch.setattr(
        "api.v1.modules.a76.invoices.exports.process.sub_process.compare_balances._resolve_import_uom",
        lambda db, import_item_line_id: "PZA",
    )

    entry = DownloadEntry(
        origin_procedure="TEM",
        export_line=1,
        part_number="PN-1",
        class_code="CLS",
        quantity=Decimal("6"),
        quantity_used=Decimal("0"),
        unit_of_measure="PZA",
        import_invoice="IMP-1",
        import_line=1,
        line_item_id=1,
        available_lots=[
            AvailableLot(
                import_item_line_id=11,
                import_invoice_id=101,
                part_number_id=None,
                available_qty=Decimal("3"),
                value_me=Decimal("0"),
                value_mn=Decimal("0"),
                order_peps=1,
            ),
            AvailableLot(
                import_item_line_id=12,
                import_invoice_id=101,
                part_number_id=None,
                available_qty=Decimal("5"),
                value_me=Decimal("0"),
                value_mn=Decimal("0"),
                order_peps=2,
            ),
        ],
    )

    errors = ErrorCollector()
    compare_balances(db_session, export_invoice=None, to_discharge=[entry], errors=errors)  # type: ignore[arg-type]
    assert not errors.has_errors()
    assert entry.quantity_used == Decimal("6")
    assert entry.available_lots[0].consumed_qty == Decimal("3")
    assert entry.available_lots[1].consumed_qty == Decimal("3")
