reducers/bets.js

/* eslint-disable no-param-reassign */
/**
 * @namespace reducer/betsReducer
 */
import { fromJS } from 'immutable';
import {
  setActiveMarkets, parseTurboTimeline,
  parseMatches, parseOdds, parseTimeline, parseSelectedMarket,
} from '../utils/parser';
import {
  SELECT_MARKET,
  SELECT_SUBSELECTION,
  LIVE_FINISHED,
  GET_INIT_SUCCESS,
  FETCH_ROUND_SUCCESS,
  SET_ROUND_LOADER,
  GET_ROUND_SUCCESS,
  REPEAT_CALL_SUCCESS,
  SET_MARKETS,
  GET_TURBO_ROUND_SUCCESS,
  SET_END_DATE,
  KICK_OFF_SUCCESS,
  SELECT_LEAGUE_SUCCESS,
  GO_TO_LOGIN,
  SET_USER_DATA,
  BET_RE_COUNT,
  SET_INITIALISE_BET,
  CHANGE_PAGE,
  SHOW_KICK_OFF,
  SET_TURBO_ROUND_ID,
  SET_STATISTICS,
  CLEAR_STATISTICS,
} from '.';

const initialState = fromJS({
  selectedMarket: null,
  selectedSubselection: 0,
  selectedLeague: null,
  selectedLeagueIndex: null,
  isLeagueSelectionOpen: false,
  haveLiveMatches: false,
  markets: undefined,
  activeMarkets: null,
  leagues: null,
  matches: null,
  roundId: null,
  turboRoundId: null,
  roundEnd: null,
  liveId: null,
  liveStart: null,
  repeatRoundCall: 0,
  nextRound: null,
  initialBetsLoaded: false,
  initialTurboBetsLoaded: false,
  noBettingTimeCountdown: 0,
  noBettingTime: 0,
  liveMatchDuration: 0,
  isBetsLoading: false,
  showNoBetTime: false,
  roundLoading: false,
  timeline: null,
  odds: null,
  gameType: null,
  showKickOff: false,
  halftimeResults: null,
  results: null,
  numberOfBets: 0,
  classicRoundEnd: null,
  winCap: null,
  statistics: {},
});

/**
 * @memberof reducer/betsReducer
 * @param {object}  state Contain initial and final state of data
 * @param {object}  action Return the action object
 *
 * @property {object}  [selectedMarket=null]
 * @property {number}  [selectedSubselection=0]
 * @property {object}  [selectedLeague=null]
 * @property {number}  [selectedLeagueIndex=null]
 * @property {boolean}  [isLeagueSelectionOpen=false]
 * @property {boolean}  [haveLiveMatches=false]
 * @property {Array}  [markets=undefined]
 * @property {Array}  [activeMarkets=null]
 * @property {Array}  [leagues=null]
 * @property {Array}  [matches=null]
 * @property {number}  [roundId=null]
 * @property {Time}  [roundEnd=null]
 * @property {number}  [liveId=null]
 * @property {Time}  [liveStart=null]
 * @property {number}  [repeatRoundCall=0] Maximum 5 time to repeat round API
 * @property {object}  [nextRound=null] Contain number of week for betting round and live round when
 *  round API is called while is no betting time
 * @property {boolean}  [initialBetsLoaded=false]
 * @property {boolean}  [initialTurboBetsLoaded=false]
 * @property {number}  [noBettingTimeCountdown=0] Time when modal open
 * @property {number}  [noBettingTime=0] Time when placebet is disabled
 * @property {number}  [liveMatchDuration=0]
 * @property {boolean}  [isBetsLoading=false]
 * @property {boolean}  [showNoBetTime=false]
 * @property {boolean}  [roundLoading=false]
 * @property {string|null}  [gameType=null]
 * @property {boolean} showKickOff Show Kick Off button
 *
 * @returns {state}
 */
