from typing import Callable, Any, Iterator, AsyncIterator, TypeVar, ParamSpec, Self, Type, Optional, Union, Literal, overload
from quent import ResultOrAwaitable, Null


T = TypeVar('T')
P = ParamSpec('P')


ReturnValue = TypeVar('ReturnValue')
RootValue = TypeVar('RootValue')
CurrentValue = TypeVar('CurrentValue')
NextValue = TypeVar('NextValue')
IgnoredValue = TypeVar('IgnoredValue')

CurrentItem = TypeVar('CurrentItem')
NextItem = TypeVar('NextItem')
IgnoredItem = TypeVar('IgnoredItem')

CurrentValueContext = TypeVar('CurrentValueContext')

RootLink = RootValue | Callable[[P], RootValue]

IN = TypeVar('IN')
OUT = TypeVar('OUT')

Link = Callable[[IN | P], OUT]
AnyLink = OUT | Callable[[IN | P], OUT]


FuncT = TypeVar("FuncT", bound=Callable[..., Any])
class Chain:
  def __init__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> None: ...

  def config(self, *, autorun: Optional[bool] = None, debug: Optional[bool] = None) -> Self: ...

  def autorun(self, autorun: bool = True) -> Self: ...

  def clone(self) -> Self: ...

  def freeze(self) -> FrozenChain: ...

  def decorator(self) -> Callable[[FuncT], FuncT]: ...

  def run(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[ReturnValue]: ...

  @overload
  def then(self, v: NextValue, *args: P.args, **kwargs: P.kwargs) -> Self: ...
  @overload
  def then(self, v: Callable[[CurrentValue], NextValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...
  @overload
  def then(self, v: Callable[..., NextValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def do(self, fn: Link[CurrentValue, IgnoredValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  @overload
  def root(self) -> Self: ...
  @overload
  def root(self, fn: Link[RootValue, NextValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def root_do(self, fn: Link[RootValue, IgnoredValue] , *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def attr(self, name: str) -> Self: ...

  def attr_fn(self, name: str, *args, **kwargs) -> Self: ...

  def except_(
    self, fn: Link[RootValue, IgnoredValue], *args: P.args,
    exceptions: list[Type[BaseException]] | Type[BaseException] = None, raise_: bool = True, **kwargs: P.kwargs
  ) -> Self: ...

  def finally_(self, fn: Link[RootValue, IgnoredValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def while_true(self, fn: Link[CurrentValue, None], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def iterate(self, fn: Optional[Link[CurrentItem, NextItem]] = None) -> Union[Iterator, AsyncIterator]: ...

  def iterate_do(self, fn: Optional[Link[CurrentItem, IgnoredItem]] = None) -> Union[Iterator, AsyncIterator]: ...

  def foreach(self, fn: Link[CurrentItem, NextItem]) -> Self: ...

  def foreach_do(self, fn: Link[CurrentItem, IgnoredItem]) -> Self: ...

  def with_(self, fn: Link[CurrentValueContext, NextValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def with_do(self, fn: Link[CurrentValueContext, IgnoredValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def if_(self, v: AnyLink[NextValue, CurrentValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def else_(self, v: AnyLink[NextValue, CurrentValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def if_not(self, v: AnyLink[NextValue, CurrentValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def if_raise(self, exc: BaseException) -> Self: ...

  def else_raise(self, exc: BaseException) -> Self: ...

  def if_not_raise(self, exc: BaseException) -> Self: ...

  def condition(self, fn: Link[CurrentValue, NextValue], *args: P.args, **kwargs: P.kwargs) -> Self: ...

  def not_(self) -> Self: ...

  def eq(self, value: Any) -> Self: ...

  def neq(self, value: Any) -> Self: ...

  def is_(self, value: Any) -> Self: ...

  def is_not(self, value: Any) -> Self: ...

  def in_(self, value: Any) -> Self: ...

  def not_in(self, value: Any) -> Self: ...

  def or_(self, value: Any) -> Self: ...

  def isinstance_(self, *types: Type) -> Self: ...

  def trace(self, label: Optional[str] = None) -> Self: ...

  def debug(self, fn: Optional[Callable[[Any], None]] = None) -> Self: ...

  def raise_(self, exc: BaseException) -> Self: ...

  @classmethod
  def null(cls) -> Null: ...

  @classmethod
  def return_(cls, v: Optional[AnyLink[ReturnValue, CurrentValue]] = None, *args: P.args, **kwargs: P.kwargs) -> None: ...

  @classmethod
  def break_(cls, v: Optional[AnyLink[NextValue, CurrentValue]] = None, *args: P.args, **kwargs: P.kwargs) -> None: ...

  @classmethod
  def from_(cls, *args: Any) -> Self: ...

  def __or__(self, other: Union[NextValue, Callable[[CurrentValue], NextValue]]) -> Self: ...

  def __call__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[ReturnValue]: ...


class Cascade(Chain):
  def __init__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> None: ...
  def run(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[RootValue]: ...


class ChainAttr(Chain):
  def __getattr__(self, name: str) -> Self: ...

  @overload
  def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Self: ...
  @overload
  def __call__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[ReturnValue]: ...


class CascadeAttr(Cascade):
  def __getattr__(self, name: str) -> Self: ...
  
  @overload
  def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Self: ...
  @overload
  def __call__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[RootValue]: ...


class FrozenChain:
  def decorator(self) -> Callable[[FuncT], FuncT]: ...

  def run(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[ReturnValue]: ...

  def __call__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> ResultOrAwaitable[ReturnValue]: ...

class run:
  """Replacement for Chain.run() when using pipe syntax"""
  def __init__(self, v: RootLink = None, *args: P.args, **kwargs: P.kwargs) -> None: ...
  def __ror__(self, other: Chain) -> ResultOrAwaitable[ReturnValue]: ...

class QuentException(Exception): ...
