# tests/unit/models/database/test_order_item_exception.py
"""Unit tests for OrderItemException database model."""

from datetime import UTC

import pytest
from sqlalchemy.exc import IntegrityError

from app.modules.orders.models import OrderItemException


@pytest.mark.unit
@pytest.mark.database
class TestOrderItemExceptionModel:
    """Test OrderItemException model."""

    def test_exception_creation(self, db, test_order_item, test_store):
        """Test OrderItemException model creation."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            original_product_name="Test Missing Product",
            original_sku="MISSING-SKU-001",
            exception_type="product_not_found",
            status="pending",
        )

        db.add(exception)
        db.commit()
        db.refresh(exception)

        assert exception.id is not None
        assert exception.order_item_id == test_order_item.id
        assert exception.store_id == test_store.id
        assert exception.original_gtin == "4006381333931"
        assert exception.original_product_name == "Test Missing Product"
        assert exception.original_sku == "MISSING-SKU-001"
        assert exception.exception_type == "product_not_found"
        assert exception.status == "pending"
        assert exception.created_at is not None

    def test_exception_unique_order_item(self, db, test_order_item, test_store):
        """Test that only one exception can exist per order item."""
        exception1 = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
        )
        db.add(exception1)
        db.commit()

        # Second exception for the same order item should fail
        with pytest.raises(IntegrityError):
            exception2 = OrderItemException(
                order_item_id=test_order_item.id,
                store_id=test_store.id,
                original_gtin="4006381333932",
                exception_type="product_not_found",
            )
            db.add(exception2)
            db.commit()

    def test_exception_types(self, db, test_order_item, test_store):
        """Test different exception types."""
        exception_types = ["product_not_found", "gtin_mismatch", "duplicate_gtin"]

        # Create new order items for each test to avoid unique constraint
        from app.modules.orders.models import OrderItem

        for i, exc_type in enumerate(exception_types):
            order_item = OrderItem(
                order_id=test_order_item.order_id,
                product_id=test_order_item.product_id,
                product_name=f"Product {i}",
                product_sku=f"SKU-{i}",
                quantity=1,
                unit_price=10.00,
                total_price=10.00,
            )
            db.add(order_item)
            db.commit()
            db.refresh(order_item)

            exception = OrderItemException(
                order_item_id=order_item.id,
                store_id=test_store.id,
                original_gtin=f"400638133393{i}",
                exception_type=exc_type,
            )
            db.add(exception)
            db.commit()
            db.refresh(exception)

            assert exception.exception_type == exc_type

    def test_exception_status_values(self, db, test_order_item, test_store):
        """Test different status values."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
            status="pending",
        )
        db.add(exception)
        db.commit()

        # Test pending status
        assert exception.status == "pending"
        assert exception.is_pending is True
        assert exception.is_resolved is False
        assert exception.is_ignored is False
        assert exception.blocks_confirmation is True

        # Test resolved status
        exception.status = "resolved"
        db.commit()
        db.refresh(exception)

        assert exception.is_pending is False
        assert exception.is_resolved is True
        assert exception.is_ignored is False
        assert exception.blocks_confirmation is False

        # Test ignored status
        exception.status = "ignored"
        db.commit()
        db.refresh(exception)

        assert exception.is_pending is False
        assert exception.is_resolved is False
        assert exception.is_ignored is True
        assert exception.blocks_confirmation is True  # Ignored still blocks

    def test_exception_nullable_fields(self, db, test_order_item, test_store):
        """Test that GTIN and other fields can be null."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin=None,  # Can be null for vouchers etc.
            original_product_name="Gift Voucher",
            original_sku=None,
            exception_type="product_not_found",
        )
        db.add(exception)
        db.commit()
        db.refresh(exception)

        assert exception.id is not None
        assert exception.original_gtin is None
        assert exception.original_sku is None
        assert exception.original_product_name == "Gift Voucher"

    def test_exception_resolution(self, db, test_order_item, test_store, test_product, test_user):
        """Test resolving an exception with a product."""
        from datetime import datetime

        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
            status="pending",
        )
        db.add(exception)
        db.commit()

        # Resolve the exception
        now = datetime.now(UTC)
        exception.status = "resolved"
        exception.resolved_product_id = test_product.id
        exception.resolved_at = now
        exception.resolved_by = test_user.id
        exception.resolution_notes = "Matched to existing product"
        db.commit()
        db.refresh(exception)

        assert exception.status == "resolved"
        assert exception.resolved_product_id == test_product.id
        assert exception.resolved_at is not None
        assert exception.resolved_by == test_user.id
        assert exception.resolution_notes == "Matched to existing product"

    def test_exception_relationships(self, db, test_order_item, test_store):
        """Test OrderItemException relationships."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
        )
        db.add(exception)
        db.commit()
        db.refresh(exception)

        assert exception.order_item is not None
        assert exception.order_item.id == test_order_item.id
        assert exception.store is not None
        assert exception.store.id == test_store.id

    def test_exception_repr(self, db, test_order_item, test_store):
        """Test OrderItemException __repr__ method."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
            status="pending",
        )
        db.add(exception)
        db.commit()
        db.refresh(exception)

        repr_str = repr(exception)
        assert "OrderItemException" in repr_str
        assert str(exception.id) in repr_str
        assert "4006381333931" in repr_str
        assert "pending" in repr_str

    def test_exception_cascade_delete(self, db, test_order_item, test_store):
        """Test that exception is deleted when order item is deleted."""
        exception = OrderItemException(
            order_item_id=test_order_item.id,
            store_id=test_store.id,
            original_gtin="4006381333931",
            exception_type="product_not_found",
        )
        db.add(exception)
        db.commit()
        exception_id = exception.id

        # Delete the order item
        db.delete(test_order_item)
        db.commit()

        # Exception should be cascade deleted
        deleted_exception = (
            db.query(OrderItemException)
            .filter(OrderItemException.id == exception_id)
            .first()
        )
        assert deleted_exception is None
