import type {
  ReactElement,
  RefObject,
  RefAttributes,
  ReactNode,
  ComponentType,
  CSSProperties,
  ReactHTML,
} from "react";

declare const pendingStatus: unique symbol;
declare const resolveStatus: unique symbol;
declare const rejectStatus: unique symbol;

export type Status = typeof pendingStatus | typeof resolveStatus | typeof rejectStatus;

export declare function isPending(status: Status): boolean;

export declare function isResolve(status: Status): boolean;

export declare function isReject(status: Status): boolean;

export interface ResolveData<T, E = any> {
  first: boolean;
  status: Status;
  value: T;
  error: E;
}

export interface WatchOptions {
  update: () => void;
  unWatch: () => void;
  reWatch: () => void;

  get isWatching(): boolean;
}

export interface AwaitOptions<T, E = any> {
  resolve?: Promise<T>;
  init?: T;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: T) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
}

export declare function useAwait<T = any, E = any>(options: AwaitOptions<T, E>): ResolveData<T, E>;

export type AwaitProps<T, U = any, E = any> = AwaitOptions<T, E> & {
  onComputed?: (resolveData: ResolveData<T, E>) => U;
  children: (data: ResolveData<T, E> & { computed: U; placeholder?: RefObject<any>; }) => ReactElement;
};

export declare function Await<T = any, U = any, E = any>(props: AwaitProps<T, U, E>): ReactElement;

export interface AwaitStateOptions<T, Deps = any, Arg = any, E = any> {
  deps?: Deps;
  handle: (deps: Deps, arg?: Arg) => Promise<T>;
  init?: T;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: T) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
}

export declare function useAwaitState<T = any, Deps = any, Arg = any, E = any>(options: AwaitStateOptions<T, Deps, E>): [ResolveData<T, E>, (resolve?: Promise<Arg> | Arg) => void];

export type AwaitStateProps<T, Deps = any, Arg = any, U = any, E = any> = AwaitStateOptions<T, Deps, Arg, E> & {
  onComputed?: (resolveData: ResolveData<T, E>) => U;
  children: (data: ResolveData<T, E> & { computed: U; setResolve: (resolve?: Promise<Arg> | Arg) => void; placeholder?: RefObject<any>; }) => ReactElement;
};

export declare function AwaitState<T = any, Deps = any, Arg = any, U = any, E = any>(props: AwaitStateProps<T, Deps, Arg, U, E> & RefAttributes<{ setResolve: (resolve?: Promise<Arg> | Arg) => void; }>): ReactElement;

export interface ActionType<T extends string = string, P = any> {
  type: T;
  payload?: P;
}

export type Reducer<R = any, T extends string = string, P = any, D = any> = (action: ActionType<T, P> & { deps: D; }) => R | Promise<R>;

export type Reducers = Record<string, Reducer> | (() => Record<string, Reducer>);

export interface AwaitReducerOptions<T, Rs extends Reducers = Reducers, Deps = any, RsDeps extends Record<string, any> = Record<string, any>, Arg = any, E = any> {
  deps?: Deps;
  handle: (deps: Deps, arg?: Arg) => Promise<T>;
  reducersDeps?: RsDeps;
  reducers?: Rs;
  init?: T;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: T) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
}

export interface Dispatch<T extends string = string, P = any> {
  (action?: ActionType<T, P>): void;
}

type ReturnTypeOrSelf<T> = T extends (...args: any[]) => infer R ? R : T;

type ReducersKey<T> = ReturnTypeOrSelf<T> extends Record<string, any> ? {
  [K in keyof ReturnTypeOrSelf<T>]: ReturnTypeOrSelf<T>[K] extends Reducer ? K extends string ? K : never : never;
} : never;

type DispatchActions<T> = ReturnTypeOrSelf<T> extends Record<string, any> ? {
  [K in keyof ReturnTypeOrSelf<T>]: ReturnTypeOrSelf<T>[K] extends Reducer<infer R, infer T1, infer P, infer D> ? K extends string ? (payload?: P) => ActionType<K, P> : never : never;
} : never;

