from __future__ import annotations

import asyncio
import os
import uuid
from typing import Any, Awaitable, Callable

from flownet.api.actor_method_ref import ActorMethodRef
from flownet.core.request import ActorCall, FlownetPacket
from flownet.core.types import PayloadData
from flownet.runtime.transport.call_manager import RemoteCallManager
from flownet.runtime.actors.execution import ActorCallExecutor


def _rpc_debug_logs_enabled() -> bool:
    value = (os.environ.get("FLOWNET_RPC_DEBUG_LOGS") or "").strip().lower()
    return value in {"1", "true", "yes", "on"}


class ActorDispatcher:
    def __init__(
        self,
        call_manager: RemoteCallManager,
        local_address_fn: Callable[[], str],
        resolve_actor_fn: Callable[[str], Any],
        user_loop: asyncio.AbstractEventLoop | None,
        container_handler: Any,
        call_remote_via_v1: Callable[[ActorMethodRef, tuple[Any, ...], dict[str, Any]], Awaitable[Any]] | None = None,
        v1_fail_open_legacy: bool = True,
    ):
        self._call_manager = call_manager
        self._local_address_fn = local_address_fn
        self._resolve_actor_fn = resolve_actor_fn
        self._user_loop = user_loop
        self._container_handler = container_handler
        self._call_remote_via_v1 = call_remote_via_v1
        self._v1_fail_open_legacy = bool(v1_fail_open_legacy)
        self._executor = ActorCallExecutor(
            resolve_actor_fn=resolve_actor_fn,
            user_loop=user_loop,
            container_handler=container_handler,
        )

    async def handle_actor_call(self, rmf: ActorMethodRef, *args, **kwargs) -> Any:
        if _rpc_debug_logs_enabled():
            print("[FlownetRuntime] Handling actor call:", rmf, args, kwargs)
        local_address = self._local_address_fn()
        if rmf.address != local_address:
            if _rpc_debug_logs_enabled():
                print("[FlownetRuntime] Routing actor call to remote address:", rmf.address)
            if self._call_remote_via_v1 is not None:
                try:
                    return await self._call_remote_via_v1(
                        rmf,
                        tuple(args),
                        dict(kwargs),
                    )
                except Exception:
                    if not self._v1_fail_open_legacy:
                        raise
            return await self._call_manager.send_packet(
                rmf.address,
                FlownetPacket(
                    type="actorcall",
                    uuid=uuid.uuid4().hex,
                    reply=True,
                    packet_content=ActorCall(
                        source=local_address,
                        destination=rmf,
                        payload=PayloadData(args=list(args), kwargs=kwargs),
                    ),
                ),
            )

        return await self._executor.execute(rmf, *args, **kwargs)
