"""Tests for authentication Pydantic schemas."""

from datetime import datetime, timedelta
from uuid import uuid4

import pytest
from pydantic import ValidationError

from preloop.schemas.auth import (
    ApiKeyCreate,
    ApiKeyResponse,
    ApiKeySummary,
    ApiUsageStatistics,
    EmailVerificationRequest,
    LoginRequest,
    PasswordChangeRequest,
    PasswordResetConfirmRequest,
    PasswordResetRequest,
    RefreshRequest,
    Token,
    TokenData,
    User,
    AuthUserCreate,
    UserInDB,
    AuthUserResponse,
    AuthUserUpdate,
)


class TestToken:
    """Test Token schema."""

    def test_create_token(self):
        """Test creating Token with all fields."""
        token = Token(
            access_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
            refresh_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
            token_type="bearer",
            expires_in=3600,
        )

        assert token.access_token == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        assert token.refresh_token == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        assert token.token_type == "bearer"
        assert token.expires_in == 3600


class TestTokenData:
    """Test TokenData schema."""

    def test_create_with_defaults(self):
        """Test creating TokenData with default values."""
        token_data = TokenData()

        assert token_data.sub is None
        assert token_data.scopes == []
        assert token_data.exp is None
        assert token_data.refresh is False

    def test_create_with_all_fields(self):
        """Test creating TokenData with all fields."""
        exp_time = datetime.now() + timedelta(hours=1)
        token_data = TokenData(
            sub="user123",
            scopes=["read:issues", "write:issues"],
            exp=exp_time,
            refresh=True,
        )

        assert token_data.sub == "user123"
        assert token_data.scopes == ["read:issues", "write:issues"]
        assert token_data.exp == exp_time
        assert token_data.refresh is True


class TestUser:
    """Test User schema."""

    def test_create_minimal_user(self):
        """Test creating User with minimal fields."""
        user = User(username="testuser")

        assert user.username == "testuser"
        assert user.email is None
        assert user.full_name is None
        assert user.disabled is None
        assert user.email_verified is None

    def test_create_complete_user(self):
        """Test creating User with all fields."""
        user = User(
            username="johndoe",
            email="john@example.com",
            full_name="John Doe",
            disabled=False,
            email_verified=True,
        )

        assert user.username == "johndoe"
        assert user.email == "john@example.com"
        assert user.full_name == "John Doe"
        assert user.disabled is False
        assert user.email_verified is True


class TestUserInDB:
    """Test UserInDB schema."""

    def test_inherits_from_user(self):
        """Test that UserInDB inherits from User."""
        user_in_db = UserInDB(
            username="testuser",
            email="test@example.com",
            hashed_password="$2b$12$...",
        )

        assert isinstance(user_in_db, User)
        assert user_in_db.username == "testuser"
        assert user_in_db.hashed_password == "$2b$12$..."


class TestAuthUserCreate:
    """Test AuthUserCreate schema."""

    def test_create_with_required_fields(self):
        """Test creating AuthUserCreate with required fields."""
        user_create = AuthUserCreate(
            username="newuser",
            email="new@example.com",
            password="securepassword123",
        )

        assert user_create.username == "newuser"
        assert user_create.email == "new@example.com"
        assert user_create.password == "securepassword123"
        assert user_create.full_name is None

    def test_create_with_full_name(self):
        """Test creating AuthUserCreate with optional full_name."""
        user_create = AuthUserCreate(
            username="newuser",
            email="new@example.com",
            password="securepassword123",
            full_name="New User",
        )

        assert user_create.full_name == "New User"

    def test_username_length_validation(self):
        """Test username length validation."""
        # Too short
        with pytest.raises(ValidationError):
            AuthUserCreate(
                username="ab",
                email="test@example.com",
                password="password123",
            )

        # Too long
        with pytest.raises(ValidationError):
            AuthUserCreate(
                username="a" * 51,
                email="test@example.com",
                password="password123",
            )

    def test_username_alphanumeric_validation(self):
        """Test username alphanumeric validation."""
        with pytest.raises(ValidationError) as exc_info:
            AuthUserCreate(
                username="invalid-username",
                email="test@example.com",
                password="password123",
            )

        errors = exc_info.value.errors()
        assert any("alphanumeric" in str(error).lower() for error in errors)

    def test_password_length_validation(self):
        """Test password minimum length validation."""
        with pytest.raises(ValidationError):
            AuthUserCreate(
                username="testuser",
                email="test@example.com",
                password="short",
            )

    def test_email_validation(self):
        """Test email format validation."""
        with pytest.raises(ValidationError):
            AuthUserCreate(
                username="testuser",
                email="invalid-email",
                password="password123",
            )


