"""#1718：PATCH /auth/me 资料更新接口测试。

覆盖：
- 正常更新昵称并落库
- strip 生效（前后空白去除）
- 纯空白/超长 -> 422（pydantic 校验）
- 首次设置昵称 profile_completed False->True
- 已完成用户重复提交幂等（仍 True）
- 未登录由 get_current_user 依赖保证 401（框架行为，这里验证路由声明了该依赖）
- 响应结构 {user: {...}} 含 wechat_bound/profile_completed 全字段
- 微信新建用户 profile_completed 默认 False（wechat_sync _create_wechat_user）
"""

from __future__ import annotations

import asyncio
import os
import sys
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock

import pytest
from pydantic import ValidationError

os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///test.db")

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))

from app.api.routes import auth as auth_route  # noqa: E402

from packages.adapters.in_memory.user_repository import InMemoryUserRepository  # noqa: E402
from packages.domain.entities import User  # noqa: E402


def _auth_user(user):
    return SimpleNamespace(user=user, session_id="s-1", token_type="user_auth")


def _make_user(**kw):
    defaults = dict(
        id="u-1",
        email="user@example.com",
        username="user",
        display_name="微信用户",
        password_hash="x",
        email_verified=True,
        profile_completed=False,
    )
    defaults.update(kw)
    return User(**defaults)


# ---------- 请求体校验 ----------


def test_display_name_strips_whitespace():
    req = auth_route.UpdateProfileRequest(display_name="  ying123  ")
    assert req.display_name == "ying123"


def test_display_name_blank_rejected():
    with pytest.raises(ValidationError) as exc:
        auth_route.UpdateProfileRequest(display_name="    ")
    assert "空白" in str(exc.value)


def test_display_name_empty_rejected():
    with pytest.raises(ValidationError):
        auth_route.UpdateProfileRequest(display_name="")


def test_display_name_too_long_rejected():
    with pytest.raises(ValidationError) as exc:
        auth_route.UpdateProfileRequest(display_name="甲" * 21)
    assert "1-20" in str(exc.value)


def test_display_name_max_length_accepted():
    req = auth_route.UpdateProfileRequest(display_name="甲" * 20)
    assert req.display_name == "甲" * 20


# ---------- 路由逻辑 ----------


def test_patch_me_updates_display_name_and_persists():
    user = _make_user()
    repo = InMemoryUserRepository()
    repo.save(user)

    resp = asyncio.run(
        auth_route.update_current_user_profile(
            auth_route.UpdateProfileRequest(display_name="  ying123 "),
            current_user=_auth_user(user),
            user_repository=repo,
        )
    )
    assert resp.user.display_name == "ying123"
    assert resp.user.profile_completed is True
    assert resp.user.wechat_bound is False
    # 落库验证
    fresh = repo.find_by_id("u-1")
    assert fresh.display_name == "ying123"
    assert fresh.profile_completed is True


def test_patch_me_first_time_sets_profile_completed_true():
    user = _make_user(profile_completed=False)
    repo = InMemoryUserRepository()
    repo.save(user)
    assert repo.find_by_id("u-1").profile_completed is False

    asyncio.run(
        auth_route.update_current_user_profile(
            auth_route.UpdateProfileRequest(display_name="小虾"),
            current_user=_auth_user(user),
            user_repository=repo,
        )
    )
    assert repo.find_by_id("u-1").profile_completed is True


def test_patch_me_idempotent_for_completed_user():
    user = _make_user(display_name="老名字", profile_completed=True)
    repo = InMemoryUserRepository()
    repo.save(user)

    resp = asyncio.run(
        auth_route.update_current_user_profile(
            auth_route.UpdateProfileRequest(display_name="新名字"),
            current_user=_auth_user(user),
            user_repository=repo,
        )
    )
    assert resp.user.profile_completed is True
    assert resp.user.display_name == "新名字"
    # 再提交一次同样内容，不报错、状态稳定
    resp2 = asyncio.run(
        auth_route.update_current_user_profile(
            auth_route.UpdateProfileRequest(display_name="新名字"),
            current_user=_auth_user(repo.find_by_id("u-1")),
            user_repository=repo,
        )
    )
    assert resp2.user.profile_completed is True


def test_patch_me_response_contains_all_me_fields():
    user = _make_user(wechat_openid="wx-1", phone="13800000000", phone_verified=True)
    repo = InMemoryUserRepository()
    repo.save(user)

    resp = asyncio.run(
        auth_route.update_current_user_profile(
            auth_route.UpdateProfileRequest(display_name="昵称"),
            current_user=_auth_user(user),
            user_repository=repo,
        )
    )
    payload = resp.user.model_dump()
    for field in (
        "user_id",
        "email",
        "username",
        "display_name",
        "email_verified",
        "phone",
        "phone_verified",
        "binding_complete",
        "wechat_bound",
        "profile_completed",
    ):
        assert field in payload, f"missing field {field}"
    assert payload["wechat_bound"] is True
    assert payload["phone"] == "13800000000"


def test_get_me_includes_profile_completed_flag():
    # 未完成
    u = _make_user(profile_completed=False)
    resp = asyncio.run(auth_route.get_current_user_info(authenticated_user=_auth_user(u)))
    assert resp.profile_completed is False
    assert resp.wechat_bound is False

    # 已完成 + 已绑微信
    u2 = _make_user(profile_completed=True, wechat_openid="wx-9")
    resp2 = asyncio.run(auth_route.get_current_user_info(authenticated_user=_auth_user(u2)))
    assert resp2.profile_completed is True
    assert resp2.wechat_bound is True


def test_patch_me_requires_auth_dependency():
    # 路由签名必须依赖 get_current_user，未携带 token 时框架返回 401
    params = (
        auth_route.update_current_user_profile.__wrapped__
        if hasattr(auth_route.update_current_user_profile, "__wrapped__")
        else auth_route.update_current_user_profile
    )
    import inspect

    sig = inspect.signature(params)
    dep = sig.parameters.get("current_user")
    assert dep is not None
    assert dep.default is not None and getattr(dep.default, "dependency", None) is auth_route.get_current_user


def test_wechat_new_user_created_with_profile_completed_false():
    # 微信同步建号：新用户 profile_completed=False（需引导设置昵称）
    from packages.application.auth.wechat_sync_use_case import (
        WechatSyncRequest,
        WechatSyncUseCase,
    )

    repo = InMemoryUserRepository()
    # session_store 用 mock，不依赖 redis
    use_case = WechatSyncUseCase(user_repository=repo, session_store=MagicMock(), jwt_secret_key="test-secret")
    resp, err = use_case.execute(WechatSyncRequest(openid="wx-new-openid", nickname="微信测试", source="web"))
    assert err is None
    user = repo.find_by_id(resp.user_id)
    assert user.profile_completed is False
