import asyncio
import typing
import kungfu

from nodnod import Node
from nodnod.agent import EventLoopAgent
from nodnod.interface.scalar import scalar_node
from nodnod.node import is_type
from nodnod.scope import Scope


def self_hook(cls: type[Node], dep_name: str, dep_type: type[typing.Any]) -> kungfu.Pulse[str]:
    if is_type(dep_type, typing.Self):
        print(cls, dep_name, dep_type)
        return kungfu.Ok()
    return kungfu.Error("cannot process")


class NodeConstructor(Node, abstract=True):
    __args__ = ()

    @classmethod
    def __construct__(cls, *args: typing.Any) -> typing.Self:
        raise NotImplementedError

    def __class_getitem__(cls, item: typing.Any | tuple[typing.Any, ...], /) -> typing.Any:
        data = cls.__construct__(*((item,) if not isinstance(item, tuple) else item))
        node = type(
            cls.__name__, 
            (cls,), 
            dict(
                __module__=cls.__module__, 
                __args__=data,
            )
        )
        setattr(node, "__type__", node)
        return node


@scalar_node
class A(Node):
    @classmethod
    def __compose__(cls) -> int:
        return 123


class Lolik(NodeConstructor):
    def __init__(self, x: type[str | int]) -> None:
        self.x = x

    @classmethod
    def __construct__(cls, x: type[str | int] = str) -> typing.Self:
        return cls(x)

    def __compose__(self, a: A):
        print(a)
        return self.x


@scalar_node
class Boba:
    @classmethod
    def __compose__(cls, lolik: Lolik[int], lolik2: Lolik[str]) -> int:
        print("lolik", lolik.x, "lolik2", lolik2.x)
        return lolik


async def main():
    agent = EventLoopAgent.build({Boba})
    scope = Scope(detail="test")

    async with scope:
        await agent.run(scope, {})


asyncio.run(main())


__all__ = ("NodeConstructor",)