# tests/unit/models/schema/test_order.py
"""Unit tests for order Pydantic schemas."""

from datetime import UTC, datetime

import pytest
from pydantic import ValidationError

from app.modules.orders.schemas import (
    AddressSnapshot,
    CustomerSnapshot,
    CustomerSnapshotResponse,
    OrderCreate,
    OrderItemCreate,
    OrderItemResponse,
    OrderListResponse,
    OrderResponse,
    OrderUpdate,
)


@pytest.mark.unit
@pytest.mark.schema
class TestOrderItemCreateSchema:
    """Test OrderItemCreate schema validation."""

    def test_valid_order_item(self):
        """Test valid order item creation."""
        item = OrderItemCreate(
            product_id=1,
            quantity=2,
        )
        assert item.product_id == 1
        assert item.quantity == 2

    def test_product_id_required(self):
        """Test product_id is required."""
        with pytest.raises(ValidationError) as exc_info:
            OrderItemCreate(quantity=2)
        assert "product_id" in str(exc_info.value).lower()

    def test_quantity_required(self):
        """Test quantity is required."""
        with pytest.raises(ValidationError) as exc_info:
            OrderItemCreate(product_id=1)
        assert "quantity" in str(exc_info.value).lower()

    def test_quantity_must_be_at_least_1(self):
        """Test quantity must be >= 1."""
        with pytest.raises(ValidationError) as exc_info:
            OrderItemCreate(
                product_id=1,
                quantity=0,
            )
        assert "quantity" in str(exc_info.value).lower()

    def test_negative_quantity_invalid(self):
        """Test negative quantity is invalid."""
        with pytest.raises(ValidationError):
            OrderItemCreate(
                product_id=1,
                quantity=-1,
            )


@pytest.mark.unit
@pytest.mark.schema
class TestAddressSnapshotSchema:
    """Test AddressSnapshot schema validation."""

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

    def test_required_fields(self):
        """Test required fields validation."""
        with pytest.raises(ValidationError):
            AddressSnapshot(
                first_name="John",
                # missing required fields
            )

    def test_optional_address_line_2(self):
        """Test optional address_line_2 field."""
        address = AddressSnapshot(
            first_name="John",
            last_name="Doe",
            address_line_1="123 Main St",
            address_line_2="Suite 500",
            city="Luxembourg",
            postal_code="L-1234",
            country_iso="LU",
        )
        assert address.address_line_2 == "Suite 500"

    def test_first_name_min_length(self):
        """Test first_name minimum length."""
        with pytest.raises(ValidationError):
            AddressSnapshot(
                first_name="",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_iso="LU",
            )

    def test_country_iso_min_length(self):
        """Test country_iso minimum length (2)."""
        with pytest.raises(ValidationError):
            AddressSnapshot(
                first_name="John",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_iso="L",  # Too short
            )


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerSnapshotSchema:
    """Test CustomerSnapshot schema validation."""

    def test_valid_customer(self):
        """Test valid customer snapshot."""
        customer = CustomerSnapshot(
            first_name="John",
            last_name="Doe",
            email="john@example.com",
            phone="+352123456",
            locale="en",
        )
        assert customer.first_name == "John"
        assert customer.email == "john@example.com"

    def test_optional_phone(self):
        """Test phone is optional."""
        customer = CustomerSnapshot(
            first_name="John",
            last_name="Doe",
            email="john@example.com",
        )
        assert customer.phone is None

    def test_optional_locale(self):
        """Test locale is optional."""
        customer = CustomerSnapshot(
            first_name="John",
            last_name="Doe",
            email="john@example.com",
        )
        assert customer.locale is None


@pytest.mark.unit
@pytest.mark.schema
class TestCustomerSnapshotResponseSchema:
    """Test CustomerSnapshotResponse schema."""

    def test_full_name_property(self):
        """Test full_name property."""
        response = CustomerSnapshotResponse(
            first_name="John",
            last_name="Doe",
            email="john@example.com",
            phone=None,
            locale=None,
        )
        assert response.full_name == "John Doe"


