from decimal import Decimal

from api.v1.modules.a24.balance_movements.models import BalanceMovement, MovementType
from api.v1.modules.a24.discharges.models import DischargeDetail, DischargeHeader
from api.v1.modules.a76.invoices.imports.process import main_process as import_main
from api.v1.modules.a76.invoices.exports.process import main_process as export_main
from tests.fixtures.builders import (
    create_business_catalogs,
    create_export_invoice_with_line,
    create_import_invoice_with_line,
    ensure_reference_data,
)


def _patch_import_pipeline(monkeypatch, import_line):
    monkeypatch.setattr(import_main, "pre_validators", lambda *args, **kwargs: [import_line])
    monkeypatch.setattr(import_main, "review_classes", lambda *args, **kwargs: None)
    monkeypatch.setattr(import_main, "review_exchange_rate", lambda *args, **kwargs: None)
    monkeypatch.setattr(import_main, "review_weights_kgs", lambda *args, **kwargs: None)
    monkeypatch.setattr(import_main, "review_weights_lbs", lambda *args, **kwargs: None)
    monkeypatch.setattr(import_main, "_validate_sisimp_limits", lambda *args, **kwargs: None)
    monkeypatch.setattr(import_main, "_validate_lines", lambda *args, **kwargs: ([], {}))


def _patch_export_pipeline(monkeypatch, export_line):
    monkeypatch.setattr(export_main, "pre_validators", lambda *args, **kwargs: [export_line])
    monkeypatch.setattr(export_main, "review_class", lambda *args, **kwargs: None)
    monkeypatch.setattr(export_main, "review_exchange_rate", lambda *args, **kwargs: None)
    monkeypatch.setattr(export_main, "assign_values", lambda *args, **kwargs: None)
    monkeypatch.setattr(export_main, "review_qty_vs_weight", lambda *args, **kwargs: None)
    monkeypatch.setattr(export_main, "review_unit_cost", lambda *args, **kwargs: None)
    monkeypatch.setattr(export_main, "review_qty_series", lambda *args, **kwargs: None)


def test_e2e_inventory_flow_import_then_export(client, db_session, monkeypatch, test_tenant):
    """
    E2E principal:
    - Alta catálogos/base
    - Procesa factura IMP -> genera entry
    - Procesa factura EXP -> genera consumo/descarga
    - Valida decremento de saldo sin negativos
    """
    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-E2E-01", qty=Decimal("10")
    )
    _patch_import_pipeline(monkeypatch, import_line)

    response = client.post(f"/api/v1/a76/invoices/{import_invoice.id}/process?company_id={cid}")
    assert response.status_code == 200

    entry_movements = (
        db_session.query(BalanceMovement)
        .filter(
            BalanceMovement.import_invoice_id == import_invoice.id,
            BalanceMovement.import_item_line_id == import_line.id,
            BalanceMovement.movement_type == MovementType.ENTRY,
        )
        .all()
    )
    assert entry_movements
    assert sum(Decimal(str(m.quantity or 0)) for m in entry_movements) > 0

    export_invoice, export_line = create_export_invoice_with_line(
        db_session, tid, cid, catalogs, import_invoice, import_line, qty=Decimal("4")
    )
    _patch_export_pipeline(monkeypatch, export_line)

    response = client.post(f"/api/v1/a76/invoices/{export_invoice.id}/process?company_id={cid}")
    assert response.status_code == 200

    consumptions = (
        db_session.query(BalanceMovement)
        .filter(
            BalanceMovement.source_invoice_id == export_invoice.id,
            BalanceMovement.movement_type == MovementType.CONSUMPTION,
        )
        .all()
    )
    assert consumptions, "El proceso de exportación debe generar consumos"

    discharge_headers = (
        db_session.query(DischargeHeader)
        .filter(DischargeHeader.source_invoice_id == export_invoice.id)
        .all()
    )
    assert discharge_headers

    discharge_details = (
        db_session.query(DischargeDetail)
        .filter(DischargeDetail.export_item_line_id == export_line.id)
        .all()
    )
    assert discharge_details
    assert all(detail.movement_id is not None for detail in discharge_details)

    net_balance = db_session.query(BalanceMovement).filter(
        BalanceMovement.import_item_line_id == import_line.id
    ).all()
    signed = Decimal("0")
    for mov in net_balance:
        qty = Decimal(str(mov.quantity or 0))
        if mov.movement_type in {MovementType.CONSUMPTION, MovementType.WASTE, MovementType.SCRAP, MovementType.DESTRUCTION}:
            signed -= qty
        else:
            signed += qty
    assert signed >= 0
    assert signed < Decimal("10")
