"""auth 模块单测。用 tmp DB 绕过默认 /opt/xiangqin/data/。"""

from __future__ import annotations

import os
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))
    # 模块级常量需要 reload 才生效（因为已经 import 过）
    from xiangqin import db as db_module

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


def test_register_then_verify_new_user():
    from xiangqin import auth

    r = auth.register("13800001111")
    assert r.request_id
    assert r.masked_phone == "138****1111"
    assert r.expires_in_s == 60

    v = auth.verify(r.request_id, "123456")
    assert v.is_new_user is True
    assert v.user_id
    assert v.session_token


def test_register_reuses_active_request():
    from xiangqin import auth

    r1 = auth.register("13800001111")
    r2 = auth.register("13800001111")
    assert r1.request_id == r2.request_id  # 60s 内复用


def test_verify_wrong_code():
    from xiangqin import auth

    r = auth.register("13800001111")
    with pytest.raises(auth.AuthError) as e:
        auth.verify(r.request_id, "999999")
    assert e.value.code == "invalid_code"


def test_verify_twice_errors():
    from xiangqin import auth

    r = auth.register("13800001111")
    auth.verify(r.request_id, "123456")
    with pytest.raises(auth.AuthError) as e:
        auth.verify(r.request_id, "123456")
    assert e.value.code == "code_already_used"


def test_verify_expired():
    from xiangqin import auth, db as db_module

    r = auth.register("13800001111")
    with db_module.get_conn() as conn:
        conn.execute(
            "UPDATE auth_requests SET expires_at = 1 WHERE request_id = ?",
            (r.request_id,),
        )
    with pytest.raises(auth.AuthError) as e:
        auth.verify(r.request_id, "123456")
    assert e.value.code == "code_expired"


def test_invalid_phone():
    from xiangqin import auth

    with pytest.raises(auth.AuthError) as e:
        auth.register("12345")
    assert e.value.code == "invalid_phone"


def test_second_login_reuses_user():
    from xiangqin import auth

    r1 = auth.register("13800001111")
    v1 = auth.verify(r1.request_id, "123456")

    # 再登一次（等 60s+ 模拟，或直接伪造新 request）
    r2 = auth.register("13800001111")
    # 上面会复用 r1 的 request_id，因为 used=0。verify 一次后 used=1，再 register 会新开
    # 注意 r1 已经 used=1 了
    v2 = auth.verify(r2.request_id, "123456")

    assert v2.user_id == v1.user_id
    assert v2.is_new_user is False
    assert v2.session_token != v1.session_token


def test_resolve_session_happy():
    from xiangqin import auth

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

    uid = auth.resolve_session(v.session_token)
    assert uid == v.user_id


def test_resolve_session_invalid():
    from xiangqin import auth

    with pytest.raises(auth.AuthError) as e:
        auth.resolve_session("bogus-token")
    assert e.value.code == "unauthenticated"


def test_logout_clears_session():
    from xiangqin import auth

    r = auth.register("13800001111")
    v = auth.verify(r.request_id, "123456")
    auth.logout(v.session_token)

    with pytest.raises(auth.AuthError):
        auth.resolve_session(v.session_token)


def test_phone_never_in_db_plain(tmp_db):
    """红线：手机号原文不能在 DB 任何一列出现。"""
    from xiangqin import auth

    phone = "13800001111"
    r = auth.register(phone)
    auth.verify(r.request_id, "123456")

    import sqlite3

    with sqlite3.connect(tmp_db) as conn:
        # dump 所有表内容，检查是否有手机号原文
        for table in ["users", "profiles", "exposure_orders", "exposure_usage",
                      "auth_requests", "sessions"]:
            for row in conn.execute(f"SELECT * FROM {table}"):
                for v in row:
                    assert phone not in str(v), \
                        f"手机号原文泄漏在 {table}: {row}"
