"""
Tests for schema validators (app/schemas/validators.py).

Covers all edge cases in validation functions:
- validate_password_strength
- validate_phone_number (lines 115, 119)
- validate_email_format (line 148)
- validate_slug (lines 170-183)
"""

import pytest

from app.schemas.validators import (
    validate_email_format,
    validate_password_strength,
    validate_phone_number,
    validate_slug,
)


class TestPasswordStrengthValidator:
    """Test password strength validation."""

    def test_valid_strong_password(self):
        """Test that a strong password passes validation."""
        password = "MySecureP@ss123"
        result = validate_password_strength(password)
        assert result == password

    def test_password_too_short(self):
        """Test that password shorter than 12 characters is rejected."""
        with pytest.raises(ValueError, match="at least 12 characters long"):
            validate_password_strength("Short1!")

    def test_common_password_rejected(self):
        """Test that common passwords are rejected."""
        # "password1234" is in COMMON_PASSWORDS and is 12 chars
        # Common password check happens before character type checks
        with pytest.raises(ValueError, match="too common"):
            validate_password_strength("password1234")

    def test_password_missing_lowercase(self):
        """Test that password without lowercase is rejected."""
        with pytest.raises(ValueError, match="at least one lowercase letter"):
            validate_password_strength("ALLUPPERCASE123!")

    def test_password_missing_uppercase(self):
        """Test that password without uppercase is rejected."""
        with pytest.raises(ValueError, match="at least one uppercase letter"):
            validate_password_strength("alllowercase123!")

    def test_password_missing_digit(self):
        """Test that password without digit is rejected."""
        with pytest.raises(ValueError, match="at least one digit"):
            validate_password_strength("NoDigitsHere!")

    def test_password_missing_special_char(self):
        """Test that password without special character is rejected."""
        with pytest.raises(ValueError, match="at least one special character"):
            validate_password_strength("NoSpecialChar123")


class TestPhoneNumberValidator:
    """Test phone number validation."""

    def test_valid_international_format(self):
        """Test valid international phone number."""
        result = validate_phone_number("+12345678901")
        assert result == "+12345678901"

    def test_valid_local_format(self):
        """Test valid local phone number."""
        result = validate_phone_number("0123456789")
        assert result == "0123456789"

    def test_valid_with_formatting(self):
        """Test phone number with formatting characters."""
        result = validate_phone_number("+1 (555) 123-4567")
        assert result == "+15551234567"

    def test_none_returns_none(self):
        """Test that None input returns None."""
        result = validate_phone_number(None)
        assert result is None

    def test_empty_string_rejected(self):
        """Test that empty string is rejected."""
        with pytest.raises(ValueError, match="cannot be empty"):
            validate_phone_number("")

    def test_whitespace_only_rejected(self):
        """Test that whitespace-only string is rejected."""
        with pytest.raises(ValueError, match="cannot be empty"):
            validate_phone_number("   ")

    def test_invalid_prefix_rejected(self):
        """Test that invalid prefix is rejected."""
        with pytest.raises(ValueError, match="must start with \\+ or 0"):
            validate_phone_number("12345678901")

    def test_too_short_rejected(self):
        """Test that too-short phone number is rejected."""
        with pytest.raises(ValueError, match="must start with \\+ or 0"):
            validate_phone_number("+1234567")  # Only 7 digits after +

    def test_too_long_rejected(self):
        """Test that too-long phone number is rejected."""
        with pytest.raises(ValueError, match="must start with \\+ or 0"):
            validate_phone_number("+123456789012345")  # 15 digits after +

    def test_multiple_plus_symbols_rejected(self):
        r"""Test phone number with multiple + symbols.

        Note: Line 115 is defensive code - the regex check at line 110 catches this first.
        The regex ^(?:\+[0-9]{8,14}|0[0-9]{8,14})$ only allows + at the start.
        """
        with pytest.raises(
            ValueError, match="must start with \\+ or 0 followed by 8-14 digits"
        ):
            validate_phone_number("+1234+5678901")

    def test_non_digit_after_prefix_rejected(self):
        """Test phone number with non-digit characters after prefix.

        Note: Line 119 is defensive code - the regex check at line 110 catches this first.
        The regex requires all digits after the prefix.
        """
        with pytest.raises(ValueError, match="must start with \\+ or 0"):
            validate_phone_number("+123abc45678")


class TestEmailFormatValidator:
    """Test email format validation."""

    def test_valid_email_lowercase(self):
        """Test valid lowercase email."""
        result = validate_email_format("test@example.com")
        assert result == "test@example.com"

    def test_email_normalized_to_lowercase(self):
        """Test email is normalized to lowercase (covers line 148)."""
        result = validate_email_format("Test@Example.COM")
        assert result == "test@example.com"

    def test_email_with_uppercase_domain(self):
        """Test email with uppercase domain is normalized."""
        result = validate_email_format("user@EXAMPLE.COM")
        assert result == "user@example.com"


class TestSlugValidator:
    """Test slug validation."""

    def test_valid_slug_lowercase_letters(self):
        """Test valid slug with lowercase letters."""
        result = validate_slug("test-slug")
        assert result == "test-slug"

    def test_valid_slug_with_numbers(self):
        """Test valid slug with numbers."""
        result = validate_slug("test-123")
        assert result == "test-123"

    def test_valid_slug_minimal_length(self):
        """Test valid slug with minimal length (2 characters)."""
        result = validate_slug("ab")
        assert result == "ab"

    def test_empty_slug_rejected(self):
        """Test empty slug is rejected (covers line 170)."""
        with pytest.raises(ValueError, match="at least 2 characters long"):
            validate_slug("")

    def test_single_character_slug_rejected(self):
        """Test single character slug is rejected (covers line 170)."""
        with pytest.raises(ValueError, match="at least 2 characters long"):
            validate_slug("a")

    def test_slug_too_long_rejected(self):
        """Test slug longer than 50 characters is rejected (covers line 173)."""
        long_slug = "a" * 51
        with pytest.raises(ValueError, match="at most 50 characters long"):
            validate_slug(long_slug)

    def test_slug_max_length_accepted(self):
        """Test slug with exactly 50 characters is accepted."""
        max_slug = "a" * 50
        result = validate_slug(max_slug)
        assert result == max_slug

    def test_slug_starts_with_hyphen_rejected(self):
        """Test slug starting with hyphen is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="cannot start or end with a hyphen"):
            validate_slug("-test")

    def test_slug_ends_with_hyphen_rejected(self):
        """Test slug ending with hyphen is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="cannot start or end with a hyphen"):
            validate_slug("test-")

    def test_slug_consecutive_hyphens_rejected(self):
        """Test slug with consecutive hyphens is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="cannot contain consecutive hyphens"):
            validate_slug("test--slug")

    def test_slug_uppercase_letters_rejected(self):
        """Test slug with uppercase letters is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="only contain lowercase letters"):
            validate_slug("Test-Slug")

    def test_slug_special_characters_rejected(self):
        """Test slug with special characters is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="only contain lowercase letters"):
            validate_slug("test_slug")

    def test_slug_spaces_rejected(self):
        """Test slug with spaces is rejected (covers line 177)."""
        with pytest.raises(ValueError, match="only contain lowercase letters"):
            validate_slug("test slug")
