from typing import Any

from ..utils import convert_nanos_to_iso
from .types import OTLP_SPAN_KIND_MAP, OTLP_STATUS_CODE_MAP


def convert_otlp_to_json(
    wire_span: dict[str, Any],
    resource: dict[str, Any] | None = None,
    scope: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Convert an OTLP wire format span to SDK JSON format.

    Wire format spec: https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto

    Args:
        wire_span: A span dict from resourceSpans[].scopeSpans[].spans[].
        resource: Optional resource dict from resourceSpans[].resource.
        scope: Optional scope dict from resourceSpans[].scopeSpans[].scope.

    Returns:
        A span dict in SDK JSON format.
    """
    span_attrs = convert_attributes(wire_span.get("attributes", []))
    resource_attrs = {}
    if resource:
        resource_attrs = convert_attributes(resource.get("attributes", []))

    merged_attrs = {**resource_attrs, **span_attrs}
    start_time = convert_nanos_to_iso(wire_span.get("startTimeUnixNano"))
    end_time = convert_nanos_to_iso(wire_span.get("endTimeUnixNano"))

    converted: dict[str, Any] = {
        "name": wire_span.get("name"),
        "context": {
            "trace_id": wire_span.get("traceId"),
            "span_id": wire_span.get("spanId"),
            "trace_state": wire_span.get("traceState", ""),
        },
        "kind": convert_span_kind(wire_span.get("kind")),
        "parent_id": wire_span.get("parentSpanId"),
        "start_time": start_time,
        "end_time": end_time,
        "status": convert_status(wire_span.get("status", {})),
        "attributes": merged_attrs,
        "events": convert_events(wire_span.get("events", [])),
        "links": wire_span.get("links", []),
    }

    if resource:
        converted["resource"] = {"attributes": resource_attrs}

    if scope:
        converted["instrumentation_scope"] = {
            "name": scope.get("name"),
            "version": scope.get("version"),
        }

    return converted


def convert_attributes(wire_attrs: list[dict[str, Any]]) -> dict[str, Any]:
    """Convert OTLP attributes array to a flat dict."""
    result: dict[str, Any] = {}
    for attr in wire_attrs:
        key = attr.get("key")
        value_obj = attr.get("value", {})
        if key:
            result[key] = extract_value(value_obj)
    return result


def extract_value(value_obj: dict[str, Any]) -> Any:
    """Extract the value from an OTLP AnyValue wrapper."""
    match value_obj:
        case {"stringValue": val}:
            return val
        case {"intValue": val}:
            return int(val) if isinstance(val, str) else val
        case {"doubleValue": val}:
            return val
        case {"boolValue": val}:
            return val
        case {"arrayValue": arr}:
            values = arr.get("values", [])
            return [extract_value(v) for v in values]
        case {"kvlistValue": kv}:
            kv_list = kv.get("values", [])
            return {item.get("key"): extract_value(item.get("value", {})) for item in kv_list}
        case {"bytesValue": val}:
            return val
        case _:
            return None


def convert_span_kind(kind: int | None) -> str:
    """Convert OTLP SpanKind enum to string."""
    if kind is None:
        return "SpanKind.UNSPECIFIED"
    return OTLP_SPAN_KIND_MAP.get(kind, "SpanKind.UNSPECIFIED")


def convert_status(wire_status: dict[str, Any]) -> dict[str, Any]:
    """Convert OTLP status to SDK format."""
    code = wire_status.get("code", 0)
    return {
        "status_code": OTLP_STATUS_CODE_MAP.get(code, "UNSET"),
        "message": wire_status.get("message", ""),
    }


def convert_events(wire_events: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Convert OTLP events to SDK format."""
    return [
        {
            "name": event.get("name"),
            "timestamp": convert_nanos_to_iso(event.get("timeUnixNano")),
            "attributes": convert_attributes(event.get("attributes", [])),
        }
        for event in wire_events
    ]
