import pytest

from flyway.domain.errors import BusinessError
from flyway.services.stock import compute_remaining, lock_items, refresh, stock
from tests import builders as b


def _source(db, qty=100, unit=1):
    c = b.client(db)
    p = b.product(db)
    bt = b.batch(db, p)
    li = b.transfer(db, c)  # 已完成入库
    return c, p, bt, b.item(db, li, bt, qty, unit)


def test_draft_load_out_does_not_deduct(db):
    c, _, bt, src = _source(db)
    lo = b.transfer(db, c, load_out=True, draft=True)
    b.item(db, lo, bt, 30, main=src)
    assert compute_remaining(db, src) == 100


def test_completed_load_out_deducts_quantity_times_unit(db):
    c, _, bt, src = _source(db)
    lo = b.transfer(db, c, load_out=True)
    b.item(db, lo, bt, 3, unit=6, main=src)
    assert compute_remaining(db, src) == 82


def test_load_out_item_itself_has_zero_remaining(db):
    c, _, bt, src = _source(db)
    lo = b.transfer(db, c, load_out=True)
    out = b.item(db, lo, bt, 3, main=src)
    assert compute_remaining(db, out) == 0


def test_refresh_writes_back_and_rejects_negative(db):
    c, _, bt, src = _source(db, qty=10)
    lo = b.transfer(db, c, load_out=True)
    b.item(db, lo, bt, 4, main=src)
    refresh(db, [src])
    assert src.remaining_stock == 6
    b.item(db, lo, bt, 7, main=src)
    with pytest.raises(BusinessError):
        refresh(db, [src])


def test_stock_counts_only_completed_load_ins(db):
    c, p, bt, src = _source(db, qty=10)
    refresh(db, [src])
    draft = b.transfer(db, c, draft=True)
    d = b.item(db, draft, bt, 5)
    refresh(db, [d])
    assert stock(db, client_id=c.id) == 10
    assert stock(db, product_id=p.id) == 10
    assert stock(db, batch_id=bt.id) == 10


def test_lock_items_returns_sorted_and_ignores_none(db):
    _, _, bt, src = _source(db)
    other = b.item(db, src.transfer, bt, 1)
    locked = lock_items(db, [other.id, None, src.id])
    assert [i.id for i in locked] == sorted([other.id, src.id])
