import inspect
import asyncio
from asyncio import CancelledError
from collections.abc import Callable
from types import FunctionType
from typing import Optional

# from axirpc.entity import RemoteMethod, NetEntity
from .connect import NetworkConnect, create_bind_remote_func
from .util import get_namespace, ResourceManager, namespace_to_uuid


class NetworkServer:
    def __init__(self, addr: tuple[str, int], on_accept: Callable[[NetworkConnect], ...] | None = None):
        """
        注意：on_accept如果为同步函数时，他的堵塞操作会堵塞整个循环
        """
        super().__init__()
        self.server: Optional[asyncio.Server] = None
        self.addr = addr
        self.connections: set[NetworkConnect] = set()
        self.on_accept = on_accept
        self.loop: asyncio.AbstractEventLoop | None = None
        self.res_man = ResourceManager()

    def register_func(self, func: FunctionType | Callable, namespace=None):
        namespace = namespace or get_namespace(func)
        uuid = namespace_to_uuid(namespace)
        self.res_man.register(func, uuid)
        return create_bind_remote_func(func, self.connections)

    # 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

    async def run(self):
        self.loop = asyncio.get_running_loop()
        self.server = await asyncio.start_server(self._connect, *self.addr)
        try:
            async with self.server:
                await self.server.serve_forever()
        except CancelledError:
            ...
        for conn in self.connections:
            conn.close()

    async def _connect(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
        addr = writer.get_extra_info("peername")
        conn = NetworkConnect(addr)
        conn.set_server(self, reader, writer)
        self.connections.add(conn)
        if self.on_accept:
            if inspect.iscoroutinefunction(self.on_accept):
                await self.on_accept(conn)
            else:
                self.on_accept(conn)
        await conn.run()

    def stop(self):
        self.server.close()

    def disconnect(self, conn: NetworkConnect):
        conn.close()
        if conn in self.connections:
            self.connections.remove(conn)
