"""admin.py 单测（#021 + #016 + #017）。"""

from __future__ import annotations

import pytest

from xiangqin import admin, auth, mail


# ─── fixtures ─────────────────────────────────────────


def _register(phone: str) -> str:
    r = auth.register(phone)
    v = auth.verify(r.request_id, "123456")
    return v.user_id


@pytest.fixture
def admin_user(monkeypatch: pytest.MonkeyPatch) -> str:
    """建一个 user + 把它加到 admin 白名单。"""
    uid = _register("13800009999")
    monkeypatch.setenv("XIANGQIN_ADMIN_USER_IDS", uid)
    return uid


@pytest.fixture
def target_user() -> str:
    return _register("13800001111")


# ─── 权限 ─────────────────────────────────────────────


def test_is_admin_from_env(monkeypatch):
    monkeypatch.setenv("XIANGQIN_ADMIN_USER_IDS", "id1,id2, id3 ")
    assert admin.is_admin("id1")
    assert admin.is_admin("id2")
    assert admin.is_admin("id3")
    assert not admin.is_admin("id4")


def test_is_admin_empty_env_all_false(monkeypatch):
    monkeypatch.delenv("XIANGQIN_ADMIN_USER_IDS", raising=False)
    assert not admin.is_admin("any-id")


def test_require_admin_raises_403(monkeypatch):
    monkeypatch.delenv("XIANGQIN_ADMIN_USER_IDS", raising=False)
    with pytest.raises(admin.AdminError) as ei:
        admin.require_admin("not-admin-id")
    assert ei.value.code == "not_admin"
    assert ei.value.http_status == 403


# ─── ban / unban / freeze ─────────────────────────────


def test_ban_user_updates_status(admin_user: str, target_user: str):
    s = admin.ban_user(target_user, reason="骚扰", admin_id=admin_user)
    assert s.status == "banned"
    assert s.status_reason == "骚扰"


def test_ban_user_kills_existing_sessions(admin_user: str, target_user: str):
    """banned 后老 session 应被清。"""
    # 注册时已经有 session，ban 后 resolve_session 应挂
    # 先拿 session token
    r = auth.register("13800007777")
    v = auth.verify(r.request_id, "123456")
    uid = v.user_id
    token = v.session_token
    # 验证 session 能用
    assert auth.resolve_session(token) == uid

    admin.ban_user(uid, reason="test", admin_id=admin_user)

    # session 应该被清
    with pytest.raises(auth.AuthError):
        auth.resolve_session(token)


def test_unban_restores_active(admin_user: str, target_user: str):
    admin.ban_user(target_user, reason="test", admin_id=admin_user)
    s = admin.unban_user(target_user, admin_id=admin_user)
    assert s.status == "active"
    assert s.status_reason is None


def test_freeze_user(admin_user: str, target_user: str):
    s = admin.freeze_user(target_user, reason="冷静期", admin_id=admin_user)
    assert s.status == "frozen"


def test_ban_user_not_found(admin_user: str):
    with pytest.raises(admin.AdminError) as ei:
        admin.ban_user("NONEXISTENT-ID", reason="x", admin_id=admin_user)
    assert ei.value.code == "user_not_found"
    assert ei.value.http_status == 404


def test_get_user_status_defaults_active(target_user: str):
    s = admin.get_user_status(target_user)
    assert s.status == "active"
    assert s.status_reason is None


def test_list_users_by_status(admin_user: str):
    u1 = _register("13800001111")
    u2 = _register("13800002222")
    u3 = _register("13800003333")
    admin.ban_user(u1, reason="r1", admin_id=admin_user)
    admin.ban_user(u2, reason="r2", admin_id=admin_user)

    banned = admin.list_users_by_status("banned")
    banned_ids = {u.user_id for u in banned}
    assert u1 in banned_ids and u2 in banned_ids
    assert u3 not in banned_ids


# ─── 业务层拦截（banned 不能发信）─────────────────────


def test_banned_user_cannot_send(admin_user: str):
    a = _register("13800001111")
    b = _register("13800002222")
    admin.ban_user(a, reason="test", admin_id=admin_user)

    with pytest.raises(mail.MailError) as ei:
        mail.send(a, b, "hi")
    assert ei.value.code == "user_banned"


def test_frozen_user_cannot_send(admin_user: str):
    a = _register("13800001111")
    b = _register("13800002222")
    admin.freeze_user(a, reason="冷静期", admin_id=admin_user)

    with pytest.raises(mail.MailError) as ei:
        mail.send(a, b, "hi")
    assert ei.value.code == "user_frozen"


# ─── 举报审核 ─────────────────────────────────────────


def _make_report(admin_user: str) -> tuple[str, str, str]:
    """发信人 sender 被收件人 receiver 举报（reporter 必须是 recipient）。
    返回 (sender_id, receiver_id, report_id)。"""
    sender = _register("13800001111")
    receiver = _register("13800002222")
    msg = mail.send(sender, receiver, "骚扰内容")
    rep = mail.report(receiver, msg.id, reason="骚扰")
    return sender, receiver, rep["id"]


def test_list_pending_reports(admin_user: str):
    _, _, rid = _make_report(admin_user)
    pending = admin.list_pending_reports()
    assert any(r.id == rid and r.review_status == "pending" for r in pending)


def test_pass_report(admin_user: str):
    _, _, rid = _make_report(admin_user)
    r = admin.pass_report(rid, admin_id=admin_user)
    assert r.review_status == "passed"
    assert r.reviewed_by == admin_user
    # 不在 pending 里了
    assert not any(p.id == rid for p in admin.list_pending_reports())


def test_reject_report(admin_user: str):
    _, _, rid = _make_report(admin_user)
    r = admin.reject_report(rid, admin_id=admin_user)
    assert r.review_status == "rejected"


def test_confirm_report_without_ban(admin_user: str):
    sender, _, rid = _make_report(admin_user)
    r = admin.confirm_report(rid, admin_id=admin_user, ban_target=False)
    assert r.review_status == "confirmed"
    # sender（被举报者）仍 active
    s = admin.get_user_status(sender)
    assert s.status == "active"


def test_confirm_report_with_ban_target(admin_user: str):
    sender, _, rid = _make_report(admin_user)
    admin.confirm_report(rid, admin_id=admin_user, ban_target=True, ban_reason="严重骚扰")
    s = admin.get_user_status(sender)
    assert s.status == "banned"
    assert s.status_reason == "严重骚扰"


def test_review_already_reviewed_rejected(admin_user: str):
    _, _, rid = _make_report(admin_user)
    admin.pass_report(rid, admin_id=admin_user)
    with pytest.raises(admin.AdminError) as ei:
        admin.reject_report(rid, admin_id=admin_user)
    assert ei.value.code == "already_reviewed"


def test_review_not_found(admin_user: str):
    with pytest.raises(admin.AdminError) as ei:
        admin.pass_report("NONEXISTENT-REPORT-ID", admin_id=admin_user)
    assert ei.value.code == "report_not_found"


# ─── stats ─────────────────────────────────────────────


def test_stats_counts_users_and_orders(admin_user: str):
    u = _register("13800001111")
    # 买 1 个真付订单（mock=True 标 paid）
    mail.buy_envelopes(u, count=1, mock=True)

    stats = admin.get_stats()
    assert stats["users"]["total"] >= 2  # admin + u
    assert stats["users"]["active"] >= 2
    assert stats["orders"]["total"] >= 1
    # mock 订单不计入 paid_real
    assert stats["orders"]["paid_real"] == 0
    assert stats["orders"]["paid_amount_yuan"] == 0.0