export declare function useAwaitReducer<T = any, Rs extends Reducers = Reducers, Deps = any, RsDeps extends Record<string, any> = Record<string, any>, Arg = any, E = any>(options: AwaitReducerOptions<T, Rs, Deps, RsDeps, Arg, E>): [ResolveData<T, E>, Dispatch<ReducersKey<Rs>[keyof ReducersKey<Rs>]>, DispatchActions<Rs>];

export type AwaitReducerProps<T, Rs extends Reducers = Reducers, Deps = any, RsDeps extends Record<string, any> = Record<string, any>, Arg = any, U = any, E = any> =
  AwaitReducerOptions<T, Rs, Deps, RsDeps, Arg, E> & {
  onComputed?: (resolveData: ResolveData<T, E>) => U;
  children: (data: ResolveData<T, E> & { computed: U; dispatch: Dispatch<ReducersKey<Rs>[keyof ReducersKey<Rs>]>, actions: DispatchActions<Rs>; placeholder?: RefObject<any>; }) => ReactElement;
};

export declare function AwaitReducer<T = any, Rs extends Reducers = Reducers, Deps = any, RsDeps extends Record<string, any> = Record<string, any>, Arg = any, U = any, E = any>(props: AwaitReducerProps<T, Rs, Deps, RsDeps, Arg, U, E> & RefAttributes<{ dispatch: Dispatch<ReducersKey<Rs>[keyof ReducersKey<Rs>]>, actions: DispatchActions<Rs>; }>): ReactElement;

export type AwaitStateViewProps =
  Omit<AwaitViewProps, "children">
  & { children: ReactElement<AwaitStateProps<any>, typeof AwaitState | typeof AwaitReducer>; };

export declare function AwaitStateView(props: AwaitStateViewProps): ReactElement;

export interface AwaitWatchOptions<T, Deps = any, E = any> {
  deps?: Deps;
  handle: (deps: Deps) => Promise<T>;
  compare?: ((newDeps: Deps, oldDeps: Deps) => boolean) | false;
  init?: T;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: T) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
}

export declare function useAwaitWatch<T = any, Deps = any, E = any>(options: AwaitWatchOptions<T, Deps, E>): [ResolveData<T, E>, WatchOptions];

export declare function useAwaitWatchObject<T = any, Deps extends Record<string, any> = Record<string, any>, E = any>(options: AwaitWatchOptions<T, Deps, E>): [ResolveData<T, E>, WatchOptions];

export declare function useAwaitWatchArray<T = any, Deps extends any[] = any[], E = any>(options: AwaitWatchOptions<T, Deps, E>): [ResolveData<T, E>, WatchOptions];

export interface OptimisticDeps<T> {
  value: T;
  optimisticValue: T;
}

export type AwaitWatchProps<T, Deps = any, U = any, E = any> = AwaitWatchOptions<T, Deps, E> & {
  onComputed?: (resolveData: ResolveData<T, E>) => U;
  children: (data: ResolveData<T, E> & { computed: U; watchOptions: WatchOptions; placeholder?: RefObject<any>; }) => ReactElement;
};

export declare function AwaitWatch<T = any, Deps = any, U = any, E = any>(props: AwaitWatchProps<T, Deps, U, E> & RefAttributes<{ watchOptions: WatchOptions; }>): ReactElement;

export declare function AwaitWatchObject<T = any, Deps extends Record<string, any> = Record<string, any>, U = any, E = any>(props: AwaitWatchProps<T, Deps, U, E> & RefAttributes<{ watchOptions: WatchOptions; }>): ReactElement;

export declare function AwaitWatchArray<T = any, Deps extends any[] = any[], U = any, E = any>(props: AwaitWatchProps<T, Deps, U, E> & RefAttributes<{ watchOptions: WatchOptions; }>): ReactElement;

export type AwaitWatchViewProps =
  Omit<AwaitViewProps, "children">
  & { children: ReactElement<AwaitWatchProps<any>, typeof AwaitWatch | typeof AwaitWatchObject | typeof AwaitWatchArray>; };

