"""Span attribute names for Mingx OpenTelemetry integration."""

import json
from typing import Any, Optional


def _serialize(obj: Any) -> Optional[str]:
    if obj is None or isinstance(obj, str):
        return obj
    try:
        return json.dumps(obj, default=str)
    except (TypeError, ValueError):
        return str(obj)


class MingxOtelSpanAttributes:
    """Attribute key constants for Mingx span attributes (mingx.* namespace)."""

    # Trace-level attributes (set on root span or via update_current_trace)
    TRACE_NAME = "mingx.trace.name"
    TRACE_USER_ID = "mingx.trace.user_id"
    TRACE_SESSION_ID = "mingx.trace.session_id"
    TRACE_METADATA = "mingx.trace.metadata"
    TRACE_INPUT = "mingx.trace.input"
    TRACE_OUTPUT = "mingx.trace.output"
    TRACE_TAGS = "mingx.trace.tags"

    # Observation-level attributes
    OBSERVATION_TYPE = "mingx.observation.type"
    OBSERVATION_METADATA = "mingx.observation.metadata"
    OBSERVATION_LEVEL = "mingx.observation.level"
    OBSERVATION_STATUS_MESSAGE = "mingx.observation.status_message"
    OBSERVATION_INPUT = "mingx.observation.input"
    OBSERVATION_OUTPUT = "mingx.observation.output"

    # Generation-specific attributes
    OBSERVATION_MODEL = "mingx.observation.model"
    OBSERVATION_MODEL_PARAMETERS = "mingx.observation.model_parameters"
    OBSERVATION_USAGE_DETAILS = "mingx.observation.usage_details"
    OBSERVATION_COST_DETAILS = "mingx.observation.cost_details"
    OBSERVATION_COMPLETION_START_TIME = "mingx.observation.completion_start_time"

    # General
    ENVIRONMENT = "mingx.environment"
    VERSION = "mingx.version"


def create_trace_attributes(
    *,
    name: Optional[str] = None,
    user_id: Optional[str] = None,
    session_id: Optional[str] = None,
    version: Optional[str] = None,
    input: Optional[Any] = None,
    output: Optional[Any] = None,
    metadata: Optional[Any] = None,
    tags: Optional[list[str]] = None,
) -> dict[str, Any]:
    """Build trace-level attribute dict; omit None values."""
    attrs: dict[str, Any] = {
        MingxOtelSpanAttributes.TRACE_NAME: name,
        MingxOtelSpanAttributes.TRACE_USER_ID: user_id,
        MingxOtelSpanAttributes.TRACE_SESSION_ID: session_id,
        MingxOtelSpanAttributes.VERSION: version,
        MingxOtelSpanAttributes.TRACE_INPUT: _serialize(input),
        MingxOtelSpanAttributes.TRACE_OUTPUT: _serialize(output),
        MingxOtelSpanAttributes.TRACE_TAGS: tags,
    }
    if metadata is not None:
        if isinstance(metadata, dict):
            for k, v in metadata.items():
                key = f"{MingxOtelSpanAttributes.TRACE_METADATA}.{k}"
                attrs[key] = v if isinstance(v, (str, int, float, bool)) else _serialize(v)
        else:
            attrs[MingxOtelSpanAttributes.TRACE_METADATA] = _serialize(metadata)
    return {k: v for k, v in attrs.items() if v is not None}


def create_span_attributes(
    *,
    observation_type: str = "span",
    metadata: Optional[Any] = None,
    input: Optional[Any] = None,
    output: Optional[Any] = None,
    level: Optional[str] = None,
    status_message: Optional[str] = None,
    version: Optional[str] = None,
) -> dict[str, Any]:
    """Build observation-level attribute dict for span/event."""
    attrs: dict[str, Any] = {
        MingxOtelSpanAttributes.OBSERVATION_TYPE: observation_type,
        MingxOtelSpanAttributes.OBSERVATION_LEVEL: level,
        MingxOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE: status_message,
        MingxOtelSpanAttributes.VERSION: version,
        MingxOtelSpanAttributes.OBSERVATION_INPUT: _serialize(input),
        MingxOtelSpanAttributes.OBSERVATION_OUTPUT: _serialize(output),
    }
    if metadata is not None:
        if isinstance(metadata, dict):
            for k, v in metadata.items():
                key = f"{MingxOtelSpanAttributes.OBSERVATION_METADATA}.{k}"
                attrs[key] = v if isinstance(v, (str, int, float, bool)) else _serialize(v)
        else:
            attrs[MingxOtelSpanAttributes.OBSERVATION_METADATA] = _serialize(metadata)
    return {k: v for k, v in attrs.items() if v is not None}


def create_generation_attributes(
    *,
    metadata: Optional[Any] = None,
    input: Optional[Any] = None,
    output: Optional[Any] = None,
    level: Optional[str] = None,
    status_message: Optional[str] = None,
    version: Optional[str] = None,
    model: Optional[str] = None,
    model_parameters: Optional[dict[str, Any]] = None,
    usage_details: Optional[dict[str, int]] = None,
    cost_details: Optional[dict[str, float]] = None,
    completion_start_time: Optional[str] = None,
) -> dict[str, Any]:
    """Build observation-level attribute dict for generation."""
    base = create_span_attributes(
        observation_type="generation",
        metadata=metadata,
        input=input,
        output=output,
        level=level,
        status_message=status_message,
        version=version,
    )
    extra = {
        MingxOtelSpanAttributes.OBSERVATION_MODEL: model,
        MingxOtelSpanAttributes.OBSERVATION_MODEL_PARAMETERS: _serialize(model_parameters),
        MingxOtelSpanAttributes.OBSERVATION_USAGE_DETAILS: _serialize(usage_details),
        MingxOtelSpanAttributes.OBSERVATION_COST_DETAILS: _serialize(cost_details),
        MingxOtelSpanAttributes.OBSERVATION_COMPLETION_START_TIME: completion_start_time,
    }
    base.update({k: v for k, v in extra.items() if v is not None})
    return base
