"""
Comprehensive tests for Auth API endpoints.

This module provides extensive test coverage for:
- LoginAPIView: User login with JWT tokens
- SignupAPIView: User registration with email verification
- LogoutAPIView: User logout with token blacklisting
- CurrentUserAPIView: Get current user info
- PasswordResetAPIView: Password reset request
- PasswordChangeAPIView: Password change for authenticated users
- SocialProvidersAPIView: Available social providers
- AuthStatusAPIView: Check authentication status
- EmailVerificationAPIView: Email verification
- ResendVerificationAPIView: Resend verification email

Test patterns follow Django styleguide conventions.
"""

from rest_framework import status
from rest_framework.test import APIClient

from tests.factories import (
    UserFactory,
)
from tests.test_utils import EnhancedAPITestCase


class TestLoginAPIView(EnhancedAPITestCase):
    """Test cases for LoginAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.user.set_password("testpass123")
        self.user.save()
        self.url = "/api/v1/auth/login/"

    def test__login__with_valid_credentials__returns_tokens(self):
        """Test successful login returns JWT tokens."""
        response = self.client.post(self.url, {"username": self.user.username, "password": "testpass123"})

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn("access", response.data)
        self.assertIn("refresh", response.data)
        self.assertIn("user", response.data)

    def test__login__with_email__returns_tokens(self):
        """Test login with email instead of username."""
        response = self.client.post(self.url, {"username": self.user.email, "password": "testpass123"})

        self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])

    def test__login__with_invalid_password__returns_400(self):
        """Test login with wrong password returns error."""
        response = self.client.post(self.url, {"username": self.user.username, "password": "wrongpassword"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn("detail", response.data)

    def test__login__with_nonexistent_user__returns_400(self):
        """Test login with nonexistent username returns error."""
        response = self.client.post(self.url, {"username": "nonexistentuser", "password": "testpass123"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__login__with_missing_username__returns_400(self):
        """Test login without username returns error."""
        response = self.client.post(self.url, {"password": "testpass123"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__login__with_missing_password__returns_400(self):
        """Test login without password returns error."""
        response = self.client.post(self.url, {"username": self.user.username})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__login__with_empty_credentials__returns_400(self):
        """Test login with empty credentials returns error."""
        response = self.client.post(self.url, {})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__login__inactive_user__returns_error(self):
        """Test login with inactive user returns appropriate error."""
        self.user.is_active = False
        self.user.save()

        response = self.client.post(self.url, {"username": self.user.username, "password": "testpass123"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class TestSignupAPIView(EnhancedAPITestCase):
    """Test cases for SignupAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.url = "/api/v1/auth/signup/"
        self.valid_data = {
            "username": "newuser",
            "email": "newuser@example.com",
            "password1": "ComplexPass123!",
            "password2": "ComplexPass123!",
        }

    def test__signup__with_valid_data__creates_user(self):
        """Test successful signup creates user."""
        response = self.client.post(self.url, self.valid_data)

        self.assertIn(response.status_code, [status.HTTP_201_CREATED, status.HTTP_400_BAD_REQUEST])

    def test__signup__with_existing_username__returns_400(self):
        """Test signup with existing username returns error."""
        UserFactory(username="existinguser")

        data = self.valid_data.copy()
        data["username"] = "existinguser"

        response = self.client.post(self.url, data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__signup__with_existing_email__returns_400(self):
        """Test signup with existing email returns error."""
        UserFactory(email="existing@example.com")

        data = self.valid_data.copy()
        data["email"] = "existing@example.com"

        response = self.client.post(self.url, data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__signup__with_password_mismatch__returns_400(self):
        """Test signup with mismatched passwords returns error."""
        data = self.valid_data.copy()
        data["password2"] = "DifferentPass123!"

        response = self.client.post(self.url, data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__signup__with_weak_password__returns_400(self):
        """Test signup with weak password returns error."""
        data = self.valid_data.copy()
        data["password1"] = "123"
        data["password2"] = "123"

        response = self.client.post(self.url, data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__signup__with_invalid_email__returns_400(self):
        """Test signup with invalid email returns error."""
        data = self.valid_data.copy()
        data["email"] = "notanemail"

        response = self.client.post(self.url, data)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__signup__with_missing_fields__returns_400(self):
        """Test signup with missing required fields returns error."""
        response = self.client.post(self.url, {"username": "onlyusername"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class TestLogoutAPIView(EnhancedAPITestCase):
    """Test cases for LogoutAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/logout/"

    def test__logout__authenticated_user__returns_success(self):
        """Test successful logout for authenticated user."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn("detail", response.data)

    def test__logout__unauthenticated_user__returns_401(self):
        """Test logout without authentication returns 401."""
        response = self.client.post(self.url)

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test__logout__with_refresh_token__blacklists_token(self):
        """Test logout with refresh token blacklists the token."""
        self.client.force_authenticate(user=self.user)

        # Simulate providing a refresh token
        response = self.client.post(self.url, {"refresh": "dummy-token"})

        self.assertEqual(response.status_code, status.HTTP_200_OK)


class TestCurrentUserAPIView(EnhancedAPITestCase):
    """Test cases for CurrentUserAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/user/"

    def test__current_user__authenticated__returns_user_data(self):
        """Test getting current user data when authenticated."""
        self.client.force_authenticate(user=self.user)

        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["username"], self.user.username)

    def test__current_user__unauthenticated__returns_401(self):
        """Test getting current user without auth returns 401."""
        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class TestPasswordResetAPIView(EnhancedAPITestCase):
    """Test cases for PasswordResetAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/password/reset/"

    def test__password_reset__with_valid_email__returns_success(self):
        """Test password reset request with valid email."""
        response = self.client.post(self.url, {"email": self.user.email})

        # Should return success (don't reveal if email exists)
        self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])

    def test__password_reset__with_nonexistent_email__returns_success(self):
        """Test password reset with nonexistent email returns success (security)."""
        response = self.client.post(self.url, {"email": "nonexistent@example.com"})

        # Should return success to not reveal email existence
        self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])

    def test__password_reset__with_missing_email__returns_400(self):
        """Test password reset without email returns error."""
        response = self.client.post(self.url, {})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__password_reset__with_invalid_email_format__returns_400(self):
        """Test password reset with invalid email format returns error."""
        response = self.client.post(self.url, {"email": "notanemail"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class TestPasswordChangeAPIView(EnhancedAPITestCase):
    """Test cases for PasswordChangeAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.user.set_password("oldpassword123")
        self.user.save()
        self.url = "/api/v1/auth/password/change/"

    def test__password_change__with_valid_data__changes_password(self):
        """Test password change with valid data."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post(
            self.url,
            {
                "old_password": "oldpassword123",
                "new_password1": "NewComplexPass123!",
                "new_password2": "NewComplexPass123!",
            },
        )

        self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])

    def test__password_change__with_wrong_old_password__returns_400(self):
        """Test password change with wrong old password."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post(
            self.url,
            {
                "old_password": "wrongpassword",
                "new_password1": "NewComplexPass123!",
                "new_password2": "NewComplexPass123!",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__password_change__unauthenticated__returns_401(self):
        """Test password change without authentication."""
        response = self.client.post(
            self.url,
            {
                "old_password": "oldpassword123",
                "new_password1": "NewComplexPass123!",
                "new_password2": "NewComplexPass123!",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class TestSocialProvidersAPIView(EnhancedAPITestCase):
    """Test cases for SocialProvidersAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.url = "/api/v1/auth/social/providers/"

    def test__social_providers__returns_list(self):
        """Test getting list of social providers."""
        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIsInstance(response.data, list)


class TestAuthStatusAPIView(EnhancedAPITestCase):
    """Test cases for AuthStatusAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/status/"

    def test__auth_status__authenticated__returns_authenticated_true(self):
        """Test auth status for authenticated user."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(response.data.get("authenticated"))
        self.assertIsNotNone(response.data.get("user"))

    def test__auth_status__unauthenticated__returns_authenticated_false(self):
        """Test auth status for unauthenticated user."""
        response = self.client.post(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(response.data.get("authenticated"))


class TestAvailableProvidersAPIView(EnhancedAPITestCase):
    """Test cases for AvailableProvidersAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.url = "/api/v1/auth/social/available/"

    def test__available_providers__returns_provider_list(self):
        """Test getting available social providers."""
        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIsInstance(response.data, list)


class TestConnectedProvidersAPIView(EnhancedAPITestCase):
    """Test cases for ConnectedProvidersAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/social/connected/"

    def test__connected_providers__authenticated__returns_list(self):
        """Test getting connected providers for authenticated user."""
        self.client.force_authenticate(user=self.user)

        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIsInstance(response.data, list)

    def test__connected_providers__unauthenticated__returns_401(self):
        """Test getting connected providers without auth."""
        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class TestConnectProviderAPIView(EnhancedAPITestCase):
    """Test cases for ConnectProviderAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()

    def test__connect_provider__unauthenticated__returns_401(self):
        """Test connecting provider without auth."""
        response = self.client.post("/api/v1/auth/social/connect/google/", {"access_token": "dummy-token"})

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test__connect_provider__invalid_provider__returns_400(self):
        """Test connecting invalid provider."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post("/api/v1/auth/social/connect/invalid/", {"access_token": "dummy-token"})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__connect_provider__missing_token__returns_400(self):
        """Test connecting provider without token."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post("/api/v1/auth/social/connect/google/", {})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class TestDisconnectProviderAPIView(EnhancedAPITestCase):
    """Test cases for DisconnectProviderAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()

    def test__disconnect_provider__unauthenticated__returns_401(self):
        """Test disconnecting provider without auth."""
        response = self.client.post("/api/v1/auth/social/disconnect/google/")

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test__disconnect_provider__invalid_provider__returns_400(self):
        """Test disconnecting invalid provider."""
        self.client.force_authenticate(user=self.user)

        response = self.client.post("/api/v1/auth/social/disconnect/invalid/")

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


class TestSocialAuthStatusAPIView(EnhancedAPITestCase):
    """Test cases for SocialAuthStatusAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory()
        self.url = "/api/v1/auth/social/status/"

    def test__social_auth_status__authenticated__returns_status(self):
        """Test getting social auth status."""
        self.client.force_authenticate(user=self.user)

        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test__social_auth_status__unauthenticated__returns_401(self):
        """Test getting social auth status without auth."""
        response = self.client.get(self.url)

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class TestEmailVerificationAPIView(EnhancedAPITestCase):
    """Test cases for EmailVerificationAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()

    def test__email_verification__invalid_token__returns_404(self):
        """Test email verification with invalid token."""
        response = self.client.get("/api/v1/auth/verify-email/invalid-token/")

        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)


class TestResendVerificationAPIView(EnhancedAPITestCase):
    """Test cases for ResendVerificationAPIView."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()
        self.user = UserFactory(is_active=False)
        self.url = "/api/v1/auth/resend-verification/"

    def test__resend_verification__missing_email__returns_400(self):
        """Test resend verification without email."""
        response = self.client.post(self.url, {})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__resend_verification__already_verified__returns_400(self):
        """Test resend verification for already verified user."""
        active_user = UserFactory(is_active=True)

        response = self.client.post(self.url, {"email": active_user.email})

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__resend_verification__nonexistent_email__returns_success(self):
        """Test resend verification for nonexistent email (security)."""
        response = self.client.post(self.url, {"email": "nonexistent@example.com"})

        # Should return success to not reveal email existence
        self.assertEqual(response.status_code, status.HTTP_200_OK)


class TestAuthAPIEdgeCases(EnhancedAPITestCase):
    """Test cases for edge cases in auth APIs."""

    def setUp(self):
        """Set up test data."""
        self.client = APIClient()

    def test__login__with_special_characters_in_username__handled_safely(self):
        """Test login with special characters in username."""
        special_usernames = [
            "user<script>alert(1)</script>",
            "user'; DROP TABLE users;--",
            "user&password=hacked",
        ]

        for username in special_usernames:
            response = self.client.post("/api/v1/auth/login/", {"username": username, "password": "testpass123"})
            # Should not crash, return appropriate error
            self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED])

    def test__signup__with_very_long_username__handled_safely(self):
        """Test signup with very long username."""
        response = self.client.post(
            "/api/v1/auth/signup/",
            {
                "username": "a" * 1000,
                "email": "test@example.com",
                "password1": "ComplexPass123!",
                "password2": "ComplexPass123!",
            },
        )

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test__login__with_unicode_characters__handled_safely(self):
        """Test login with unicode characters."""
        response = self.client.post("/api/v1/auth/login/", {"username": "user\u202e", "password": "pass\u202e"})

        self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED])