@pytest.mark.unit
@pytest.mark.schema
class TestOrderCreateSchema:
    """Test OrderCreate schema validation."""

    def test_valid_order(self):
        """Test valid order creation."""
        order = OrderCreate(
            items=[
                OrderItemCreate(product_id=1, quantity=2),
                OrderItemCreate(product_id=2, quantity=1),
            ],
            customer=CustomerSnapshot(
                first_name="John",
                last_name="Doe",
                email="john@example.com",
            ),
            shipping_address=AddressSnapshot(
                first_name="John",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_iso="LU",
            ),
        )
        assert len(order.items) == 2
        assert order.customer_id is None  # Optional for guest checkout

    def test_items_required(self):
        """Test items are required."""
        with pytest.raises(ValidationError) as exc_info:
            OrderCreate(
                customer=CustomerSnapshot(
                    first_name="John",
                    last_name="Doe",
                    email="john@example.com",
                ),
                shipping_address=AddressSnapshot(
                    first_name="John",
                    last_name="Doe",
                    address_line_1="123 Main St",
                    city="Luxembourg",
                    postal_code="L-1234",
                    country_iso="LU",
                ),
            )
        assert "items" in str(exc_info.value).lower()

    def test_items_must_not_be_empty(self):
        """Test items list must have at least 1 item."""
        with pytest.raises(ValidationError) as exc_info:
            OrderCreate(
                items=[],
                customer=CustomerSnapshot(
                    first_name="John",
                    last_name="Doe",
                    email="john@example.com",
                ),
                shipping_address=AddressSnapshot(
                    first_name="John",
                    last_name="Doe",
                    address_line_1="123 Main St",
                    city="Luxembourg",
                    postal_code="L-1234",
                    country_iso="LU",
                ),
            )
        assert "items" in str(exc_info.value).lower()

    def test_customer_required(self):
        """Test customer is required."""
        with pytest.raises(ValidationError) as exc_info:
            OrderCreate(
                items=[OrderItemCreate(product_id=1, quantity=1)],
                shipping_address=AddressSnapshot(
                    first_name="John",
                    last_name="Doe",
                    address_line_1="123 Main St",
                    city="Luxembourg",
                    postal_code="L-1234",
                    country_iso="LU",
                ),
            )
        assert "customer" in str(exc_info.value).lower()

    def test_shipping_address_required(self):
        """Test shipping_address is required."""
        with pytest.raises(ValidationError) as exc_info:
            OrderCreate(
                items=[OrderItemCreate(product_id=1, quantity=1)],
                customer=CustomerSnapshot(
                    first_name="John",
                    last_name="Doe",
                    email="john@example.com",
                ),
            )
        assert "shipping_address" in str(exc_info.value).lower()

    def test_optional_billing_address(self):
        """Test billing_address is optional."""
        order = OrderCreate(
            items=[OrderItemCreate(product_id=1, quantity=1)],
            customer=CustomerSnapshot(
                first_name="John",
                last_name="Doe",
                email="john@example.com",
            ),
            shipping_address=AddressSnapshot(
                first_name="John",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_iso="LU",
            ),
            billing_address=AddressSnapshot(
                first_name="Jane",
                last_name="Doe",
                address_line_1="456 Other St",
                city="Esch",
                postal_code="L-4321",
                country_iso="LU",
            ),
        )
        assert order.billing_address is not None
        assert order.billing_address.first_name == "Jane"

    def test_optional_customer_notes(self):
        """Test optional customer_notes."""
        order = OrderCreate(
            items=[OrderItemCreate(product_id=1, quantity=1)],
            customer=CustomerSnapshot(
                first_name="John",
                last_name="Doe",
                email="john@example.com",
            ),
            shipping_address=AddressSnapshot(
                first_name="John",
                last_name="Doe",
                address_line_1="123 Main St",
                city="Luxembourg",
                postal_code="L-1234",
                country_iso="LU",
            ),
            customer_notes="Please leave at door",
        )
        assert order.customer_notes == "Please leave at door"


