from __future__ import annotations

from unittest.mock import Mock, patch

import pytest
from opentelemetry.trace import SpanKind

from verse_sdk.contexts.utils import create_span_in_existing_trace


class TestCreateSpanInExistingTrace:
    def test_create_span_with_valid_inputs(self):
        trace_id_hex = "1234567890abcdef1234567890abcdef"
        parent_span_id_hex = "1234567890abcdef"

        with patch("verse_sdk.contexts.utils.trace") as mock_trace:
            mock_tracer = Mock()
            mock_span = Mock()
            mock_trace.get_tracer.return_value = mock_tracer
            mock_tracer.start_span.return_value = mock_span

            result = create_span_in_existing_trace(trace_id_hex, parent_span_id_hex)

            assert result == mock_span
            mock_trace.get_tracer.assert_called_once()
            mock_tracer.start_span.assert_called_once_with(
                "llm_generation",
                context=mock_trace.set_span_in_context.return_value,
                kind=SpanKind.INTERNAL,
            )

    def test_create_span_with_hex_prefixes(self):
        trace_id_hex = "0x1234567890abcdef1234567890abcdef"
        parent_span_id_hex = "0X1234567890abcdef"

        with patch("verse_sdk.contexts.utils.trace") as mock_trace:
            mock_tracer = Mock()
            mock_span = Mock()
            mock_trace.get_tracer.return_value = mock_tracer
            mock_tracer.start_span.return_value = mock_span

            result = create_span_in_existing_trace(trace_id_hex, parent_span_id_hex)
            assert result == mock_span
            mock_tracer.start_span.assert_called_once()

    def test_create_span_with_non_string_inputs(self):
        trace_id_hex = 0x1234567890ABCDEF1234567890ABCDEF
        parent_span_id_hex = 0x1234567890ABCDEF

        with patch("verse_sdk.contexts.utils.trace") as mock_trace:
            mock_tracer = Mock()
            mock_span = Mock()
            mock_trace.get_tracer.return_value = mock_tracer
            mock_tracer.start_span.return_value = mock_span

            result = create_span_in_existing_trace(trace_id_hex, parent_span_id_hex)
            assert result == mock_span
            mock_tracer.start_span.assert_called_once()

    def test_create_span_with_invalid_hex_raises_error(self):
        trace_id_hex = "invalid_hex_string"
        parent_span_id_hex = "1234567890abcdef"

        with pytest.raises(
            ValueError, match="invalid literal for int\\(\\) with base 16"
        ):
            create_span_in_existing_trace(trace_id_hex, parent_span_id_hex)

    def test_create_span_with_empty_strings_raises_error(self):
        trace_id_hex = ""
        parent_span_id_hex = ""

        with pytest.raises(
            ValueError, match="invalid literal for int\\(\\) with base 16"
        ):
            create_span_in_existing_trace(trace_id_hex, parent_span_id_hex)

    def test_create_span_with_none_inputs_raises_error(self):
        with pytest.raises(
            ValueError, match="invalid literal for int\\(\\) with base 16: 'None'"
        ):
            create_span_in_existing_trace(None, None)
