# #region Test.TraceContextMiddleware [C:3] [TYPE Module] [SEMANTICS test,middleware,trace,asgi]
# @BRIEF Tests for core/middleware/trace.py — TraceContextMiddleware ASGI middleware.
# @RELATION BINDS_TO -> [TraceContextMiddlewareModule]

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))

from unittest.mock import AsyncMock, MagicMock, patch
import pytest


# #region test_trace_middleware [C:2] [TYPE Function]
# @BRIEF Test TraceContextMiddleware for various ASGI scenarios.
class TestTraceContextMiddleware:
    @pytest.fixture
    def mock_app(self):
        return AsyncMock()

    @pytest.fixture
    def middleware(self, mock_app):
        from src.core.middleware.trace import TraceContextMiddleware
        return TraceContextMiddleware(mock_app)

    @pytest.mark.asyncio
    async def test_non_http_passthrough(self, middleware, mock_app):
        """Non-HTTP scopes pass through without seeding trace."""
        scope = {"type": "websocket"}
        receive = MagicMock()
        send = MagicMock()
        await middleware(scope, receive, send)
        mock_app.assert_called_once_with(scope, receive, send)

    @pytest.mark.asyncio
    async def test_http_seeds_new_trace_id(self, middleware, mock_app):
        """HTTP scope without X-Trace-ID header seeds a new trace."""
        scope = {"type": "http", "headers": []}
        with patch("src.core.middleware.trace.seed_trace_id") as mock_seed:
            await middleware(scope, MagicMock(), MagicMock())
            mock_seed.assert_called_once()
            mock_app.assert_called_once()

    @pytest.mark.asyncio
    async def test_http_accepts_valid_v4_trace_id(self, middleware, mock_app):
        """Valid UUID4 in X-Trace-ID header is used."""
        valid_uuid = "550e8400-e29b-41d4-a716-446655440000"
        scope = {
            "type": "http",
            "headers": [(b"x-trace-id", valid_uuid.encode())],
        }
        with patch("src.core.middleware.trace.set_trace_id") as mock_set:
            await middleware(scope, MagicMock(), MagicMock())
            mock_set.assert_called_once_with(valid_uuid)

    @pytest.mark.asyncio
    async def test_rejects_non_v4_trace_id(self, middleware, mock_app):
        """Non-v4 UUID falls back to seed_trace_id."""
        non_v4_uuid = "550e8400-e29b-11d4-a716-446655440000"  # version 1
        scope = {
            "type": "http",
            "headers": [(b"x-trace-id", non_v4_uuid.encode())],
        }
        with patch("src.core.middleware.trace.set_trace_id") as mock_set, \
             patch("src.core.middleware.trace.seed_trace_id") as mock_seed:
            await middleware(scope, MagicMock(), MagicMock())
            mock_set.assert_not_called()
            mock_seed.assert_called_once()

    @pytest.mark.asyncio
    async def test_rejects_invalid_uuid(self, middleware, mock_app):
        """Invalid UUID string falls back to seed_trace_id."""
        scope = {
            "type": "http",
            "headers": [(b"x-trace-id", b"not-a-uuid")],
        }
        with patch("src.core.middleware.trace.set_trace_id") as mock_set, \
             patch("src.core.middleware.trace.seed_trace_id") as mock_seed:
            await middleware(scope, MagicMock(), MagicMock())
            mock_set.assert_not_called()
            mock_seed.assert_called_once()

    @pytest.mark.asyncio
    async def test_handles_non_utf8_header(self, middleware, mock_app):
        """Non-UTF8 bytes in header do not crash."""
        scope = {
            "type": "http",
            "headers": [(b"x-trace-id", b"\xff\xfe\x00\x01")],
        }
        with patch("src.core.middleware.trace.seed_trace_id") as mock_seed:
            await middleware(scope, MagicMock(), MagicMock())
            mock_seed.assert_called_once()

    @pytest.mark.asyncio
    async def test_forwards_to_app(self, middleware, mock_app):
        """After seeding, the ASGI app is called."""
        scope = {"type": "http", "headers": []}
        receive = MagicMock()
        send = MagicMock()
        await middleware(scope, receive, send)
        mock_app.assert_called_once_with(scope, receive, send)
# #endregion test_trace_middleware

# #endregion Test.TraceContextMiddleware
