import { OBSERVERS_PATH } from "./const";
import { Observer } from "./Observer";
import { GetReactiveValue, ReactiveMethods } from "./types";
import { isObject, Ref } from "../core";


export function withReactiveMethods<
  Instance,
  Names extends keyof Partial<Instance>,
  Return extends ReactiveMethods<Instance, Names>
>(ref: Ref<Instance>, methods?: readonly Names[]): Return {
  const obj: Record<any, any> = {};

  if (!methods) {
    return obj;
  }

  methods.forEach(name => {
    obj[name] = function (...args: any[]) {
      const current: any = ref.current || ref.value;

      return current[name](...args);
    };
  });
  return obj as Return;
}


export function observe<Type>(defaultValue?: Type): Observer<Type> {
  return new Observer<Type>(defaultValue);
}

export function defineObservers(instance: any) {
  const observers: Record<string, Observer<any>> = {};

  Object.defineProperty(instance, OBSERVERS_PATH, {
    get() {
      return observers;
    },
  });

  return observers;
}

export function getObservers(instance: any): Record<string, Observer<any>> {
  if (!instance[OBSERVERS_PATH]) {
    defineObservers(instance);
  }
  return instance[OBSERVERS_PATH];
}

export function getObserver(instance: any, name: string, defaultValue?: any): Observer<any> {
  const observers = getObservers(instance);

  if (!observers[name]) {
    observers[name] = observe(defaultValue);
  }
  return observers[name];
}

export function setObserver(instance: any, name: string, observer: Observer<any>) {
  const observers = getObservers(instance);

  observers[name] = observer;
}

export function isObserver(val: any): val is Observer {
  return val && isObject(val) && ("current" in val && "subscribe" in val && "unsubscribe" in val);
}
