# Copyright (c) Microsoft. All rights reserved.

import logging
from typing import Any, Dict, Optional

from mantisdk.semconv import AGL_MESSAGE, LightningSpanAttributes
from mantisdk.tracer.base import get_active_tracer
from mantisdk.tracer.dummy import DummyTracer
from mantisdk.types import Attributes, SpanLike
from mantisdk.utils.otel import flatten_attributes, sanitize_attributes

logger = logging.getLogger(__name__)


def emit_message(message: str, attributes: Optional[Dict[str, Any]] = None, propagate: bool = True) -> None:
    """Emit a textual message as an OpenTelemetry span.

    Commonly used for sending debugging and logging messages.

    Args:
        message: Human readable message to attach as a span attribute.
        attributes: Additional attributes to attach to the message span.
        propagate: Whether to propagate the span to exporters automatically.

    !!! note
        OpenTelemetry distinguishes between logs and spans. Emitting the message as a
        span keeps all Mantisdk telemetry in a single data store for analysis.
    """
    if not isinstance(message, str):  # type: ignore
        raise TypeError(f"Message must be a string or list of strings, got: {type(message)}.")

    if propagate:
        tracer = get_active_tracer()
        if tracer is None:
            raise RuntimeError("No active tracer found. Cannot emit message span.")
    else:
        tracer = DummyTracer()
    span_attributes: Attributes = {LightningSpanAttributes.MESSAGE_BODY.value: message}
    if attributes:
        flattened = flatten_attributes(attributes, expand_leaf_lists=False)
        span_attributes.update(sanitize_attributes(flattened))
    logger.debug("Emitting message span with message: %s", message)
    tracer.create_span(
        AGL_MESSAGE,
        attributes=span_attributes,
    )


def get_message_value(span: SpanLike) -> Optional[str]:
    """Extract the message string from a message span.

    Args:
        span: Span-like object to extract the message from.
    """
    span_attributes = span.attributes or {}
    if LightningSpanAttributes.MESSAGE_BODY.value not in span_attributes:
        return None
    message = span_attributes[LightningSpanAttributes.MESSAGE_BODY.value]
    if isinstance(message, str):
        return message
    raise TypeError(f"Message must be a string, got: {type(message)}.")
