import asyncio
import inspect
from asyncio import IncompleteReadError
from collections.abc import Callable
from types import FunctionType
from typing import Optional, TYPE_CHECKING, TypeVar, Collection

# from axirpc.entity import NetEntity, RemoteMethod
from .util import PackageSender, get_namespace, namespace_to_uuid, ResourceManager

if TYPE_CHECKING:
    from .server import NetworkServer
_Function = TypeVar("_Function", bound=FunctionType | Callable)


class ConnectBindRemoteFunction:
    def __init__(self, remote_func: FunctionType, targets: Collection["NetworkConnect"]):
        self.remote_func = remote_func
        self.targets = targets

    async def __call__(self, *args, **kwargs):
        from axirpc.util import Package
        package = Package(self.remote_func, args, kwargs)
        for target in self.targets:
            await target.psender.send_package(package)


class SyncConnectBindRemoteFunction:
    def __init__(self, remote_func: FunctionType, targets: Collection["NetworkConnect"]):
        self.remote_func = remote_func
        self.targets = targets

    def __call__(self, *args, **kwargs):
        from axirpc.util import Package
        package = Package(self.remote_func, args, kwargs)
        for target in self.targets:
            asyncio.run_coroutine_threadsafe(target.psender.send_package(package), target.loop)


def create_bind_remote_func(func: FunctionType, targets: Collection["NetworkConnect"]):
    if inspect.iscoroutinefunction(func):
        return ConnectBindRemoteFunction(func, targets)
    return SyncConnectBindRemoteFunction(func, targets)


class NetworkConnect:
    def __init__(self, addr: tuple[str, int]):
        self.addr = addr
        self.psender: Optional[PackageSender] = None
        self.res_man = ResourceManager()
        self.server: Optional["NetworkServer"] = None
        self.loop: asyncio.AbstractEventLoop | None = None

    def register_func(self, func: _Function, namespace: Optional[str] = None) -> _Function:
        namespace = namespace or get_namespace(func)
        uuid = namespace_to_uuid(namespace)
        self.res_man.register(func, uuid)
        return create_bind_remote_func(func, {self})

    # def register_entity(self, entity: NetEntity):
    #     self.resource_manager.register(entity, entity.uuid)
    #     entity.owner = self
    #     for key, value in entity.__class__.__dict__.items():
    #         if isinstance(value, RemoteMethod):
    #             self.register_func(value.origin, value.namespace)
    #     return entity

    def set_server(self, server: "NetworkServer", reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
        self.res_man = server.res_man.create_sub_manager()
        self.psender = PackageSender(reader, writer, self.res_man)
        self.server = server
        self.loop = server.loop

    async def connect(self):
        if self.psender:
            raise RuntimeError("Do not connect repeatedly")
        reader, writer = await asyncio.open_connection(*self.addr)
        self.psender = PackageSender(reader, writer, self.res_man)

    async def run(self):
        self.loop = asyncio.get_running_loop()
        if not self.psender:
            await self.connect()
        while True:
            try:
                package = await self.psender.read_package()
            except IncompleteReadError:
                self.disconnect()
                return
            else:
                if inspect.iscoroutinefunction(package.func):
                    await package.func(*package.args, **package.kwargs)
                else:
                    package.func(*package.args, **package.kwargs)

    def disconnect(self):
        """关闭客户端，会识别是否属于服务端并执行对应关闭操作"""
        if self.server:
            self.server.disconnect(self)
        else:
            self.close()

    def close(self):
        self.psender.writer.close()
