"""用户注册 UseCase 单元测试."""

from __future__ import annotations

from unittest.mock import MagicMock

import pytest

from packages.application.auth.register_user_use_case import (
    RegisterUserRequest,
    RegisterUserUseCase,
    VerifyEmailRequest,
    VerifyEmailUseCase,
)
from packages.domain.entities import User


@pytest.fixture
def mock_user_repo():
    return MagicMock()


@pytest.fixture
def mock_email_service():
    svc = MagicMock()
    svc.send_verification_email.return_value = (True, None)
    return svc


@pytest.fixture
def sample_user():
    user = User(
        id="user_001",
        email="test@example.com",
        username="testuser",
        display_name="测试用户",
        password_hash="hashed_pw",
    )
    user.email_verified = False
    user.email_verification_token = "some_token"
    return user


class TestRegisterUserRequest:
    """RegisterUserRequest 测试"""

    def test_email_lowercased_stripped(self):
        """邮箱转小写并去空格"""
        req = RegisterUserRequest(
            email="  Test@Example.COM  ",
            password="TestPass1!",
            username="testuser",
            display_name="测试用户",
        )
        assert req.email == "test@example.com"

    def test_username_stripped(self):
        """用户名去空格"""
        req = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="  testuser  ",
            display_name="测试用户",
        )
        assert req.username == "testuser"

    def test_display_name_stripped(self):
        """显示名去空格"""
        req = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="  测试用户  ",
        )
        assert req.display_name == "测试用户"


class TestRegisterUserUseCase:
    """RegisterUserUseCase 测试"""

    def test_register_success(self, mock_user_repo, mock_email_service):
        """注册成功"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None
        mock_user_repo.save.return_value = None

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="newuser@example.com",
            password="StrongPass1!",
            username="newuser",
            display_name="新用户",
        )
        response, error = use_case.execute(request)

        assert error is None
        assert response is not None
        assert response.email == "newuser@example.com"
        assert response.username == "newuser"
        assert response.display_name == "新用户"
        assert response.email_verification_sent is True
        assert response.user_id is not None
        mock_user_repo.save.assert_called_once()
        mock_email_service.send_verification_email.assert_called_once()

    def test_register_empty_email(self, mock_user_repo, mock_email_service):
        """空邮箱返回错误"""
        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert "Email is required" in error
        mock_user_repo.save.assert_not_called()

    def test_register_empty_username(self, mock_user_repo, mock_email_service):
        """空用户名返回错误"""
        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert "Username is required" in error

    def test_register_empty_display_name(self, mock_user_repo, mock_email_service):
        """空显示名返回错误"""
        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert "Display name is required" in error

    def test_register_weak_password(self, mock_user_repo, mock_email_service):
        """弱密码返回错误"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="weak",
            username="testuser",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert error is not None
        mock_user_repo.save.assert_not_called()

    def test_register_email_already_exists(self, mock_user_repo, mock_email_service, sample_user):
        """邮箱已被注册"""
        mock_user_repo.find_by_email.return_value = sample_user

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert "Email already registered" in error
        mock_user_repo.save.assert_not_called()

    def test_register_username_already_taken(self, mock_user_repo, mock_email_service, sample_user):
        """用户名已被占用"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = sample_user

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="new@example.com",
            password="TestPass1!",
            username="existinguser",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert response is None
        assert "Username already taken" in error
        mock_user_repo.save.assert_not_called()

    def test_register_password_is_hashed(self, mock_user_repo, mock_email_service):
        """用户密码被哈希存储，不是明文"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None
        saved_user = None

        def capture_save(user):
            nonlocal saved_user
            saved_user = user

        mock_user_repo.save.side_effect = capture_save

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="MySecretPass1!",
            username="testuser",
            display_name="测试",
        )
        use_case.execute(request)

        assert saved_user is not None
        assert saved_user.password_hash != "MySecretPass1!"
        assert len(saved_user.password_hash) > 0

    def test_register_verification_token_generated(self, mock_user_repo, mock_email_service):
        """生成邮箱验证令牌"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None
        saved_user = None

        def capture_save(user):
            nonlocal saved_user
            saved_user = user

        mock_user_repo.save.side_effect = capture_save

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        use_case.execute(request)

        assert saved_user.email_verification_token is not None
        assert len(saved_user.email_verification_token) > 0
        assert saved_user.email_verified is False

    def test_register_verification_email_contains_url(self, mock_user_repo, mock_email_service):
        """验证邮件包含正确的验证链接"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://app.example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        use_case.execute(request)

        call_args = mock_email_service.send_verification_email.call_args
        verif_url = call_args[1].get("verification_url", "") or ""
        assert "https://app.example.com/verify-email?token=" in verif_url

    def test_register_email_failure_still_creates_user(self, mock_user_repo, mock_email_service):
        """邮件发送失败但用户仍被创建"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None
        mock_email_service.send_verification_email.return_value = (False, "SMTP error")

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        response, error = use_case.execute(request)

        assert error is None
        assert response is not None
        assert response.email_verification_sent is False
        mock_user_repo.save.assert_called_once()

    def test_register_generates_user_id(self, mock_user_repo, mock_email_service):
        """新用户有 id"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        request = RegisterUserRequest(
            email="test@example.com",
            password="TestPass1!",
            username="testuser",
            display_name="测试",
        )
        response, _ = use_case.execute(request)

        assert response.user_id is not None
        assert len(response.user_id) > 0

    def test_register_two_users_different_ids(self, mock_user_repo, mock_email_service):
        """两个用户的 id 不同"""
        mock_user_repo.find_by_email.return_value = None
        mock_user_repo.find_by_username.return_value = None

        use_case = RegisterUserUseCase(
            mock_user_repo,
            base_url="https://example.com",
            email_service=mock_email_service,
        )
        r1 = RegisterUserRequest(
            email="user1@example.com",
            password="TestPass1!",
            username="user1",
            display_name="用户1",
        )
        r2 = RegisterUserRequest(
            email="user2@example.com",
            password="TestPass1!",
            username="user2",
            display_name="用户2",
        )

        resp1, _ = use_case.execute(r1)
        resp2, _ = use_case.execute(r2)

        assert resp1.user_id != resp2.user_id


