"""Tests for error propagation through the order system.

These tests verify that errors originating in the data layer
propagate correctly through service and handler with proper
error types, messages, and HTTP status codes.

DO NOT modify these tests.
"""

import pytest

from src.models import AppError, ValidationError, NotFoundError


@pytest.fixture(autouse=True)
def reset_store():
    """Reset order store between tests."""
    from src import repository
    repository._orders.clear()
    repository._next_id = 1
    yield
    repository._orders.clear()


# ── Validation errors should propagate as 400 Bad Request ──────────────


class TestValidationErrorPropagation:

    def test_missing_customer_returns_400(self):
        from src.handler import handle_create_order
        result = handle_create_order({"quantity": 1, "price": 10.0})
        assert result["status"] == 400
        assert "customer" in result["error"].lower()

    def test_invalid_quantity_returns_400(self):
        from src.handler import handle_create_order
        result = handle_create_order({
            "customer": "Alice", "quantity": -1, "price": 10.0,
        })
        assert result["status"] == 400
        assert "quantity" in result["error"].lower()

    def test_invalid_price_returns_400(self):
        from src.handler import handle_create_order
        result = handle_create_order({
            "customer": "Alice", "quantity": 1, "price": 0,
        })
        assert result["status"] == 400
        assert "price" in result["error"].lower()

    def test_validation_error_includes_code(self):
        from src.handler import handle_create_order
        result = handle_create_order({"quantity": 1, "price": 10.0})
        assert result.get("code") is not None


# ── Not-found errors should propagate as 404 Not Found ─────────────────


class TestNotFoundErrorPropagation:

    def test_missing_order_returns_404(self):
        from src.handler import handle_get_order
        result = handle_get_order(999)
        assert result["status"] == 404
        assert "not found" in result["error"].lower()

    def test_not_found_includes_code(self):
        from src.handler import handle_get_order
        result = handle_get_order(999)
        assert result.get("code") == "ORDER_NOT_FOUND"


# ── Error chain preserved through layers ────────────────────────────────


class TestErrorChainPreservation:

    def test_service_preserves_validation_error_type(self):
        from src.service import create_order
        with pytest.raises(ValidationError):
            create_order({"quantity": -1, "price": 10.0})

    def test_service_preserves_not_found_type(self):
        from src.service import get_order
        with pytest.raises(NotFoundError):
            get_order(999)

    def test_service_error_carries_code(self):
        from src.service import create_order
        with pytest.raises(AppError) as exc_info:
            create_order({"quantity": -1, "price": 10.0})
        assert exc_info.value.code is not None


# ── Success path still works ────────────────────────────────────────────


class TestSuccessPath:

    def test_create_order_success(self):
        from src.handler import handle_create_order
        result = handle_create_order({
            "customer": "Alice", "quantity": 2, "price": 25.0,
        })
        assert result["status"] == 201
        assert result["data"]["customer"] == "Alice"
        assert result["data"]["total"] == 50.0

    def test_get_order_success(self):
        from src.handler import handle_create_order, handle_get_order
        create_result = handle_create_order({
            "customer": "Bob", "quantity": 1, "price": 10.0,
        })
        order_id = create_result["data"]["id"]
        result = handle_get_order(order_id)
        assert result["status"] == 200
        assert result["data"]["customer"] == "Bob"

    def test_quantity_cap_applied(self):
        from src.handler import handle_create_order
        result = handle_create_order({
            "customer": "Bulk", "quantity": 5000, "price": 1.0,
        })
        assert result["status"] == 201
        assert result["data"]["quantity"] == 1000


# ── Unexpected errors remain 500 ───────────────────────────────────────


class TestUnexpectedErrors:

    def test_unexpected_error_returns_500(self):
        from src import service
        from src.handler import handle_create_order

        original = service.create_order

        def explode(data):
            raise RuntimeError("database connection lost")

        service.create_order = explode
        try:
            result = handle_create_order({
                "customer": "Alice", "quantity": 1, "price": 10.0,
            })
            assert result["status"] == 500
        finally:
            service.create_order = original
