import { createElement, VNode } from "preact";

export type StateFunc<S> = (p: S) => VNode;

export type StateViewMap<StateType extends { status: string }> = {
  [S in StateType as S["status"]]: StateFunc<S>;
};

export type RecursiveState<S extends object> = S | (() => RecursiveState<S>);

export function compose<SType extends { status: string }, PType>(
  hook: (p: PType) => RecursiveState<SType>,
  viewMap: StateViewMap<SType>,
): (p: PType) => VNode {

  function withHook(stateHook: () => RecursiveState<SType>): () => VNode {
    function ComposedComponent(): VNode {
      const state = stateHook();

      if (typeof state === "function") {
        const subComponent = withHook(state);
        return createElement(subComponent, {});
      }

      const statusName = state.status as unknown as SType["status"];
      const viewComponent = viewMap[statusName] as unknown as StateFunc<SType>;
      return createElement(viewComponent, state);
    }

    return ComposedComponent;
  }

  return (p: PType) => {
    const h = withHook(() => hook(p));
    return h();
  };
}

export function recursive<PType>(
  hook: (p: PType) => RecursiveState<VNode>,
): (p: PType) => VNode {

  function withHook(stateHook: () => RecursiveState<VNode>): () => VNode {
    function ComposedComponent(): VNode {
      const state = stateHook();

      if (typeof state === "function") {
        const subComponent = withHook(state);
        return createElement(subComponent, {});
      }

      return state;
    }

    return ComposedComponent;
  }

  return (p: PType) => {
    const h = withHook(() => hook(p));
    return h();
  };
}



/**
 *
 * @param obj VNode
 * @returns
 */
export function saveVNodeForInspection<T>(obj: T): T {
  // @ts-ignore
  window["showVNodeInfo"] = function showVNodeInfo() {
    inspect(obj);
  };
  return obj;
}
function inspect(obj: any) {
  if (!obj) return;
  if (obj.__c && obj.__c.__H) {
    const componentName = obj.__c.constructor.name;
    const hookState = obj.__c.__H;
    const stateList = hookState.__ as Array<any>;
    console.log("==============", componentName);
    stateList.forEach((hook) => {
      const { __: value, c: context, __h: factory, __H: args } = hook;
      if (typeof context !== "undefined") {
        const { __c: contextId } = context;
        console.log("context:", contextId, hook);
      } else if (typeof factory === "function") {
        console.log("memo:", value, "deps:", args);
      } else if (typeof value === "function") {
        const effectName = value.name;
        console.log("effect:", effectName, "deps:", args);
      } else if (typeof value.current !== "undefined") {
        const ref = value.current;
        console.log("ref:", ref instanceof Element ? ref.outerHTML : ref);
      } else if (value instanceof Array) {
        console.log("state:", value[0]);
      } else {
        console.log(hook);
      }
    });
  }
  const children = obj.__k;
  if (children instanceof Array) {
    children.forEach((e) => inspect(e));
  } else {
    inspect(children);
  }
}