class TestAuthUserUpdate:
    """Test AuthUserUpdate schema."""

    def test_create_empty_update(self):
        """Test creating empty AuthUserUpdate."""
        update = AuthUserUpdate()

        assert update.full_name is None

    def test_create_with_full_name(self):
        """Test creating AuthUserUpdate with full_name."""
        update = AuthUserUpdate(full_name="Updated Name")

        assert update.full_name == "Updated Name"


class TestAuthUserResponse:
    """Test AuthUserResponse schema."""

    def test_create_user_response(self):
        """Test creating AuthUserResponse."""
        response = AuthUserResponse(
            username="testuser",
            email="test@example.com",
            full_name="Test User",
            email_verified=True,
        )

        assert response.username == "testuser"
        assert response.email == "test@example.com"
        assert response.full_name == "Test User"
        assert response.email_verified is True

    def test_email_verified_required(self):
        """Test that email_verified is required."""
        with pytest.raises(ValidationError):
            AuthUserResponse(
                username="testuser",
                email="test@example.com",
            )


class TestLoginRequest:
    """Test LoginRequest schema."""

    def test_create_login_request(self):
        """Test creating LoginRequest."""
        request = LoginRequest(username="testuser", password="password123")

        assert request.username == "testuser"
        assert request.password == "password123"


class TestRefreshRequest:
    """Test RefreshRequest schema."""

    def test_create_refresh_request(self):
        """Test creating RefreshRequest."""
        request = RefreshRequest(
            refresh_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        )

        assert request.refresh_token == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."


class TestEmailVerificationRequest:
    """Test EmailVerificationRequest schema."""

    def test_create_email_verification_request(self):
        """Test creating EmailVerificationRequest."""
        request = EmailVerificationRequest(token="verification-token-123")

        assert request.token == "verification-token-123"


class TestPasswordResetRequest:
    """Test PasswordResetRequest schema."""

    def test_create_password_reset_request(self):
        """Test creating PasswordResetRequest."""
        request = PasswordResetRequest(email="user@example.com")

        assert request.email == "user@example.com"

    def test_email_validation(self):
        """Test email validation."""
        with pytest.raises(ValidationError):
            PasswordResetRequest(email="invalid-email")


class TestPasswordResetConfirmRequest:
    """Test PasswordResetConfirmRequest schema."""

    def test_create_password_reset_confirm(self):
        """Test creating PasswordResetConfirmRequest."""
        request = PasswordResetConfirmRequest(
            token="reset-token-123",
            new_password="newsecurepassword",
        )

        assert request.token == "reset-token-123"
        assert request.new_password == "newsecurepassword"

    def test_password_length_validation(self):
        """Test new password length validation."""
        with pytest.raises(ValidationError):
            PasswordResetConfirmRequest(
                token="reset-token-123",
                new_password="short",
            )


class TestPasswordChangeRequest:
    """Test PasswordChangeRequest schema."""

    def test_create_password_change_request(self):
        """Test creating PasswordChangeRequest."""
        request = PasswordChangeRequest(
            current_password="oldpassword123",
            new_password="newpassword123",
        )

        assert request.current_password == "oldpassword123"
        assert request.new_password == "newpassword123"

    def test_new_password_length_validation(self):
        """Test new password length validation."""
        with pytest.raises(ValidationError):
            PasswordChangeRequest(
                current_password="oldpassword123",
                new_password="short",
            )


