import React, { Context, ReactNode, useState, createContext, useEffect, useMemo } from 'react';
import { EventEmitter } from 'events';
import { isNull } from 'lodash';
import Debug from 'debug';

import { IStoreContext, defaultContext, useStore } from './store';

const debug = Debug('deepcase:store:local');

const localStorageEvent = new EventEmitter();

export const LocalContext = createContext(defaultContext);

export const LocalStoreProvider = ({
  context = LocalContext,
  children,
}: {
  context?: Context<IStoreContext>;
  children?: ReactNode;
}) => {
  const [useStore] = useState(() => {
    return function useStore<T extends any>(
      key: string,
      defaultValue: T,
    ): [T, (value: T) => any, () => any] {
      const memoDefaultValue = useMemo(() => defaultValue, []);
      const [value, _setValue] = useState<string>(typeof(localStorage) === 'undefined' ? JSON.stringify(memoDefaultValue) : (localStorage.hasOwnProperty(key) ? localStorage.getItem(key) : JSON.stringify(memoDefaultValue)));
      useEffect(
        () => {
          const hasOwnProperty = localStorage.hasOwnProperty(key);
          debug('init', { key, defaultValue: memoDefaultValue, hasOwnProperty });
          if (!hasOwnProperty) {
            const json = JSON.stringify(memoDefaultValue);
            localStorage.setItem(key, json);
            _setValue(json);
          }
          const fn = (value) => {
            const item = localStorage.getItem(key);
            if (typeof(item) === 'undefined' || isNull(item)) _setValue(JSON.stringify(memoDefaultValue));
            else _setValue(value);
          };
          localStorageEvent.on(key, fn);
          return () => {
            localStorageEvent.off(key, fn);
          };
        },
        [],
      );
      const [setValue] = useState(() => (value) => {
        debug('setValue', { key, defaultValue: memoDefaultValue, value });
        const json = JSON.stringify(value);
        localStorage.setItem(key, json);
        _setValue(json);
        localStorageEvent.emit(key, json);
      });
      const [unsetValue] = useState(() => () => {
        debug('unsetValue', { key, defaultValue: memoDefaultValue });
        localStorage.removeItem(key);
        localStorageEvent.emit(key, memoDefaultValue);
      });
      return [JSON.parse(value), setValue, unsetValue];
    };
  });

  return <context.Provider value={{ useStore }}>
    {children}
  </context.Provider>;
};

export function useLocalStore<T extends any>(key: string, defaultValue: T, context = LocalContext) {
  return useStore(key, defaultValue, context);
}