function betsReducer(state = initialState, action) { // NOSONAR
  switch (action.type) {
    /**
     * [Action Creator]
     * This action is use to change selected market and set default submarket if it has
     *
     * @memberof reducer/betsReducer
     * @example setSelectedMarket(payload)
     * @function reducer/betsReducer~SELECT_MARKET
     * @param {object} market
     */
    case SELECT_MARKET: {
      const market = parseSelectedMarket(action.payload.market);
      return state
        .set('selectedMarket', fromJS(market))
        .set('selectedSubselection', 0);
    }
    /**
     * [Action Creator]
     * This action is use to change selected submarket
     *
     * @memberof reducer/betsReducer
     * @example setSelectedSubselection(payload)
     * @function reducer/betsReducer~SELECT_SUBSELECTION
     * @param {Array} submarkets
     * @param {number} selectedSubselection
     */
    case SELECT_SUBSELECTION: {
      const selectedMarket = state.get('selectedMarket').toJS();
      selectedMarket.selections = Object
        .keys(action.payload.submarkets[action.payload.selectedSubselection].selections);

      return state
        .set('selectedMarket', fromJS(selectedMarket))
        .set('selectedSubselection', action.payload.selectedSubselection);
    }
    /**
     * [Action Creator]
     * This action is use to change selected league
     *
     * @memberof reducer/betsReducer
     * @example setSelectedLeague(payload)
     * @function reducer/betsReducer~SELECT_LEAGUE
     * @param {object} league
     * @param {number} index
     */
    case SELECT_LEAGUE_SUCCESS: {
      return state
        .set('selectedLeague', fromJS(action.payload.league))
        .set('selectedLeagueIndex', action.payload.index)
        .set('gameType', action.payload.type)
        .set('isLeagueSelectionOpen', false)
        .set('selectedSubselection', 0)
        .set('haveLiveMatches', action.payload.clearLiveMatch ? false : state.get('haveLiveMatches'));
    }
    /**
     * [Action Creator]
     * This action is use to change avaliable markets for selected league
     *
     * @memberof reducer/betsReducer
     * @example setMarkets(payload)
     * @function reducer/betsReducer~SET_MARKETS
     */
    case SET_MARKETS: {
      let odds = state.get('odds');
      if (odds) {
        odds = odds.toJS();
        const selectedLeague = state.get('selectedLeague').toJS();
        const currentMarkets = state.get('markets').toJS();
        const { markets, activeMarkets, selectedMarket } = setActiveMarkets(
          currentMarkets, odds[selectedLeague.id][0],
        );
        if (activeMarkets.toString() !== state.get('activeMarkets').toJS().toString()) {
          return state
            .set('markets', fromJS(markets))
            .set('activeMarkets', fromJS(activeMarkets))
            .set('selectedMarket', fromJS(selectedMarket))
            .set('selectedSubselection', 0);
        }
      }
      return state;
    }
    /**
     * [Action Creator]
     * This action is used to remove live round
     * using !! to convert extraDuration value if not sent
     * from undefined to primitive Boolean value
     * same as action.payload.extraDuration ? true : false
     *
     * @memberof reducer/betsReducer
     * @example liveFinished(payload)
     * @function reducer/betsReducer~SELECT_SUBSELECTION
     */
    case LIVE_FINISHED: {
      return state
        .set('haveLiveMatches', !!action.payload.extraDuration);
    }
    /**
     * [Received Data]
     * This action is use to setup initial data
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~GET_INIT_SUCCESS
     */
    case GET_INIT_SUCCESS:
      return state
        .set('noBettingTime', action.payload.noBettingTime)
        .set('noBettingTimeCountdown', action.payload.noBettingTimeCountdown)
        .set('markets', fromJS(action.payload.markets))
        .set('selectedMarket', fromJS(action.payload.selectedMarket))
        .set('leagues', fromJS(action.payload.leagues.sort((l1, l2) => l1.id - l2.id)))
        .set('selectedLeagueIndex', action.payload.selectedLeagueIndex)
        .set('selectedLeague', fromJS(action.payload.selectedLeague));
    /**
     * [Received Data]
     * This action is use to setup LIVE round and BETTING round
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~FETCH_ROUND_SUCCESS
     */
    case FETCH_ROUND_SUCCESS:
      return state
        .set('roundId', action.payload.roundId)
        .set('roundEnd', action.payload.roundEnd)
        .set('showNoBetTime', false)
        .set('haveLiveMatches', true);
    /**
     * [Received Data]
     * This action is use to set round loader
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~SET_ROUND_LOADER
     */
    case SET_ROUND_LOADER:
      return state
        .set('roundLoading', action.payload.roundLoading);
    /**
     * [Received Data]
     * This action is use to set round data
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~GET_ROUND_SUCCESS
     */
    case GET_ROUND_SUCCESS: {
      const leagues = state.get('leagues').toJS();
      leagues.map((l) => {
        l.enabled = 0;
        l.liveEnabled = 0;
      });
      let selectedLeagueIndex = state.get('selectedLeagueIndex');
      const currSelectedLeague = state.get('selectedLeague').toJS();
      const currRoundEnd = state.get('roundEnd') || state.get('classicRoundEnd');
      const showNoBetTime = !action.payload.initialBetsLoaded && action.payload.isNoBetTime;
      const round = action.payload.round;
      const roundResults = action.payload.roundResults;

      // parse matches data
      const odds4D = parseOdds(round.odds, round.selections);
      const matches = {};
      const timeline = {};
      const odds = {};
      let league;

      // set league week and season for next round and current live
      round.competitions.forEach((id, i) => {
        league = leagues.find((l) => l.id === id);
        if (league) {
          league.season = round.seasons[i];
          league.week = round.weeks[i];
          league.enabled = 1;
          odds[id] = odds4D[i];
          matches[id] = parseMatches(round.matches[i]);
        }
      });
      // parse live data
      roundResults.competitions.forEach((id, i) => {
        league = leagues.find((l) => l.id === id);
        if (league) {
          league.liveSeason = roundResults.seasons[i];
          league.liveWeek = roundResults.weeks[i];
          league.liveEnabled = 1;
          timeline[id] = parseTimeline(roundResults.timeline[i]);
          league.liveMatches = parseMatches(roundResults.matches[i]);
        }
      });

      let selectedLeague = leagues.find((l) => (
        l.id === currSelectedLeague.id && l.enabled === 1));

      if (!selectedLeague) {
        selectedLeagueIndex = leagues.findIndex((l) => l.enabled === 1 && l.liveEnabled === 1);
        selectedLeague = leagues[selectedLeagueIndex];
      }

      const currentMarkets = state.get('markets').toJS();
      const { markets, activeMarkets, selectedMarket } = setActiveMarkets(
        currentMarkets, odds[selectedLeague.id][0],
      );

      const nextRound = {
        roundId: round.roundId,
        roundEnd: action.payload.roundEnd,
      };

      return state
        .set('markets', fromJS(markets))
        .set('activeMarkets', fromJS(activeMarkets))
        .set('selectedMarket', fromJS(selectedMarket))
        .set('matches', fromJS(matches))
        .set('nextRound', fromJS(nextRound))
        .set('haveLiveMatches', action.payload.haveLiveMatches)
        .set('leagues', fromJS(leagues))
        .set('selectedLeague', fromJS(selectedLeague))
        .set('selectedLeagueIndex', selectedLeagueIndex)
        .set('timeline', fromJS(timeline))
        .set('odds', fromJS(odds))
        .set('showNoBetTime', showNoBetTime)
        .set('liveStart', action.payload.liveStart)
        .set('roundEnd', action.payload.isNoBetTime ? currRoundEnd : action.payload.roundEnd)
        .set('roundId', round.roundId)
        .set('liveId', roundResults.roundId)
        .set('winCap', action.payload.winCap)
        .set('roundLoading', false)
        .set('repeatRoundCall', 0)
        .set('selectedSubselection', 0)
        .set('liveMatchDuration', roundResults.liveRoundDuration)
        .set('initialBetsLoaded', true);
    }
    /**
     * [Received Data]
     * This action is use to incrise number of round calls in one iteration , maximum is 5
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~REPEAT_CALL_SUCCESS
     */
    case REPEAT_CALL_SUCCESS:
      return state
        .set('showNoBetTime', action.payload.showNoBetTime)
        .set('repeatRoundCall', action.payload.repeatRoundCall);
    /**
     * [Received Data]
     * This action is use to set Turbo round data
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~GET_TURBO_ROUND_SUCCESS
     */
    case GET_TURBO_ROUND_SUCCESS: {
      const leagues = state.get('leagues').toJS();
      const currClassicRoundEnd = state.get('roundEnd');
      leagues.map((l) => {
        l.enabled = 0;
      });
      let selectedLeagueIndex = state.get('selectedLeagueIndex');
      const currSelectedLeague = state.get('selectedLeague').toJS();
      const round = action.payload.round;

      // parse matches data
      const odds4D = parseOdds(round.odds, round.selections);
      const matches = {};
      const odds = {};
      let league;

      // set league week and season for next round and current live
      // eslint-disable-next-line sonarjs/no-identical-functions
      round.competitions.forEach((id, i) => {
        league = leagues.find((l) => l.id === id);
        if (league) {
          league.season = round.seasons[i];
          league.week = round.weeks[i];
          league.enabled = 1;
          odds[id] = odds4D[i];
          matches[id] = parseMatches(round.matches[i]);
        }
      });

      let selectedLeague = leagues.find((l) => (
        l.id === currSelectedLeague.id && l.enabled === 1));

      if (!selectedLeague) {
        selectedLeagueIndex = leagues.findIndex((l) => l.enabled === 1 && l.liveEnabled === 1);
        selectedLeague = leagues[selectedLeagueIndex];
      }

      const currentMarkets = state.get('markets').toJS();
      const { markets, activeMarkets, selectedMarket } = setActiveMarkets(
        currentMarkets, odds[selectedLeague.id][0],
      );

      return state
        .set('markets', fromJS(markets))
        .set('activeMarkets', fromJS(activeMarkets))
        .set('selectedMarket', fromJS(selectedMarket))
        .set('matches', fromJS(matches))
        .set('leagues', fromJS(leagues))
        .set('selectedLeague', fromJS(selectedLeague))
        .set('selectedLeagueIndex', selectedLeagueIndex)
        .set('classicRoundEnd', currClassicRoundEnd)
        .set('odds', fromJS(odds))
        .set('roundId', round.roundId)
        .set('roundEnd', action.payload.roundEnd)
        .set('showKickOff', !!action.payload.roundEnd)
        .set('roundLoading', false)
        .set('selectedSubselection', 0)
        .set('winCap', action.payload.winCap)
        .set('initialTurboBetsLoaded', true);
    }
    case SET_USER_DATA:
      return state
        .set('initialTurboBetsLoaded', false)
        .set('initialBetsLoaded', false);
    /**
     * [Received Data]
     * This action is use to set Turbo countdown
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~SET_END_DATE
     */
    case SET_END_DATE:
      return state
        .set('roundEnd', action.payload.roundEnd)
        .set('showKickOff', action.payload.showKickOff);
    /**
     * [Received Data]
     * This action is use to set Turbo live data
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~KICK_OFF_SUCCESS
     */
    case KICK_OFF_SUCCESS: {
      const leagues = state.get('leagues').toJS();
      leagues.map((l) => {
        l.liveEnabled = 0;
      });

      const roundResults = action.payload.roundResults;

      const halftimeResults = {};
      const results = {};
      let league;

      // parse live data
      // eslint-disable-next-line sonarjs/no-identical-functions
      roundResults.competitions.forEach((id, i) => {
        league = leagues.find((l) => l.id === id);
        if (league) {
          league.liveSeason = roundResults.seasons[i];
          league.liveWeek = roundResults.weeks[i];
          league.liveEnabled = 1;
          halftimeResults[id] = parseTurboTimeline(roundResults.halftimeResults[i]);
          results[id] = parseTurboTimeline(roundResults.results[i]);
          league.liveMatches = parseMatches(roundResults.matches[i]);
        }
      });
      const liveMatchDuration = parseInt((new Date(roundResults.liveEndDate).getTime()
        - new Date(roundResults.liveStartDate).getTime()) / 1000);
      return state
        .set('haveLiveMatches', action.payload.haveLiveMatches)
        .set('leagues', fromJS(leagues))
        .set('halftimeResults', fromJS(halftimeResults))
        .set('results', fromJS(results))
        .set('liveMatchDuration', liveMatchDuration)
        // .set('showNoBetTime', showNoBetTime)
        .set('liveStart', action.payload.liveStart)
        .set('liveId', roundResults.roundId);
    }
    /**
     * [Received Data]
     * This action is use to set initialBetsLoaded and call round API again after login
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~GO_TO_LOGIN
     */
    case GO_TO_LOGIN:
      return state
        .set('initialBetsLoaded', action.payload.loadRound
          ? false : state.get('initialBetsLoaded'))
        .set('initialTurboBetsLoaded', action.payload.loadRound
          ? false : state.get('initialTurboBetsLoaded'))
        .set('gameType', null);
    /**
     * [Received Data]
     * This action is use to set initialBetsLoaded
     *
     * @memberof reducer/betsReducer
     * @function reducer/betsReducer~GO_TO_LOGIN
     */
    case SET_INITIALISE_BET:
      return state
        .set('initialBetsLoaded', action.payload.initialBetsLoaded);
    case BET_RE_COUNT:
      return state;
    /**
     * [Action Creator]
     * This action is use to change page
     *
     * @memberof reducer/betsReducer
     * @example changePage(payload)
     * @function reducer/betsReducer~CHANGE_PAGE
     * @param {string} page
     */
    case CHANGE_PAGE: {
      return state
        .set('selectedMarket', fromJS(parseSelectedMarket(state.get('markets').toJS()[0])))
        .set('selectedSubselection', 0);
    }
    /**
     * [Action Creator]
     * This action is use to show hide kick off butt
     *
     * @memberof reducer/betsReducer
     * @example changePage(payload)
     * @function reducer/betsReducer~SHOW_KICK_OFF
     * @param {string} page
     */
    case SHOW_KICK_OFF: {
      return state
        .set('showKickOff', action.payload);
    }
    /**
     * [Action Creator]
     * This action is used store turbo round id to avoid mixing them with "normal round id"
     *
     * @memberof reducer/betsReducer
     * @example changePage(payload)
     * @function reducer/betsReducer~SET_TURBO_ROUND_ID
     */
    case SET_TURBO_ROUND_ID: {
      return state
        .set('turboRoundId', action.payload.roundId);
    }
    /**
     * [Action Creator]
     * This action is used store statistics
     *
     * @memberof reducer/betsReducer
     * @example changePage(payload)
     * @function reducer/betsReducer~SET_STATISTICS
     */
    case SET_STATISTICS: {
      const clearOther = action.payload.clearOther;
      let newStats = clearOther ? {} : state.get('statistics').toJS();
      if (!newStats[action.payload.roundId]) {
        newStats = {};
        newStats[action.payload.roundId] = {};
      }
      newStats[action.payload.roundId][action.payload.leagueId] = action.payload.statistics;
      return state
        .set('statistics', fromJS(newStats));
    }
    /**
     * [Action Creator]
     * This action is used store statistics
     *
     * @memberof reducer/betsReducer
     * @example changePage(payload)
     * @function reducer/betsReducer~SET_STATISTICS
     */
    case CLEAR_STATISTICS: {
      return state.set('statistics', fromJS({}));
    }
    default:
      return state;
  }
}
export default betsReducer;