export declare function AwaitWatchView(props: AwaitWatchViewProps): ReactElement;

export interface AwaitListProps {
  order?: "forwards" | "backwards" | "together";
  tail?: "collapsed";
  gap?: number;
  children: ReactElement<AwaitProps<any> | any, typeof Await | any>[];
}

export declare function AwaitList(props: AwaitListProps): ReactElement;

export interface AwaitViewProps {
  root?: RefObject<Element | Document | null>;
  rootIsParent?: boolean;
  rootMargin?: string;
  threshold?: number;
  onIntersection?: (entry: IntersectionObserverEntry) => boolean;
  children: ReactElement<AwaitProps<any>, typeof Await>;
}

export declare function AwaitView(props: AwaitViewProps): ReactElement;

export interface AsyncProps<P = Record<string, any>, U = any, E = any> {
  wrap: ReactElement;
  compare?: ((newProps: P, oldProps: P) => boolean) | false;
  init?: ReactElement;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: ReactElement) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
  onComputed?: (resolveData: ResolveData<ReactElement, E>) => U;
  children: (data: ResolveData<ReactElement, E> & { computed: U; watchOptions: WatchOptions; placeholder?: RefObject<any>; }) => ReactElement;
}

export declare function Async<P = Record<string, any>, U = any, E = any>(props: AsyncProps<P, U, E> & RefAttributes<{ watchOptions: WatchOptions; }>): ReactElement;

export type AsyncViewProps = Omit<AwaitViewProps, "children"> & { children: ReactElement; };

export declare function AsyncView(props: AsyncViewProps): ReactElement;

export interface AsyncComponentOptions<P = Record<string, any>, T = any, A = any, U = any, E = any> {
  name?: string;
  init?: (props: P) => T;
  compare?: ((newProps: P, oldProps: P, newAction: A, oldAction: A) => boolean) | false;
  delay?: number;
  jumpFirst?: boolean;
  cleanup?: boolean;
  onStart?: (first: boolean) => void;
  onEnd?: (value: T) => void;
  onError?: (error: E) => void;
  onFinal?: (first: boolean) => void;
  onComputed?: (resolveData: ResolveData<T, E>) => U;
  useAction?: (props: P, watchOptions: WatchOptions) => A;
  loader: (props: P, action: A) => Promise<T>;
  Component: (props: P) => ReactElement;
}

export declare function defineAsyncComponent<Props = Record<string, any>, T = any, A = any, U = any, E = any>(options: AsyncComponentOptions<Props, T, A, U, E>): ComponentType<Props & RefAttributes<WatchOptions>>;

export declare function useAsyncValue<T = any, A = any, U = any, E = any>(): (ResolveData<T, E> & { action: A; computed: U; placeholder?: RefObject<any>; watchOptions: WatchOptions; });

export interface ActionProps<A = any, O = any> {
  useAction: (options?: O) => A;
  options?: O;
  children: (action: A) => ReactElement;
}

export declare function Action<A = any, O = any>(props: ActionProps<A, O>): ReactElement;

export interface IfProps {
  condition: boolean | (() => boolean);
  not?: boolean;
  children: ReactNode;
  fallback?: ReactNode;
}

export declare function If(props: IfProps): ReactElement;

export interface ShowProps {
  condition: boolean | (() => boolean);
  not?: boolean;
  children: ReactNode;
}

export declare function Show(props: ShowProps): ReactElement;

type KeyType<T extends Record<any, any> | Array<any>, K extends string | number | Array<any>> =
  K extends string | number ?
    T[K] :
    K extends [infer First] ?
      T[First] :
      K extends [infer First, ...infer Others] ?
        KeyType<T[First], Others> :
        never;

export interface ForProps<T extends Record<any, any> | Array<any>, K extends string | number | Array<string | number> = any> {
  items: Iterable<T>;
  keyPath?: K;
  children: (data: { item: T; index: number; key: KeyType<T, K>; }) => ReactElement;
}

