"""E2E 注册 → 发信 → 收信闭环。

模拟真实用户旅程，全走 TestClient 跑完整 HTTP 栈 + 真 sqlite。
用白名单测试号（XIANGQIN_TEST_PHONES + XIANGQIN_TEST_OTP）绕过 SMS provider，
不依赖人工读验证码。

对应 issue #026 + blueprint testing-strategy.md。
"""

from __future__ import annotations

import pytest
from fastapi.testclient import TestClient

TEST_OTP = "874321"
PHONE_A = "13900000001"
PHONE_B = "13900000002"


@pytest.fixture
def client(monkeypatch):
    monkeypatch.setenv("XIANGQIN_TEST_PHONES", f"{PHONE_A},{PHONE_B}")
    monkeypatch.setenv("XIANGQIN_TEST_OTP", TEST_OTP)
    # 即便 provider 设成 aliyun，白名单号也不会真调它
    monkeypatch.setenv("XIANGQIN_SMS_PROVIDER", "aliyun")

    from xiangqin.app import app

    return TestClient(app)


def _login(client: TestClient, phone: str) -> str:
    r = client.post("/auth/register", json={"phone": phone})
    assert r.status_code == 200, r.text
    req_id = r.json()["request_id"]
    v = client.post("/auth/verify", json={"request_id": req_id, "code": TEST_OTP})
    assert v.status_code == 200, v.text
    return v.json()["session_token"]


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


def test_full_onboarding_to_reply(client):
    """老板相亲旅程完整闭环：
    A 注册 → A 填资料 → B 注册 → B 填资料 →
    A 查 B → A 发信 → B 收信 → B 读信 → B 回信 → A 收到回信
    """
    # 1. A 注册并拿 session
    token_a = _login(client, PHONE_A)
    me_a = client.get("/auth/me", headers=_auth(token_a)).json()
    uid_a = me_a["user_id"]
    assert uid_a

    # 2. A 填资料
    p = client.put(
        "/profile/me",
        headers=_auth(token_a),
        json={
            "gender": "m",
            "age": 28,
            "city": "hangzhou",
            "height": 178,
            "education": "master",
            "tags": ["程序", "爬山"],
            "bio": "A 自我介绍",
        },
    )
    assert p.status_code == 200, p.text

    # 3. B 注册 + 填资料
    token_b = _login(client, PHONE_B)
    uid_b = client.get("/auth/me", headers=_auth(token_b)).json()["user_id"]
    p = client.put(
        "/profile/me",
        headers=_auth(token_b),
        json={
            "gender": "f",
            "age": 26,
            "city": "hangzhou",
            "height": 165,
            "education": "master",
            "tags": ["阅读", "爬山"],
            "bio": "B 自我介绍",
        },
    )
    assert p.status_code == 200

    # 4. A 查 hangzhou + 女 + 25-30 岁 → 应能找到 B
    q = client.post(
        "/query",
        headers=_auth(token_a),
        json={"where": "gender=f AND city=hangzhou AND age>=25 AND age<=30", "limit": 10},
    )
    assert q.status_code == 200, q.text
    ids = [row["user_id"] for row in q.json()["results"]]
    assert uid_b in ids

    # 5. A 给 B 发信（免费额度内，无需买信封）
    s = client.post(
        "/dm/send",
        headers=_auth(token_a),
        json={"to_user_id": uid_b, "body": "你好 B，我是 A，在杭州也爱爬山 🏔️"},
    )
    assert s.status_code == 200, s.text
    msg_id = s.json()["id"]

    # 6. B 收件箱应能看到
    inbox = client.get("/inbox", headers=_auth(token_b)).json()
    assert any(m["id"] == msg_id for m in inbox)

    # 7. B 读信
    read = client.post(f"/inbox/{msg_id}/read", headers=_auth(token_b))
    assert read.status_code == 200

    # 8. B 回信
    reply = client.post(
        f"/inbox/{msg_id}/reply",
        headers=_auth(token_b),
        json={"body": "嗨 A，周末要去临安，一起？"},
    )
    assert reply.status_code == 200, reply.text
    reply_id = reply.json()["id"]

    # 9. A 收件箱应有 B 的回信
    inbox_a = client.get("/inbox", headers=_auth(token_a)).json()
    assert any(m["id"] == reply_id for m in inbox_a)


def test_non_whitelist_phone_rejected_with_fake_otp(client):
    """非白名单号用 TEST_OTP 登录必须失败（白名单隔离）。"""
    r = client.post("/auth/register", json={"phone": "13800001234"})
    # 会尝试调 aliyun provider → 无真凭证 503
    assert r.status_code in (503, 500)