@pytest.mark.unit
@pytest.mark.schema
class TestOrderUpdateSchema:
    """Test OrderUpdate schema validation."""

    def test_status_update(self):
        """Test valid status update."""
        update = OrderUpdate(status="processing")
        assert update.status == "processing"

    def test_valid_status_values(self):
        """Test all valid status values."""
        valid_statuses = [
            "pending",
            "processing",
            "shipped",
            "delivered",
            "cancelled",
            "refunded",
        ]
        for status in valid_statuses:
            update = OrderUpdate(status=status)
            assert update.status == status

    def test_invalid_status(self):
        """Test invalid status raises ValidationError."""
        with pytest.raises(ValidationError) as exc_info:
            OrderUpdate(status="invalid_status")
        assert "status" in str(exc_info.value).lower()

    def test_tracking_number_update(self):
        """Test tracking number update."""
        update = OrderUpdate(tracking_number="TRACK123456")
        assert update.tracking_number == "TRACK123456"

    def test_internal_notes_update(self):
        """Test internal notes update."""
        update = OrderUpdate(internal_notes="Customer requested expedited shipping")
        assert update.internal_notes == "Customer requested expedited shipping"

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


@pytest.mark.unit
@pytest.mark.schema
class TestOrderResponseSchema:
    """Test OrderResponse schema."""

    def test_from_dict(self):
        """Test creating response from dict."""
        now = datetime.now(UTC)
        data = {
            "id": 1,
            "store_id": 1,
            "customer_id": 1,
            "order_number": "ORD-001",
            "channel": "direct",
            "status": "pending",
            "subtotal": 100.00,
            "tax_amount": 20.00,
            "shipping_amount": 10.00,
            "discount_amount": 5.00,
            "total_amount": 125.00,
            "currency": "EUR",
            # Customer snapshot
            "customer_first_name": "John",
            "customer_last_name": "Doe",
            "customer_email": "john@example.com",
            "customer_phone": None,
            "customer_locale": "en",
            # Ship address snapshot
            "ship_first_name": "John",
            "ship_last_name": "Doe",
            "ship_company": None,
            "ship_address_line_1": "123 Main St",
            "ship_address_line_2": None,
            "ship_city": "Luxembourg",
            "ship_postal_code": "L-1234",
            "ship_country_iso": "LU",
            # Bill address snapshot
            "bill_first_name": "John",
            "bill_last_name": "Doe",
            "bill_company": None,
            "bill_address_line_1": "123 Main St",
            "bill_address_line_2": None,
            "bill_city": "Luxembourg",
            "bill_postal_code": "L-1234",
            "bill_country_iso": "LU",
            # Tracking
            "shipping_method": "standard",
            "tracking_number": None,
            "tracking_provider": None,
            # Notes
            "customer_notes": None,
            "internal_notes": None,
            # Timestamps
            "order_date": now,
            "confirmed_at": None,
            "shipped_at": None,
            "delivered_at": None,
            "cancelled_at": None,
            "created_at": now,
            "updated_at": now,
        }
        response = OrderResponse(**data)
        assert response.id == 1
        assert response.order_number == "ORD-001"
        assert response.total_amount == 125.00
        assert response.channel == "direct"
        assert response.customer_full_name == "John Doe"

    def test_is_marketplace_order(self):
        """Test is_marketplace_order property."""
        now = datetime.now(UTC)
        # Direct order
        direct_order = OrderResponse(
            id=1, store_id=1, customer_id=1, order_number="ORD-001",
            channel="direct", status="pending",
            subtotal=100.0, tax_amount=0.0, shipping_amount=0.0, discount_amount=0.0,
            total_amount=100.0, currency="EUR",
            customer_first_name="John", customer_last_name="Doe",
            customer_email="john@example.com", customer_phone=None, customer_locale=None,
            ship_first_name="John", ship_last_name="Doe", ship_company=None,
            ship_address_line_1="123 Main", ship_address_line_2=None,
            ship_city="Luxembourg", ship_postal_code="L-1234", ship_country_iso="LU",
            bill_first_name="John", bill_last_name="Doe", bill_company=None,
            bill_address_line_1="123 Main", bill_address_line_2=None,
            bill_city="Luxembourg", bill_postal_code="L-1234", bill_country_iso="LU",
            shipping_method=None, tracking_number=None, tracking_provider=None,
            customer_notes=None, internal_notes=None,
            order_date=now, confirmed_at=None, shipped_at=None,
            delivered_at=None, cancelled_at=None, created_at=now, updated_at=now,
        )
        assert direct_order.is_marketplace_order is False

        # Marketplace order
        marketplace_order = OrderResponse(
            id=2, store_id=1, customer_id=1, order_number="LS-001",
            channel="letzshop", status="pending",
            subtotal=100.0, tax_amount=0.0, shipping_amount=0.0, discount_amount=0.0,
            total_amount=100.0, currency="EUR",
            customer_first_name="John", customer_last_name="Doe",
            customer_email="john@example.com", customer_phone=None, customer_locale=None,
            ship_first_name="John", ship_last_name="Doe", ship_company=None,
            ship_address_line_1="123 Main", ship_address_line_2=None,
            ship_city="Luxembourg", ship_postal_code="L-1234", ship_country_iso="LU",
            bill_first_name="John", bill_last_name="Doe", bill_company=None,
            bill_address_line_1="123 Main", bill_address_line_2=None,
            bill_city="Luxembourg", bill_postal_code="L-1234", bill_country_iso="LU",
            shipping_method=None, tracking_number=None, tracking_provider=None,
            customer_notes=None, internal_notes=None,
            order_date=now, confirmed_at=None, shipped_at=None,
            delivered_at=None, cancelled_at=None, created_at=now, updated_at=now,
        )
        assert marketplace_order.is_marketplace_order is True


