import { useReducer } from 'react'
const { max, min, ceil } = Math

const getTotalPages = (perPage, data) => ceil(data.length / perPage) - 1

const getNextPage = (page, perPage, data) =>
  min(Math.ceil(data.length / perPage) - 1, page + 1)

const getPreviousPage = (page) => max(0, page - 1)

const extractPage = (page, perPage, data) => {
  const from = page * perPage
  const to = (page + 1) * perPage

  return data ? data.slice(from, to) : []
}

const paginationReducer = (state, action) => {
  switch (action.type) {
    case 'SET_DATA': {
      const data = action.payload.data
      const { page, perPage } = state
      const paginated = extractPage(page, perPage, data)
      return { ...state, data, paginated }
    }
    case 'FIRST_PAGE': {
      const { perPage, data } = state
      const page = 0
      const paginated = extractPage(0, perPage, data)

      return {
        ...state,
        page,
        paginated,
      }
    }
    case 'LAST_PAGE': {
      const { perPage, data } = state
      const page = getTotalPages(perPage, data)
      const paginated = extractPage(page, perPage, data)

      return {
        ...state,
        page,
        paginated,
      }
    }
    case 'NEXT_PAGE': {
      const { data, perPage } = state
      const page = getNextPage(state.page, perPage, data)
      const paginated = extractPage(page, perPage, data)

      return {
        ...state,
        page,
        paginated,
      }
    }
    case 'PREVIOUS_PAGE': {
      const page = getPreviousPage(state.page)
      const { data, perPage } = state
      const paginated = extractPage(page, perPage, data)

      return {
        ...state,
        page,
        paginated,
      }
    }
    case 'GOTO_PAGE': {
      const { data, perPage } = state
      const page = action.payload.page
      const paginated = extractPage(page, perPage, data)

      return {
        ...state,
        page,
        paginated,
      }
    }
    case 'PER_PAGE': {
      const { data } = state
      const perPage = action.payload.perPage
      const page = Math.min(state.page, getTotalPages(perPage, data))
      const paginated = extractPage(page, perPage, data)

      return {
        ...state,
        perPage,
        page,
        paginated,
      }
    }
  }
}

const usePagination = (data = [], opts = {}) => {
  const { perPage, page } = { page: 0, perPage: 10, ...opts }

  const [state, dispatch] = useReducer(paginationReducer, {
    data,
    page,
    perPage,
    paginated: extractPage(page, perPage, data),
  })

  return {
    firstPage: () => dispatch({ type: 'FIRST_PAGE' }),
    lastPage: () => dispatch({ type: 'LAST_PAGE' }),
    nextPage: () => dispatch({ type: 'NEXT_PAGE' }),
    previousPage: () => dispatch({ type: 'PREVIOUS_PAGE' }),
    gotoPage: (x) => dispatch({ type: 'GOTO_PAGE', payload: { page: x } }),
    setPerPage: (x) => dispatch({ type: 'PER_PAGE', payload: { perPage: x } }),
    setData: (x) => dispatch({ type: 'SET_DATA', payload: { data: x } }),
    totalPages: ceil(data.length / state.perPage),
    paginatedData: state.paginated,
    page: state.page + 1,
    perPage: state.perPage,
  }
}

export default usePagination