class TestApiKeyCreate:
    """Test ApiKeyCreate schema."""

    def test_create_with_required_fields(self):
        """Test creating ApiKeyCreate with required fields."""
        api_key = ApiKeyCreate(name="My API Key")

        assert api_key.name == "My API Key"
        assert api_key.expires_at is None
        assert api_key.scopes == []

    def test_create_with_all_fields(self):
        """Test creating ApiKeyCreate with all fields."""
        expires_at = datetime.now() + timedelta(days=30)
        api_key = ApiKeyCreate(
            name="Production Key",
            expires_at=expires_at,
            scopes=["read:issues", "write:issues"],
        )

        assert api_key.name == "Production Key"
        assert api_key.expires_at == expires_at
        assert api_key.scopes == ["read:issues", "write:issues"]

    def test_name_length_validation(self):
        """Test name length validation."""
        # Empty name
        with pytest.raises(ValidationError):
            ApiKeyCreate(name="")

        # Name too long
        with pytest.raises(ValidationError):
            ApiKeyCreate(name="a" * 101)


class TestApiKeyResponse:
    """Test ApiKeyResponse schema."""

    def test_create_api_key_response(self):
        """Test creating ApiKeyResponse with all fields."""
        key_id = uuid4()
        user_id = uuid4()
        created_at = datetime.now()
        expires_at = datetime.now() + timedelta(days=30)
        last_used_at = datetime.now()

        response = ApiKeyResponse(
            id=key_id,
            name="Test Key",
            key="sk_test_...",
            created_at=created_at,
            expires_at=expires_at,
            scopes=["read:issues"],
            user_id=user_id,
            last_used_at=last_used_at,
        )

        assert response.id == key_id
        assert response.name == "Test Key"
        assert response.key == "sk_test_..."
        assert response.created_at == created_at
        assert response.expires_at == expires_at
        assert response.scopes == ["read:issues"]
        assert response.user_id == user_id
        assert response.last_used_at == last_used_at

    def test_create_with_defaults(self):
        """Test creating with optional fields as None."""
        key_id = uuid4()
        user_id = uuid4()
        created_at = datetime.now()

        response = ApiKeyResponse(
            id=key_id,
            name="Test Key",
            key="sk_test_...",
            created_at=created_at,
            user_id=user_id,
        )

        assert response.expires_at is None
        assert response.scopes == []
        assert response.last_used_at is None


class TestApiKeySummary:
    """Test ApiKeySummary schema."""

    def test_create_api_key_summary(self):
        """Test creating ApiKeySummary (without key field)."""
        key_id = uuid4()
        created_at = datetime.now()
        expires_at = datetime.now() + timedelta(days=30)

        summary = ApiKeySummary(
            id=key_id,
            name="Test Key",
            created_at=created_at,
            expires_at=expires_at,
            scopes=["read:issues"],
            last_used_at=None,
        )

        assert summary.id == key_id
        assert summary.name == "Test Key"
        assert summary.created_at == created_at
        assert summary.expires_at == expires_at
        assert summary.scopes == ["read:issues"]
        assert summary.last_used_at is None


class TestApiUsageStatistics:
    """Test ApiUsageStatistics schema."""

    def test_create_usage_statistics(self):
        """Test creating ApiUsageStatistics."""
        stats = ApiUsageStatistics(
            total_requests=1000,
            requests_by_date={"2025-01-15": 450, "2025-01-16": 550},
            issues_created=50,
            issues_updated=75,
            issues_closed=25,
            requests_by_endpoint={
                "/api/issues": 600,
                "/api/comments": 300,
                "/api/projects": 100,
            },
        )

        assert stats.total_requests == 1000
        assert stats.requests_by_date["2025-01-15"] == 450
        assert stats.issues_created == 50
        assert stats.issues_updated == 75
        assert stats.issues_closed == 25
        assert stats.requests_by_endpoint["/api/issues"] == 600

    def test_empty_statistics(self):
        """Test creating empty statistics."""
        stats = ApiUsageStatistics(
            total_requests=0,
            requests_by_date={},
            issues_created=0,
            issues_updated=0,
            issues_closed=0,
            requests_by_endpoint={},
        )

        assert stats.total_requests == 0
        assert len(stats.requests_by_date) == 0
        assert len(stats.requests_by_endpoint) == 0