@pytest.mark.unit
@pytest.mark.schema
class TestOrderItemResponseSchema:
    """Test OrderItemResponse schema."""

    def test_from_dict(self):
        """Test creating response from dict."""
        now = datetime.now(UTC)
        data = {
            "id": 1,
            "order_id": 1,
            "product_id": 1,
            "product_name": "Test Product",
            "product_sku": "SKU-001",
            "gtin": "4006381333931",
            "gtin_type": "EAN13",
            "quantity": 2,
            "unit_price": 50.00,
            "total_price": 100.00,
            "inventory_reserved": True,
            "inventory_fulfilled": False,
            "needs_product_match": False,
            "created_at": now,
            "updated_at": now,
        }
        response = OrderItemResponse(**data)
        assert response.id == 1
        assert response.quantity == 2
        assert response.total_price == 100.00
        assert response.gtin == "4006381333931"

    def test_has_unresolved_exception(self):
        """Test has_unresolved_exception property."""
        now = datetime.now(UTC)
        base_data = {
            "id": 1, "order_id": 1, "product_id": 1,
            "product_name": "Test", "product_sku": "SKU-001",
            "gtin": None, "gtin_type": None,
            "quantity": 1, "unit_price": 10.0, "total_price": 10.0,
            "inventory_reserved": False, "inventory_fulfilled": False,
            "created_at": now, "updated_at": now,
        }

        # No exception
        response = OrderItemResponse(**base_data, needs_product_match=False, exception=None)
        assert response.has_unresolved_exception is False

        # Pending exception
        from app.modules.orders.schemas import OrderItemExceptionBrief
        pending_exc = OrderItemExceptionBrief(
            id=1, original_gtin="123", original_product_name="Test",
            exception_type="product_not_found", status="pending",
            resolved_product_id=None,
        )
        response = OrderItemResponse(**base_data, needs_product_match=True, exception=pending_exc)
        assert response.has_unresolved_exception is True

        # Resolved exception
        resolved_exc = OrderItemExceptionBrief(
            id=1, original_gtin="123", original_product_name="Test",
            exception_type="product_not_found", status="resolved",
            resolved_product_id=5,
        )
        response = OrderItemResponse(**base_data, needs_product_match=False, exception=resolved_exc)
        assert response.has_unresolved_exception is False


@pytest.mark.unit
@pytest.mark.schema
class TestOrderListResponseSchema:
    """Test OrderListResponse schema."""

    def test_valid_list_response(self):
        """Test valid list response structure."""
        response = OrderListResponse(
            orders=[],
            total=0,
            skip=0,
            limit=10,
        )
        assert response.orders == []
        assert response.total == 0
        assert response.skip == 0
        assert response.limit == 10