class TestVerifyEmailUseCase:
    """VerifyEmailUseCase 测试"""

    def test_verify_success(self, mock_user_repo, sample_user):
        """邮箱验证成功"""
        mock_user_repo.find_by_verification_token.return_value = sample_user
        mock_user_repo.save.return_value = None

        use_case = VerifyEmailUseCase(mock_user_repo)
        request = VerifyEmailRequest(token="some_token")
        success, error = use_case.execute(request)

        assert success is True
        assert error is None
        assert sample_user.email_verified is True
        assert sample_user.email_verification_token is None
        mock_user_repo.save.assert_called_once()

    def test_verify_empty_token(self, mock_user_repo):
        """空 token 返回错误"""
        use_case = VerifyEmailUseCase(mock_user_repo)
        request = VerifyEmailRequest(token="")
        success, error = use_case.execute(request)

        assert success is False
        assert "Verification token is required" in error
        mock_user_repo.save.assert_not_called()

    def test_verify_invalid_token(self, mock_user_repo):
        """无效 token 返回错误"""
        mock_user_repo.find_by_verification_token.return_value = None

        use_case = VerifyEmailUseCase(mock_user_repo)
        request = VerifyEmailRequest(token="invalid_token")
        success, error = use_case.execute(request)

        assert success is False
        assert "Invalid or expired" in error
        mock_user_repo.save.assert_not_called()

    def test_verify_already_verified(self, mock_user_repo, sample_user):
        """已验证的用户再次验证也返回成功"""
        sample_user.email_verified = True
        mock_user_repo.find_by_verification_token.return_value = sample_user

        use_case = VerifyEmailUseCase(mock_user_repo)
        request = VerifyEmailRequest(token="some_token")
        success, error = use_case.execute(request)

        assert success is True
        assert error is None
