import { useReducer } from 'react';

interface State<T> {
  queue: T[];
}
interface Action<T> {
  type: 'enqueue' | 'dequeue' | 'clear' | 'reset';
  values?: T[];
}

function createReducer<T>() {
  return function (state: State<T>, action: Action<T>) {
    switch (action.type) {
      case 'enqueue': {
        state.queue.push(...action.values!);
        break;
      }
      case 'dequeue': {
        state.queue.shift();
        break;
      }
      case 'clear': {
        return { queue: [] };
      }
      case 'reset': {
        return { queue: action.values || [] };
      }
      default:
        break;
    }
    return {
      queue: state.queue,
    };
  };
}

export function useQueue<T>(arr: T[] = []) {
  const [state, dispatch] = useReducer(createReducer<T>(), {
    queue: arr,
  });

  return {
    enqueue: (...values: T[]) => {
      dispatch({ type: 'enqueue', values });
      return values;
    },
    dequeue: () => {
      const item = state.queue[0] || null;
      dispatch({ type: 'dequeue' });
      return item;
    },
    clear: () => dispatch({ type: 'clear' }),
    reset: (values: T[] = []) => {
      dispatch({ type: 'reset', values });
    },
    forEach: (callback: (value: T, index: number, arr: T[]) => void) =>
      state.queue.forEach(callback),
    map: function <Result>(
      callback: (value: T, index: number, arr: T[]) => Result,
    ) {
      return state.queue.map(callback);
    },
    includes: (value: T) => state.queue.includes(value),
    peek: () => state.queue[0] || null,
    entries: () => state.queue.entries(),
    values: () => state.queue.values(),
    keys: () => state.queue.keys(),
    size: state.queue.length,
    *[Symbol.iterator]() {
      const iterator1 = state.queue[Symbol.iterator]();
      for (const item of iterator1) {
        yield item;
      }
    },
  };
}
