import { useState, useMemo } from 'react';
import sortWithDirection from 'lodash-es/orderBy';
/**
 * @typedef {Object} OrderingState
 * @property {string} orderBy - property that's being used for ordering
 * @property {boolean} isAscendingOrder - is ordering ascending
 */

/**
 * @typedef {Ordering} Ordering
 * @property {string} orderBy - property that's being used for ordering
 * @property {boolean} isAscendingOrder - is ordering ascending
 * @property {function} setOrderBy - set ordering key
 * @property {function} setIsAscendingOrder - set ascending or descending order
 */

/**
 * Provides state and action for data ordering controls
 * If same key is set as `orderBy` order direction is reversed
 * Example:
 ```
  const { orderBy, isAscendingOrder, setOrderBy } = useOrderingState();
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchStuff({
      orderBy,
      orderDirection: isAscendingOrder ? 'asc' : 'desc',
    }).then(setData);
  }, [orderBy, isAscendingOrder]);

  return (
    <SomeOrderedDataView
      orderBy={orderBy}
      isAscendingOrder={isAscendingOrder}
      onOrderChange={setOrderBy}
    />
  );
 ```
 * @param {OrderingState} initialState - initial values that will be applied
 * on mount
 * @returns {Ordering}
 */
export function useOrderingState(config) {
  const orderKey = config?.orderBy ?? '';
  const initialDirection = config?.isAscendingOrder ?? false;
  const [orderBy, setOrderBy] = useState(orderKey);
  const [isAscendingOrder, setIsAscendingOrder] = useState(initialDirection);
  return useMemo(() => ({
    orderBy,
    isAscendingOrder,
    setOrderBy: newOrderKey => {
      if (newOrderKey === orderBy) {
        setIsAscendingOrder(!isAscendingOrder);
      } else {
        setOrderBy(newOrderKey);
      }
    },
    setIsAscendingOrder
  }), [orderBy, isAscendingOrder]);
}
/**
 * Ordering hook that provides an array of ordered objects along
 * with state of ordering and actions to change it
 * Example:
 ```
  const unorderedItems = getItemsSomehow();
  const { items, orderBy, isAscendingOrder, setOrderBy } = useOrdering(
    unorderedItems
  );
  const headers = ['title', 'subTitle'];

  return (
    <div>
      {headers.map(header => (
        <div
          onClick={() => setOrderBy(header)}
          className={orderBy === header ? 'active' : ''}
        >
          {header}
          <FontAwesomeIcon icon={isAscendingOrder ? faSortUp : faSortDown} />
        </div>
      ))}
      {items.map(item => (
        <div>
          <div>{item.title}</div>
          <div>{item.subTitle}</div>
        </div>
      ))}
    </div>
  );
 ```
 *
 * @param {[object]} items - array of objects that needs to be ordered
 * @param {OrderingState} initialState - initial values for ordering
 * @param {function} orderFunction - custom ordering function
 * @returns
 */

export function useOrdering(items, initialState, orderFunction = undefined) {
  const {
    orderBy,
    isAscendingOrder,
    setOrderBy,
    setIsAscendingOrder
  } = useOrderingState(initialState);
  const orderedItems = useMemo(() => {
    if (!orderBy) {
      return items;
    }
    const orders = isAscendingOrder ? 'asc' : 'desc';
    if (orderFunction) {
      return sortWithDirection(items, item => orderFunction(item, orderBy, isAscendingOrder), orders);
    }
    return sortWithDirection(items, [orderBy], orders);
  }, [isAscendingOrder, orderBy, items, orderFunction]);
  return useMemo(() => ({
    items: orderedItems,
    orderBy,
    isAscendingOrder,
    setOrderBy,
    setIsAscendingOrder
  }), [orderedItems, orderBy, isAscendingOrder, setOrderBy, setIsAscendingOrder]);
}