from __future__ import annotations

"""Flow execution context for flow-invoked actor methods."""

from contextlib import contextmanager
from dataclasses import dataclass
from typing import Any

from flownet.core.request import FlowRequest
from flownet.runtime.contexts.actor import ActorContext, _build_actor_context
from flownet.utils.context_vars import ContextSlot


@dataclass(frozen=True)
class FlowContext:
    request: FlowRequest
    actor: ActorContext | None = None

    @property
    def request_id(self) -> str:
        return self.request.request_id

    @property
    def origin_address(self) -> str:
        return self.request.origin_address

    def _require_actor(self) -> ActorContext:
        if self.actor is None:
            raise RuntimeError("Flow actor context not available.")
        return self.actor

    def local_address(self) -> str:
        return self._require_actor().local_address()

    def call_async(self, rmf, *args, **kwargs):
        return self._require_actor().call_async(rmf, *args, **kwargs)

    def call(self, rmf, *args, **kwargs):
        return self._require_actor().call(rmf, *args, **kwargs)

    def submit(self, coro: Any, *, loop: str = "user"):
        return self._require_actor().submit(coro, loop=loop)

    def emit_service_event(
        self,
        service_name: str,
        event_type: str,
        payload: Any = None,
        tags: dict[str, str] | None = None,
    ) -> None:
        self._require_actor().emit_service_event(service_name, event_type, payload, tags)

    def emit_topic_event(
        self,
        topic_id: str,
        event_type: str,
        payload: Any = None,
        tags: dict[str, str] | None = None,
    ) -> None:
        self._require_actor().emit_topic_event(topic_id, event_type, payload, tags)


_flow_ctx = ContextSlot[FlowContext]("flownet_flow_context", default=None)


def flow_context() -> FlowContext:
    ctx = _flow_ctx.get()
    if ctx is None:
        raise RuntimeError("Flow context not available.")
    return ctx


def current_flow_request() -> FlowRequest | None:
    ctx = _flow_ctx.get()
    if ctx is None:
        return None
    return ctx.request


@contextmanager
def flow_context_scope(
    request: FlowRequest,
    actor_ctx: ActorContext | None = None,
):
    if actor_ctx is None:
        actor_ctx = _build_actor_context()
    ctx = FlowContext(request=request, actor=actor_ctx)
    with _flow_ctx.use(ctx):
        yield


# Compatibility alias: historically this was a "request context" accessor.
current_request = current_flow_request


__all__ = [
    "FlowContext",
    "flow_context",
    "flow_context_scope",
    "current_flow_request",
]
