"""Payload normalization for otelctx ingestion."""

from __future__ import annotations

from otelctx.db.repository import LogRecord, MetricRecord, SpanRecord, TraceRecord
from otelctx.otlp_http import (
    normalize_otlp_http_json,
    normalize_otlp_http_logs_json,
    normalize_otlp_http_metrics_json,
)


def normalize_payload(payload: dict[str, object]) -> list[TraceRecord]:
    return normalize_trace_payload(payload)


def normalize_trace_payload(payload: dict[str, object]) -> list[TraceRecord]:
    """Normalize JSON payloads into trace records.

    Supported shapes:
    - {"traces": [{"trace_id": ..., "spans": [...]}]}
    - {"spans": [{"trace_id": ..., ...}]}
    - OTLP HTTP JSON: {"resourceSpans": [...]}.
    """
    if isinstance(payload.get("resourceSpans"), list):
        return normalize_otlp_http_json(payload)

    if isinstance(payload.get("traces"), list):
        return [_trace_from_mapping(item) for item in payload["traces"] if isinstance(item, dict)]

    if isinstance(payload.get("spans"), list):
        grouped: dict[str, list[SpanRecord]] = {}
        trace_meta: dict[str, dict[str, str]] = {}
        for item in payload["spans"]:
            if not isinstance(item, dict):
                continue
            trace_id = str(item.get("trace_id") or "").strip()
            if not trace_id:
                continue
            grouped.setdefault(trace_id, []).append(_span_from_mapping(item))
            trace_meta.setdefault(
                trace_id,
                {
                    "root_service_name": str(item.get("service_name") or "unknown"),
                    "root_span_name": str(item.get("name") or ""),
                    "status_code": str(item.get("status_code") or "UNSET").upper(),
                },
            )
        return [
            TraceRecord(trace_id=trace_id, spans=spans, **trace_meta[trace_id])
            for trace_id, spans in grouped.items()
        ]

    return []


def normalize_log_payload(payload: dict[str, object]) -> list[LogRecord]:
    if isinstance(payload.get("resourceLogs"), list):
        return normalize_otlp_http_logs_json(payload)

    if isinstance(payload.get("logs"), list):
        return [_log_from_mapping(item) for item in payload["logs"] if isinstance(item, dict)]

    return []


def normalize_metric_payload(payload: dict[str, object]) -> list[MetricRecord]:
    if isinstance(payload.get("resourceMetrics"), list):
        return normalize_otlp_http_metrics_json(payload)

    if isinstance(payload.get("metrics"), list):
        return [_metric_from_mapping(item) for item in payload["metrics"] if isinstance(item, dict)]

    return []


def _trace_from_mapping(payload: dict[str, object]) -> TraceRecord:
    spans_payload = payload.get("spans") if isinstance(payload.get("spans"), list) else []
    spans = [_span_from_mapping(item) for item in spans_payload if isinstance(item, dict)]
    return TraceRecord(
        trace_id=str(payload.get("trace_id") or "").strip(),
        root_service_name=str(payload.get("root_service_name") or payload.get("service_name") or "unknown"),
        root_span_name=str(payload.get("root_span_name") or payload.get("name") or ""),
        status_code=str(payload.get("status_code") or "UNSET").upper(),
        spans=spans,
    )


def _span_from_mapping(payload: dict[str, object]) -> SpanRecord:
    attributes = payload.get("attributes")
    if not isinstance(attributes, dict):
        attributes = {}
    return SpanRecord(
        span_id=str(payload.get("span_id") or "").strip(),
        parent_span_id=str(payload.get("parent_span_id") or "").strip(),
        name=str(payload.get("name") or "").strip(),
        service_name=str(payload.get("service_name") or "unknown").strip() or "unknown",
        kind=str(payload.get("kind") or "INTERNAL").upper(),
        status_code=str(payload.get("status_code") or "UNSET").upper(),
        start_time=str(payload.get("start_time") or ""),
        end_time=str(payload.get("end_time") or ""),
        attributes=attributes,
    )


def _log_from_mapping(payload: dict[str, object]) -> LogRecord:
    attributes = payload.get("attributes")
    if not isinstance(attributes, dict):
        attributes = {}
    return LogRecord(
        timestamp=str(payload.get("timestamp") or ""),
        observed_time=str(payload.get("observed_time") or ""),
        service_name=str(payload.get("service_name") or "unknown").strip() or "unknown",
        severity_text=str(payload.get("severity_text") or "INFO").upper(),
        body=str(payload.get("body") or ""),
        trace_id=str(payload.get("trace_id") or "").strip(),
        span_id=str(payload.get("span_id") or "").strip(),
        attributes=attributes,
    )


def _metric_from_mapping(payload: dict[str, object]) -> MetricRecord:
    attributes = payload.get("attributes")
    if not isinstance(attributes, dict):
        attributes = {}
    try:
        value = float(payload.get("value") or 0.0)
    except (TypeError, ValueError):
        value = 0.0
    return MetricRecord(
        service_name=str(payload.get("service_name") or "unknown").strip() or "unknown",
        name=str(payload.get("name") or "").strip(),
        metric_type=str(payload.get("type") or "gauge").lower(),
        unit=str(payload.get("unit") or "").strip(),
        temporality=str(payload.get("temporality") or "").upper(),
        start_time=str(payload.get("start_time") or ""),
        time=str(payload.get("time") or ""),
        value=value,
        attributes=attributes,
    )
