# tests/unit/models/schema/test_customer.py
"""Unit tests for customer Pydantic schemas."""

import pytest
from pydantic import ValidationError

from app.modules.customers.schemas import (
    CustomerAddressCreate,
    CustomerAddressResponse,
    CustomerAddressUpdate,
    CustomerPreferencesUpdate,
    CustomerRegister,
    CustomerResponse,
    CustomerUpdate,
)


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerRegisterSchema:
    """Test CustomerRegister schema validation."""

    def test_valid_registration(self):
        """Test valid registration data."""
        customer = CustomerRegister(
            email="customer@example.com",
            password="Password123",  # noqa: SEC001
            first_name="John",
            last_name="Doe",
        )
        assert customer.email == "customer@example.com"
        assert customer.first_name == "John"
        assert customer.last_name == "Doe"

    def test_email_normalized_to_lowercase(self):
        """Test email is normalized to lowercase."""
        customer = CustomerRegister(
            email="Customer@Example.COM",
            password="Password123",  # noqa: SEC001
            first_name="John",
            last_name="Doe",
        )
        assert customer.email == "customer@example.com"

    def test_invalid_email(self):
        """Test invalid email raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerRegister(
                email="not-an-email",
                password="Password123",  # noqa: SEC001
                first_name="John",
                last_name="Doe",
            )
        assert "email" in str(exc_info.value).lower()

    def test_password_min_length(self):
        """Test password must be at least 8 characters."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerRegister(
                email="customer@example.com",
                password="Pass1",  # noqa: SEC001
                first_name="John",
                last_name="Doe",
            )
        assert "password" in str(exc_info.value).lower()

    def test_password_requires_digit(self):
        """Test password must contain at least one digit."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerRegister(
                email="customer@example.com",
                password="Password",  # noqa: SEC001
                first_name="John",
                last_name="Doe",
            )
        assert "digit" in str(exc_info.value).lower()

    def test_password_requires_letter(self):
        """Test password must contain at least one letter."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerRegister(
                email="customer@example.com",
                password="12345678",  # noqa: SEC001
                first_name="John",
                last_name="Doe",
            )
        assert "letter" in str(exc_info.value).lower()

    def test_first_name_required(self):
        """Test first_name is required."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerRegister(
                email="customer@example.com",
                password="Password123",  # noqa: SEC001
                last_name="Doe",
            )
        assert "first_name" in str(exc_info.value).lower()

    def test_marketing_consent_default(self):
        """Test marketing_consent defaults to False."""
        customer = CustomerRegister(
            email="customer@example.com",
            password="Password123",  # noqa: SEC001
            first_name="John",
            last_name="Doe",
        )
        assert customer.marketing_consent is False

    def test_optional_phone(self):
        """Test optional phone field."""
        customer = CustomerRegister(
            email="customer@example.com",
            password="Password123",  # noqa: SEC001
            first_name="John",
            last_name="Doe",
            phone="+352 123 456",
        )
        assert customer.phone == "+352 123 456"


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerUpdateSchema:
    """Test CustomerUpdate schema validation."""

    def test_partial_update(self):
        """Test partial update with only some fields."""
        update = CustomerUpdate(first_name="Jane")
        assert update.first_name == "Jane"
        assert update.last_name is None
        assert update.email is None

    def test_empty_update_is_valid(self):
        """Test empty update is valid."""
        update = CustomerUpdate()
        assert update.model_dump(exclude_unset=True) == {}

    def test_email_normalized_to_lowercase(self):
        """Test email is normalized to lowercase."""
        update = CustomerUpdate(email="NewEmail@Example.COM")
        assert update.email == "newemail@example.com"


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerResponseSchema:
    """Test CustomerResponse schema."""

    def test_from_dict(self):
        """Test creating response from dict."""
        from datetime import datetime

        data = {
            "id": 1,
            "store_id": 1,
            "email": "customer@example.com",
            "first_name": "John",
            "last_name": "Doe",
            "phone": None,
            "customer_number": "CUST001",
            "marketing_consent": False,
            "preferred_language": "fr",
            "is_active": True,
            "created_at": datetime.now(),
            "updated_at": datetime.now(),
        }
        response = CustomerResponse(**data)
        assert response.id == 1
        assert response.customer_number == "CUST001"
        assert response.preferred_language == "fr"


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerAddressCreateSchema:
    """Test CustomerAddressCreate schema validation."""

    def test_valid_shipping_address(self):
        """Test valid shipping address creation."""
        address = CustomerAddressCreate(
            address_type="shipping",
            first_name="John",
            last_name="Doe",
            address_line_1="123 Main St",
            city="Luxembourg",
            postal_code="L-1234",
            country_name="Luxembourg",
            country_iso="LU",
        )
        assert address.address_type == "shipping"
        assert address.city == "Luxembourg"

    def test_valid_billing_address(self):
        """Test valid billing address creation."""
        address = CustomerAddressCreate(
            address_type="billing",
            first_name="John",
            last_name="Doe",
            address_line_1="123 Main St",
            city="Luxembourg",
            postal_code="L-1234",
            country_name="Luxembourg",
            country_iso="LU",
        )
        assert address.address_type == "billing"

    def test_invalid_address_type(self):
        """Test invalid address_type raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            CustomerAddressCreate(
                address_type="delivery",
                first_name="John",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_name="Luxembourg",
                country_iso="LU",
            )
        assert "address_type" in str(exc_info.value).lower()

    def test_is_default_defaults_to_false(self):
        """Test is_default defaults to False."""
        address = CustomerAddressCreate(
            address_type="shipping",
            first_name="John",
            last_name="Doe",
            address_line_1="123 Main St",
            city="Luxembourg",
            postal_code="L-1234",
            country_name="Luxembourg",
            country_iso="LU",
        )
        assert address.is_default is False

    def test_optional_address_line_2(self):
        """Test optional address_line_2 field."""
        address = CustomerAddressCreate(
            address_type="shipping",
            first_name="John",
            last_name="Doe",
            address_line_1="123 Main St",
            address_line_2="Apt 4B",
            city="Luxembourg",
            postal_code="L-1234",
            country_name="Luxembourg",
            country_iso="LU",
        )
        assert address.address_line_2 == "Apt 4B"

    def test_required_fields(self):
        """Test required fields."""
        with pytest.raises(ValidationError):
            CustomerAddressCreate(
                address_type="shipping",
                first_name="John",
                # missing last_name, address_line_1, city, postal_code, country
            )


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerAddressUpdateSchema:
    """Test CustomerAddressUpdate schema validation."""

    def test_partial_update(self):
        """Test partial update with only some fields."""
        update = CustomerAddressUpdate(city="Esch-sur-Alzette")
        assert update.city == "Esch-sur-Alzette"
        assert update.first_name is None

    def test_empty_update_is_valid(self):
        """Test empty update is valid."""
        update = CustomerAddressUpdate()
        assert update.model_dump(exclude_unset=True) == {}

    def test_address_type_validation(self):
        """Test address_type validation in update."""
        with pytest.raises(ValidationError):
            CustomerAddressUpdate(address_type="invalid")

    def test_valid_address_type_update(self):
        """Test valid address_type values in update."""
        shipping = CustomerAddressUpdate(address_type="shipping")
        billing = CustomerAddressUpdate(address_type="billing")
        assert shipping.address_type == "shipping"
        assert billing.address_type == "billing"


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerAddressResponseSchema:
    """Test CustomerAddressResponse schema."""

    def test_from_dict(self):
        """Test creating response from dict."""
        from datetime import datetime

        data = {
            "id": 1,
            "store_id": 1,
            "customer_id": 1,
            "address_type": "shipping",
            "first_name": "John",
            "last_name": "Doe",
            "company": None,
            "address_line_1": "123 Main St",
            "address_line_2": None,
            "city": "Luxembourg",
            "postal_code": "L-1234",
            "country_name": "Luxembourg",
            "country_iso": "LU",
            "is_default": True,
            "created_at": datetime.now(),
            "updated_at": datetime.now(),
        }
        response = CustomerAddressResponse(**data)
        assert response.id == 1
        assert response.is_default is True
        assert response.address_type == "shipping"


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerPreferencesUpdateSchema:
    """Test CustomerPreferencesUpdate schema validation."""

    def test_partial_update(self):
        """Test partial update with only some fields."""
        update = CustomerPreferencesUpdate(marketing_consent=True)
        assert update.marketing_consent is True
        assert update.preferred_language is None

    def test_language_update(self):
        """Test language preference update."""
        update = CustomerPreferencesUpdate(preferred_language="fr")
        assert update.preferred_language == "fr"

    def test_currency_update(self):
        """Test currency preference update."""
        update = CustomerPreferencesUpdate(currency="EUR")
        assert update.currency == "EUR"

    def test_notification_preferences(self):
        """Test notification preferences dict."""
        update = CustomerPreferencesUpdate(
            notification_preferences={
                "email": True,
                "sms": False,
                "push": True,
            }
        )
        assert update.notification_preferences["email"] is True
        assert update.notification_preferences["sms"] is False
