import { keys } from "src/core";
import { Ref } from "src/core/types";
import { ReactiveAdapter } from "./ReactiveAdapter";
import { ReactiveSubscribe } from "./ReactiveSubscribe";
import { ReactiveEventCallback } from "./types";
import { getObservers, withReactiveMethods } from "./utils";

export function adaptReactive<
  Instance extends ReactiveSubscribe<Record<string, any>>,
  State extends Record<string, any> = {},
  Methods extends keyof Partial<Instance> = any,
  Data = any,
  Events extends Record<string, any> = {},
  >(adapter: ReactiveAdapter<Instance, State, Methods, Data, Events>) {

  function data(): Data {
    return adapter.data?.() ?? {} as Data;
  }

  const instanceRef: Ref<Instance> = { current: adapter.created?.(data()) || null };
  let firstState: State | null = null;

  return {
    state(): State {
      const inst = instanceRef.current;

      if (firstState) {
        return firstState;
      }
      if (adapter.state) {
        firstState = adapter.state;
      } else if (inst) {
        const observers = getObservers(inst);

        firstState = keys(observers).reduce((prev, cur) => {
          prev[cur] = observers[cur].current;
          return prev;
        }, {} as any);
      }
      return firstState || {} as State;
    },
    instance() {
      return instanceRef.current;
    },
    mounted(): void {
      instanceRef.current = adapter.mounted?.(data()) || instanceRef.current;
    },
    init(): void {
      adapter.init?.(instanceRef.current!, data());
    },
    destroy(): void {
      adapter.destroy?.(instanceRef.current!, data());
    },
    methods() {
      return withReactiveMethods<any, any, any>(instanceRef, adapter.methods);
    },
    on(eventName: string, listener: ReactiveEventCallback<any, any>) {
      adapter.on?.(instanceRef.current!, eventName as never, listener);
    },
    off(eventName: string, listener: ReactiveEventCallback<any, any>) {
      adapter.off?.(instanceRef.current!, eventName as never, listener);
    },
  };
}
