"""
认证模块单元测试
"""
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

from app.services.auth_service import AuthService
from app.schemas.auth import UserRegister
from app.core.security import verify_password, create_password_hash
from app.core.exceptions import InvalidCredentialsError, UsernameExistsError


@pytest.mark.asyncio
class TestAuthService:
    """认证服务测试类"""
    
    async def test_user_registration(self, db_session: AsyncSession, test_user_data):
        """测试用户注册"""
        # 创建认证服务
        auth_service = AuthService(db_session)
        
        # 准备注册数据
        register_data = UserRegister(**test_user_data)
        
        # 注册用户
        user = await auth_service.create_user(register_data)
        
        # 验证用户创建成功
        assert user.id is not None
        assert user.username == test_user_data["username"]
        assert user.email == test_user_data["email"]
        assert user.is_active is True
        assert user.role == "trainee"
        
        # 验证密码已加密
        assert user.password_hash != test_user_data["password"]
        assert verify_password(test_user_data["password"], user.password_hash)
    
    async def test_duplicate_username_registration(
        self,
        db_session: AsyncSession,
        test_user_data
    ):
        """测试重复用户名注册"""
        auth_service = AuthService(db_session)
        
        # 第一次注册
        register_data = UserRegister(**test_user_data)
        await auth_service.create_user(register_data)
        
        # 尝试使用相同用户名再次注册
        with pytest.raises(UsernameExistsError):
            await auth_service.create_user(register_data)
    
    async def test_user_login(self, db_session: AsyncSession, test_user_data):
        """测试用户登录"""
        auth_service = AuthService(db_session)
        
        # 先注册用户
        register_data = UserRegister(**test_user_data)
        user = await auth_service.create_user(register_data)
        
        # 测试登录
        authenticated_user = await auth_service.authenticate_user(
            username=test_user_data["username"],
            password=test_user_data["password"]
        )
        
        assert authenticated_user.id == user.id
        assert authenticated_user.username == user.username
        
        # 验证登录信息已更新
        assert authenticated_user.login_count == "1"
        assert authenticated_user.failed_login_count == "0"
        assert authenticated_user.last_login is not None
    
    async def test_login_with_email(self, db_session: AsyncSession, test_user_data):
        """测试使用邮箱登录"""
        auth_service = AuthService(db_session)
        
        # 注册用户
        register_data = UserRegister(**test_user_data)
        await auth_service.create_user(register_data)
        
        # 使用邮箱登录
        user = await auth_service.authenticate_user(
            username=test_user_data["email"],
            password=test_user_data["password"]
        )
        
        assert user.email == test_user_data["email"]
    
    async def test_invalid_password_login(
        self,
        db_session: AsyncSession,
        test_user_data
    ):
        """测试错误密码登录"""
        auth_service = AuthService(db_session)
        
        # 注册用户
        register_data = UserRegister(**test_user_data)
        await auth_service.create_user(register_data)
        
        # 尝试使用错误密码登录
        with pytest.raises(InvalidCredentialsError):
            await auth_service.authenticate_user(
                username=test_user_data["username"],
                password="WrongPassword123!"
            )
    
    async def test_token_creation(self, db_session: AsyncSession, test_user_data):
        """测试Token创建"""
        auth_service = AuthService(db_session)
        
        # 注册用户
        register_data = UserRegister(**test_user_data)
        user = await auth_service.create_user(register_data)
        
        # 创建tokens
        tokens = await auth_service.create_tokens_for_user(user)
        
        assert "access_token" in tokens
        assert "refresh_token" in tokens
        assert tokens["token_type"] == "bearer"
        assert tokens["expires_in"] > 0


@pytest.mark.asyncio
class TestAuthAPI:
    """认证API测试类"""
    
    async def test_register_endpoint(self, client: AsyncClient, test_user_data):
        """测试注册端点"""
        response = await client.post(
            "/api/v1/auth/register",
            json=test_user_data
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["message"] == "注册成功"
        assert "access_token" in data["data"]
        assert "refresh_token" in data["data"]
    
    async def test_login_endpoint(self, client: AsyncClient, test_user_data):
        """测试登录端点"""
        # 先注册
        await client.post("/api/v1/auth/register", json=test_user_data)
        
        # 测试登录
        response = await client.post(
            "/api/v1/auth/login",
            data={
                "username": test_user_data["username"],
                "password": test_user_data["password"]
            }
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert "access_token" in data["data"]
    
    async def test_refresh_token_endpoint(
        self,
        client: AsyncClient,
        test_user_data
    ):
        """测试Token刷新端点"""
        # 先注册并获取tokens
        register_response = await client.post(
            "/api/v1/auth/register",
            json=test_user_data
        )
        tokens = register_response.json()["data"]
        
        # 刷新token
        response = await client.post(
            "/api/v1/auth/refresh",
            json={"refresh_token": tokens["refresh_token"]}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data["data"]
        assert data["data"]["access_token"] != tokens["access_token"]
    
    async def test_logout_endpoint(self, client: AsyncClient):
        """测试登出端点"""
        response = await client.post("/api/v1/auth/logout")
        
        assert response.status_code == 200
        data = response.json()
        assert data["message"] == "登出成功"
    
    async def test_reset_password_request(self, client: AsyncClient):
        """测试重置密码请求"""
        response = await client.post(
            "/api/v1/auth/reset-password",
            json={"email": "test@example.com"}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "如果该邮箱已注册" in data["message"]
