import { useEffect } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Loader from '../loader';
import { fetchData, mergeStatuses } from '../../utils/fetch';
import { ERROR, INIT, LOADING } from '../../../redux/constants';
import Error from '../error';

function MultiFetcher(props) {
  useEffect(() => fetchData(props.action), [props.action]);
  // Loading
  if ([INIT, LOADING].includes(props.status)) return <Loader />;
  // Error
  if (props.status === ERROR) return props.renderError(props.errors);
  // Empty Data
  const emptyDataKeys = [];
  for (const key in props.data) {
    const data = props.data[key];
    if (
      (Array.isArray(data) && data.length === 0) ||
      (Object.entries(data).length === 0 && data.constructor === Object)
    ) {
      emptyDataKeys.push(key);
    }
  }
  if (emptyDataKeys.length !== 0) {
    const result = props.renderEmpty(emptyDataKeys);
    if (result !== null) return result;
  }
  // Good Condition
  return props.children(props.data);
}

MultiFetcher.propTypes = {
  actions: PropTypes.arrayOf(PropTypes.func).isRequired,
  action: PropTypes.func.isRequired,
  stateSelectors: PropTypes.objectOf(PropTypes.func).isRequired,
  status: PropTypes.string.isRequired,
  data: PropTypes.object.isRequired,
  renderEmpty: PropTypes.func.isRequired,
  renderError: PropTypes.func.isRequired,
  errors: PropTypes.object.isRequired,
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node), PropTypes.func])
};

MultiFetcher.defaultProps = {
  renderEmpty: () => null,
  // eslint-disable-next-line react/display-name
  renderError: () => <Error />
};

function mapStateToProps(state, { stateSelectors }) {
  const statuses = [];
  const data = {};
  const errors = {};
  for (const key in stateSelectors) {
    if (!(key in stateSelectors)) continue;
    const selectedState = stateSelectors[key](state);
    statuses.push(selectedState.status);
    data[key] = selectedState.data;
    if (selectedState.status === ERROR) errors[key] = selectedState.error;
  }
  return { status: mergeStatuses(statuses), data, errors };
}

function mapDispatchToProps(dispatch, { actions }) {
  function action() {
    return function (dispatch) {
      for (let i = 0; i < actions.length; i++) dispatch(actions[i]());
    };
  }

  return bindActionCreators({ action }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(MultiFetcher);
