"""注销账号(DELETE /api/v1/user):软删 + 释放唯一约束 + 资金前置校验。

覆盖:
  - 干净注销:status=deleted + phone 匿名化 + wechat_openid/invite_code 等唯一槽全部释放
  - 回归原始 bug:注销释放 openid 后,别的账号能绑同一个微信
  - 资金前置闸:有可提现现金 / 在审提现单 → 409 拒绝注销,账号不被删
  - 注销后旧 token 即时失效(status==active 闸)
"""
from __future__ import annotations

from sqlalchemy import select

from app.db.session import SessionLocal
from app.models.user import User
from app.models.wallet import CoinAccount


def _login(client, phone: str) -> str:
    client.post("/api/v1/auth/sms/send", json={"phone": phone})
    r = client.post("/api/v1/auth/sms/login", json={"phone": phone, "code": "123456"})
    assert r.status_code == 200, r.text
    return r.json()["access_token"]


def _auth(token: str) -> dict[str, str]:
    return {"Authorization": f"Bearer {token}"}


def _patch_userinfo(monkeypatch, openid: str) -> None:
    monkeypatch.setattr(
        "app.integrations.wxpay.code_to_userinfo",
        lambda code: {"openid": openid, "nickname": "微信昵称", "avatar_url": "https://x/a.png", "raw": {}},
    )


def _seed_cash(client, token: str, phone: str, cents: int) -> None:
    """先访问 /account 触发建账户,再用 DB 直接灌现金余额。"""
    client.get("/api/v1/wallet/account", headers=_auth(token))
    db = SessionLocal()
    try:
        user = db.execute(select(User).where(User.phone == phone)).scalar_one()
        acc = db.get(CoinAccount, user.id)
        acc.cash_balance_cents = cents
        db.commit()
    finally:
        db.close()


def _get_user(phone: str) -> User | None:
    db = SessionLocal()
    try:
        return db.execute(select(User).where(User.phone == phone)).scalar_one_or_none()
    finally:
        db.close()


def test_delete_clean_account_releases_all_unique_slots(client) -> None:
    phone = "13900000001"
    token = _login(client, phone)
    # 预置:给该用户造昵称 + 邀请码(占 invite_code 唯一槽),验证注销时一并释放
    db = SessionLocal()
    try:
        u = db.execute(select(User).where(User.phone == phone)).scalar_one()
        u.nickname = "张三"
        u.invite_code = "INVCODE0001"
        db.commit()
        uid = u.id
    finally:
        db.close()

    r = client.delete("/api/v1/user", headers=_auth(token))
    assert r.status_code == 200, r.text

    u = _get_user(f"deleted_{uid}")
    assert u is not None
    assert u.status == "deleted"
    assert u.phone == f"deleted_{uid}"
    assert u.nickname is None
    assert u.avatar_url is None
    assert u.wechat_openid is None
    assert u.wechat_nickname is None
    assert u.wechat_avatar_url is None
    assert u.invite_code is None


def test_delete_releases_wechat_so_another_account_can_bind(client, monkeypatch) -> None:
    """原始 bug 回归:A 绑微信 → 注销,B 必须能绑同一个 openid(修复前会 409)。"""
    openid = "openid_delreuse_a1"  # 独特值,避免跨测试共享库污染(如 test_withdraw 也用 openid_shared_xyz)
    # A 绑微信
    _patch_userinfo(monkeypatch, openid)
    token_a = _login(client, "13900000002")
    r = client.post("/api/v1/wallet/bind-wechat", json={"code": "ca"}, headers=_auth(token_a))
    assert r.status_code == 200, r.text
    # A 注销(无现金、无提现单 → 允许)
    r = client.delete("/api/v1/user", headers=_auth(token_a))
    assert r.status_code == 200, r.text
    # B 绑同一个 openid → 必须成功(修复前会 409 "该微信已绑定其他账号")
    token_b = _login(client, "13900000003")
    r = client.post("/api/v1/wallet/bind-wechat", json={"code": "cb"}, headers=_auth(token_b))
    assert r.status_code == 200, r.text
    assert r.json()["bound"] is True


def test_delete_blocked_when_cash_balance_positive(client) -> None:
    phone = "13900000004"
    token = _login(client, phone)
    _seed_cash(client, token, phone, 100)  # 1 元未提现
    r = client.delete("/api/v1/user", headers=_auth(token))
    assert r.status_code == 409, r.text
    assert "现金" in r.json()["detail"]
    u = _get_user(phone)  # 账号未被删
    assert u is not None and u.status == "active"


def test_delete_blocked_when_reviewing_withdraw_exists(client, monkeypatch) -> None:
    """cash 提光(=0)但有在审提现单 → 仍拒绝(命中 reviewing 闸,非 cash 闸)。"""
    phone = "13900000005"
    _patch_userinfo(monkeypatch, "openid_delrev_b2")
    token = _login(client, phone)
    _seed_cash(client, token, phone, 50)
    client.post("/api/v1/wallet/bind-wechat", json={"code": "c"}, headers=_auth(token))
    # 提光 50 → cash 归 0,单进 reviewing
    r = client.post("/api/v1/wallet/withdraw", json={"amount_cents": 50}, headers=_auth(token))
    assert r.status_code == 200 and r.json()["status"] == "reviewing", r.text
    assert r.json()["cash_balance_cents"] == 0

    r = client.delete("/api/v1/user", headers=_auth(token))
    assert r.status_code == 409, r.text
    assert "审核" in r.json()["detail"]
    u = _get_user(phone)
    assert u is not None and u.status == "active"


def test_deleted_user_old_token_is_rejected(client) -> None:
    phone = "13900000006"
    token = _login(client, phone)
    r = client.delete("/api/v1/user", headers=_auth(token))
    assert r.status_code == 200, r.text
    # 注销后旧 token 立即失效(get_current_user 校验 status==active)
    r = client.get("/api/v1/user/onboarding/status", params={"device_id": "d"}, headers=_auth(token))
    assert r.status_code == 401, r.text
