"""
用户服务测试
"""

import pytest
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import ConflictError, NotFoundError
from app.core.security import verify_password
from app.models.user import User
from app.schemas.user import UserCreate, UserFilter, UserUpdate
from app.services.user_service import UserService


@pytest.mark.asyncio
class TestUserService:
    """用户服务测试类"""

    async def test_create_user(self, db_session: AsyncSession):
        """测试创建用户"""
        # 准备数据
        user_in = UserCreate(
            username="newuser",
            email="newuser@example.com",
            password="password123",
            full_name="New User",
            role="trainee",
        )

        # 创建用户
        service = UserService(db_session)
        user = await service.create_user(obj_in=user_in)

        # 验证结果
        assert user.username == "newuser"
        assert user.email == "newuser@example.com"
        assert user.full_name == "New User"
        assert user.role == "trainee"
        assert verify_password("password123", user.hashed_password)

    async def test_create_user_duplicate_username(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试创建重复用户名的用户"""
        user_in = UserCreate(
            username=test_user.username,  # 使用已存在的用户名
            email="another@example.com",
            password="password123",
        )

        service = UserService(db_session)
        with pytest.raises(ConflictError) as exc_info:
            await service.create_user(obj_in=user_in)

        assert f"用户名 {test_user.username} 已存在" in str(exc_info.value)

    async def test_create_user_duplicate_email(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试创建重复邮箱的用户"""
        user_in = UserCreate(
            username="anotheruser",
            email=test_user.email,  # 使用已存在的邮箱
            password="password123",
        )

        service = UserService(db_session)
        with pytest.raises(ConflictError) as exc_info:
            await service.create_user(obj_in=user_in)

        assert f"邮箱 {test_user.email} 已存在" in str(exc_info.value)

    async def test_get_by_username(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试根据用户名获取用户"""
        service = UserService(db_session)
        user = await service.get_by_username(test_user.username)

        assert user is not None
        assert user.id == test_user.id
        assert user.username == test_user.username

    async def test_get_by_email(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试根据邮箱获取用户"""
        service = UserService(db_session)
        user = await service.get_by_email(test_user.email)

        assert user is not None
        assert user.id == test_user.id
        assert user.email == test_user.email

    async def test_update_user(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试更新用户"""
        user_update = UserUpdate(
            full_name="Updated Name",
            bio="Updated bio",
        )

        service = UserService(db_session)
        user = await service.update_user(
            user_id=test_user.id,
            obj_in=user_update,
        )

        assert user.full_name == "Updated Name"
        assert user.bio == "Updated bio"

    async def test_update_user_not_found(self, db_session: AsyncSession):
        """测试更新不存在的用户"""
        user_update = UserUpdate(full_name="Updated Name")

        service = UserService(db_session)
        with pytest.raises(NotFoundError) as exc_info:
            await service.update_user(
                user_id=999,
                obj_in=user_update,
            )

        assert "用户不存在" in str(exc_info.value)

    async def test_update_password(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试更新密码"""
        service = UserService(db_session)

        # 更新密码
        user = await service.update_password(
            user_id=test_user.id,
            old_password="testpass123",
            new_password="newpass123",
        )

        # 验证新密码
        assert verify_password("newpass123", user.hashed_password)
        assert user.password_changed_at is not None

    async def test_update_password_wrong_old(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试使用错误的旧密码更新"""
        service = UserService(db_session)

        with pytest.raises(ConflictError) as exc_info:
            await service.update_password(
                user_id=test_user.id,
                old_password="wrongpass",
                new_password="newpass123",
            )

        assert "旧密码错误" in str(exc_info.value)

    async def test_get_users_with_filter(
        self,
        db_session: AsyncSession,
        test_user: User,
        admin_user: User,
        manager_user: User,
    ):
        """测试根据筛选条件获取用户"""
        service = UserService(db_session)

        # 测试角色筛选
        filter_params = UserFilter(role="admin")
        users, total = await service.get_users_with_filter(
            skip=0,
            limit=10,
            filter_params=filter_params,
        )
        assert total == 1
        assert users[0].id == admin_user.id

        # 测试关键词搜索
        filter_params = UserFilter(keyword="manager")
        users, total = await service.get_users_with_filter(
            skip=0,
            limit=10,
            filter_params=filter_params,
        )
        assert total == 1
        assert users[0].id == manager_user.id

    async def test_authenticate_username(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试使用用户名认证"""
        service = UserService(db_session)

        # 正确的密码
        user = await service.authenticate(
            username=test_user.username,
            password="testpass123",
        )
        assert user is not None
        assert user.id == test_user.id

        # 错误的密码
        user = await service.authenticate(
            username=test_user.username,
            password="wrongpass",
        )
        assert user is None

    async def test_authenticate_email(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试使用邮箱认证"""
        service = UserService(db_session)

        user = await service.authenticate(
            username=test_user.email,
            password="testpass123",
        )
        assert user is not None
        assert user.id == test_user.id

    async def test_soft_delete(
        self,
        db_session: AsyncSession,
        test_user: User,
    ):
        """测试软删除用户"""
        service = UserService(db_session)

        # 软删除
        user = await service.soft_delete(db_obj=test_user)
        assert user.is_deleted is True
        assert user.deleted_at is not None

        # 验证无法通过常规方法获取
        user = await service.get_by_id(test_user.id)
        assert user is None

