"""Bridge between PathwayVM events and the observability system."""

import logging
from dataclasses import dataclass
from datetime import datetime

from core.observability.events import (
    EventEmitter,
    NodeCompletedEvent,
    NodeFailedEvent,
    NodeStartedEvent,
    PathwayCompletedEvent,
    PathwayStartedEvent,
)
from core.pathways.models.execution import PathwayEvent

logger = logging.getLogger(__name__)


@dataclass
class _ExecutionIndex:
    pathway_id: str | None = None
    thread_id: str | None = None
    turn_id: str | None = None
    started_at: datetime | None = None


class PathwayVMObservabilityBridge:
    """Listener that converts PathwayVM events into observability events."""

    def __init__(self, *, events: EventEmitter):
        self._events = events
        self._by_execution_id: dict[str, _ExecutionIndex] = {}

    def __call__(self, ev: PathwayEvent) -> None:
        try:
            event_type = getattr(ev, "event_type", None)
            data = getattr(ev, "data", {}) or {}
            ts = getattr(ev, "timestamp", None)
            node_id = getattr(ev, "node_id", None)
            pathway_id = getattr(ev, "pathway_id", None)
            error = getattr(ev, "error", None)

            execution_id = data.get("execution_id")
            if not isinstance(execution_id, str) or not execution_id:
                return

            idx = self._by_execution_id.get(execution_id)
            if idx is None:
                idx = _ExecutionIndex()
                self._by_execution_id[execution_id] = idx

            if (
                isinstance(ts, datetime)
                and idx.started_at is None
                and event_type == "execution_started"
            ):
                idx.started_at = ts

            if event_type == "execution_started":
                idx.pathway_id = pathway_id or idx.pathway_id
                self._events.emit(
                    PathwayStartedEvent(
                        thread_id=idx.thread_id,
                        turn_id=idx.turn_id,
                        execution_id=execution_id,
                        pathway_id=idx.pathway_id or "",
                    ),
                )
                return

            effective_pathway_id = idx.pathway_id or pathway_id or ""

            builder = _EVENT_BUILDERS.get(event_type)
            if builder:
                event = builder(
                    execution_id=execution_id,
                    node_id=node_id,
                    pathway_id=effective_pathway_id,
                    data=data,
                    error=error,
                    ts=ts,
                    idx=idx,
                )
                self._events.emit(event)
                if event_type == "execution_completed":
                    self._by_execution_id.pop(execution_id, None)

        except Exception as e:
            logger.debug("PathwayVMObservabilityBridge dropped event: %s", e)


# ---------------------------------------------------------------------------
# Event builders: event_type → constructor function
# ---------------------------------------------------------------------------


def _build_node_started(*, execution_id, node_id, pathway_id, **_kw):
    return NodeStartedEvent(
        execution_id=execution_id,
        node_id=node_id or "",
        pathway_id=pathway_id,
    )


def _build_node_completed(*, execution_id, node_id, pathway_id, data, **_kw):
    return NodeCompletedEvent(
        execution_id=execution_id,
        node_id=node_id or "",
        pathway_id=pathway_id,
        duration_ms=data.get("duration_ms"),
    )


def _build_node_failed(*, execution_id, node_id, pathway_id, error, **_kw):
    return NodeFailedEvent(
        execution_id=execution_id,
        node_id=node_id or "",
        pathway_id=pathway_id,
        error=str(error) if error else "",
    )


def _build_execution_completed(*, execution_id, data, ts, idx, **_kw):
    duration_ms = None
    if isinstance(ts, datetime) and isinstance(idx.started_at, datetime):
        duration_ms = (ts - idx.started_at).total_seconds() * 1000
    return PathwayCompletedEvent(
        execution_id=execution_id,
        pathway_id=idx.pathway_id or "",
        duration_ms=duration_ms,
        success=str(data.get("status", "")) == "completed",
    )


_EVENT_BUILDERS = {
    "node_started": _build_node_started,
    "node_completed": _build_node_completed,
    "node_failed": _build_node_failed,
    "execution_completed": _build_execution_completed,
}


def attach_pathway_vm_observability(*, vm: object, events: EventEmitter) -> None:
    """Attach a single bridge listener to a PathwayVM-like object."""
    add_listener = getattr(vm, "add_listener", None)
    if not callable(add_listener):
        raise TypeError("vm does not support add_listener(listener)")
    add_listener(PathwayVMObservabilityBridge(events=events))


__all__ = [
    "PathwayVMObservabilityBridge",
    "attach_pathway_vm_observability",
]
