"""exposure 单测。"""

from __future__ import annotations

from pathlib import Path

import pytest


@pytest.fixture(autouse=True)
def tmp_db(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
    db_path = tmp_path / "xq.db"
    monkeypatch.setenv("XIANGQIN_DB_PATH", str(db_path))
    from xiangqin import db as db_module

    db_module.DEFAULT_DB_PATH = db_path
    db_module.init_db(db_path)
    yield db_path


@pytest.fixture()
def user_id():
    from xiangqin import auth

    r = auth.register("13800001111")
    v = auth.verify(r.request_id, "123456")
    return v.user_id


def test_buy_happy(user_id):
    from xiangqin import exposure

    o = exposure.buy(user_id, count=100, mock=True)
    assert o.user_id == user_id
    assert o.count == 100
    assert o.amount_cents == 100 * 10  # 10 分/次
    assert o.mock is True
    assert o.paid is True


def test_buy_updates_balance(user_id):
    from xiangqin import exposure

    exposure.buy(user_id, count=100, mock=True)
    b = exposure.get_balance(user_id)
    assert b.remaining == 100
    assert b.total_purchased == 100


def test_buy_twice_accumulates(user_id):
    from xiangqin import exposure

    exposure.buy(user_id, count=50, mock=True)
    exposure.buy(user_id, count=30, mock=True)
    b = exposure.get_balance(user_id)
    assert b.remaining == 80
    assert b.total_purchased == 80


def test_mock_required(user_id):
    from xiangqin import exposure

    with pytest.raises(exposure.ExposureError) as e:
        exposure.buy(user_id, count=100, mock=False)
    assert e.value.code == "mock_required"


def test_count_too_small(user_id):
    from xiangqin import exposure

    with pytest.raises(exposure.ExposureError):
        exposure.buy(user_id, count=0, mock=True)


def test_count_too_large(user_id):
    from xiangqin import exposure

    with pytest.raises(exposure.ExposureError):
        exposure.buy(user_id, count=10_001, mock=True)


def test_list_orders_desc(user_id):
    from xiangqin import exposure

    o1 = exposure.buy(user_id, count=10, mock=True)
    o2 = exposure.buy(user_id, count=20, mock=True)
    o3 = exposure.buy(user_id, count=30, mock=True)

    orders = exposure.list_orders(user_id)
    assert len(orders) == 3
    # 新的在前
    assert orders[0].id == o3.id
    assert orders[2].id == o1.id


def test_balance_then_query_consumes():
    """集成测：买 → 余额 → query 命中 → 余额扣减。"""
    from xiangqin import auth, exposure, profile, query

    # caller
    r1 = auth.register("13000001111")
    v1 = auth.verify(r1.request_id, "123456")
    caller_id = v1.user_id
    profile.update(caller_id, {"gender": "m", "age": 28, "city": "hangzhou"})

    # target（买曝光）
    r2 = auth.register("13900001111")
    v2 = auth.verify(r2.request_id, "123456")
    target_id = v2.user_id
    profile.update(target_id, {"gender": "f", "age": 27, "city": "hangzhou"})

    exposure.buy(target_id, count=5, mock=True)
    assert exposure.get_balance(target_id).remaining == 5

    # caller query
    result = query.run(caller_id, "city=hangzhou AND gender=f")
    assert result.promoted_hits == 1
    assert result.exposure_consumed == 1

    # target 余额扣 1
    assert exposure.get_balance(target_id).remaining == 4
