import cloneDeep from 'lodash/cloneDeep';

/**
 * Creates a reactive state object that batches changes and notifies all subscribers with the full old and new state.
 *
 * @param initialState - The initial state object
 * @returns Object with { state: reactive proxy, subscribe: function to add callbacks }
 *
 * @example
 * ```typescript
 * const { state, subscribe } = createBatchedReactiveState({ count: 0, user: { name: 'Alice' } });
 *
 * const unsubscribe1 = subscribe((newState, oldState) => {
 *   console.log('Subscriber 1:', oldState, '->', newState);
 * });
 *
 * const unsubscribe2 = subscribe((newState, oldState) => {
 *   console.log('Subscriber 2:', newState.count);
 * });
 *
 * state.count = 1;        // Triggers both subscribers once
 * state.user.name = 'Bob'; // Triggers both subscribers once (batched)
 *
 * unsubscribe1();         // Remove first subscriber
 * state.count = 2;        // Only subscriber 2 is called
 * ```
 */
export function createReactiveState<T extends object>(initialState: T) {
  const callbacks = new Set<(newState: T, oldState: T) => void>();
  let isBatching = false;
  let oldState: T | null = null;
  let scheduled = false;

  const rootState = cloneDeep(initialState);

  function createReactiveObject(obj: any): any {
    return new Proxy(obj, {
      get(target, property, receiver) {
        const value = Reflect.get(target, property, receiver);
        return typeof value === 'object' && value !== null ? createReactiveObject(value) : value;
      },
      set(target, property, value, receiver) {
        if (!isBatching) {
          isBatching = true;
          oldState = cloneDeep(rootState);
        }
        const success = Reflect.set(target, property, value, receiver);
        if (success) {
          if (!scheduled) {
            scheduled = true;
            queueMicrotask(() => {
              callbacks.forEach((cb) => cb(rootState, oldState!));
              isBatching = false;
              oldState = null;
              scheduled = false;
            });
          }
        }
        return success;
      },
    });
  }

  const state = createReactiveObject(rootState);

  return [
    state,
    function subscribe(callback: (newState: T, oldState: T) => void) {
      callbacks.add(callback);
      return () => callbacks.delete(callback);
    },
  ] as const;
}