export declare function For<T extends Record<any, any> | Array<any> = any, K extends string | number | Array<string | number> = any>(props: ForProps<T, K>): ReactElement;

export interface MemoProps {
  deps?: any[];
  children: ReactNode;
}

export declare function Memo(props: MemoProps): ReactElement;

export interface HostProps {
  children: ReactElement | ReactElement[];
}

export declare function Host(props: HostProps): ReactElement;

export interface TmplProps<P extends Omit<Record<string, any>, "name" | "children">> {
  name?: string;
  children: ReactElement | ReactElement[] | ((props: P) => ReactElement);
}

export declare function Tmpl<P extends Omit<Record<string, any>, "name" | "children"> = any>(props: TmplProps<P>): ReactElement;

export type SlottedProps = Omit<Record<string, any>, "name" | "children">;

export declare function Slotted(props: SlottedProps): ReactElement;

export interface GenProps {
  children: ReactElement | ReactElement[];
}

export declare function Gen(props: GenProps): ReactElement;

export interface YieldProps<P extends Omit<Record<string, any>, "children">> {
  children: ReactElement | ReactElement[] | ((props: P) => ReactElement);
}

export declare function Yield<P extends Omit<Record<string, any>, "children"> = any>(props: YieldProps<P>): ReactElement;

export type NextProps = Omit<Record<string, any>, "children">;

export declare function Next(props: NextProps): ReactElement;

export interface KeepAliveProps {
  uniqueKey: string;
  include?: (uniqueKey: string) => boolean;
  max?: number;
  meta?: any;
  children: ReactElement;
}

export interface KeepAliveRefAttributes {
  get current(): [uniqueKey: string, meta: any];

  get length(): number;

  get: () => Record<string, any>;
  remove: (uniqueKey: string) => void;
}

export declare function KeepAlive(props: KeepAliveProps & RefAttributes<KeepAliveRefAttributes>): ReactElement;

export type Activated = () => (() => void) | void;

export declare function useActivated(activated: Activated | Activated[]): void;

export declare function useKeepAliveMeta<T = any>(): T;

export interface TransitionProps {
  uniqueKey: any;
  tag?: keyof ReactHTML;
  name?: string;
  type?: "transition" | "animation" | "animate";
  duration?: number | { enter: number; leave: number; move: number; };
  appear?: boolean;
  enterFromClass?: string;
  enterActiveClass?: string;
  enterToClass?: string;
  leaveFromClass?: string;
  leaveActiveClass?: string;
  leaveToClass?: string;
  onEnter?: (element: HTMLElement) => void;
  onLeave?: (element: HTMLElement, done: () => void) => void;
  className?: string;
  style?: CSSProperties;
  children: ReactElement;
}

export declare function Transition(props: TransitionProps & RefAttributes<HTMLElement>): ReactElement;

export interface TransitionGroupProps<T extends Record<any, any> | Array<any>, K extends string | number | Array<string | number> = any> {
  tag?: keyof ReactHTML;
  name?: string;
  type?: "transition" | "animation" | "animate";
  duration?: number | { enter: number; leave: number; move: number; };
  appear?: boolean;
  enterFromClass?: string;
  enterActiveClass?: string;
  enterToClass?: string;
  leaveFromClass?: string;
  leaveActiveClass?: string;
  leaveToClass?: string;
  moveClass?: string;
  onEnter?: (elements: Array<HTMLElement>) => void;
  onLeave?: (elements: Array<{ element: HTMLElement; done: () => void; }>) => void;
  onMove?: (elements: Array<{ element: HTMLElement; x: number; y: number; }>) => void;
  cssVars?: { x: string; y: string; };
  items: Iterable<T>;
  keyPath?: K;
  className?: string;
  style?: CSSProperties;
  children: (data: { item: T; index: number; key: KeyType<T, K>; }) => ReactElement;
}

export declare function TransitionGroup<T extends Record<any, any> | Array<any> = any, K extends string | number | Array<string | number> = any>(props: TransitionGroupProps<T, K> & RefAttributes<HTMLElement>): ReactElement[];