/* eslint-disable no-restricted-globals */
/* eslint-disable array-callback-return */
/* eslint-disable no-plusplus */
/* eslint-disable no-shadow */
const getOutcomeRow = (row, addNotes, applyEachWay, applyRuleFour) => {
  const outcome = row.children[addNotes ? 0 : 1].children[0].children[0].value;
  const oddsValue = row.children[addNotes ? 1 : 2].children[0].value;

  let placeOdds = null;
  let ruleFour = null;
  if (row.children.length > 2) {
    if (applyEachWay === 'Yes') {
      placeOdds = row.children[addNotes ? 2 : 3].children[0].children[0].value;
    }

    if (applyRuleFour && applyEachWay === 'Yes') {
      ruleFour =
        row.children.length === 5 && row.children[addNotes ? 3 : 4].children[0].children[0].value;
    } else if (applyRuleFour && applyEachWay === 'No') {
      ruleFour =
        row.children.length === 4 && row.children[addNotes ? 2 : 3].children[0].children[0].value;
    }
  }
  return [outcome, oddsValue, placeOdds, ruleFour];
};

const getDeadHeatRow = (row) => {
  const tiedPosition = row.children[1]?.children[0]?.children[0]?.children[1]?.children[0]?.value;
  const deadHeatsrunners =
    row.children[1]?.children[0]?.children[1]?.children[1]?.children[0]?.value;
  const placesOffered = row.children[1]?.children[0]?.children[2]?.children[1]?.children[0]?.value;

  return [tiedPosition, deadHeatsrunners, placesOffered];
};

const calculateSSA = (result, stake, betType) => {
  let final = 0;
  if (result[0] > 0) {
    let firstBet = result[0] * stake;
    let secondPartStake = 0;

    if (betType === 'Double stakes about') {
      secondPartStake = firstBet < stake * 2 ? firstBet : stake * 2;
    } else {
      secondPartStake = firstBet < stake ? firstBet : stake;
    }

    if (result[1] > 0) {
      firstBet += result[1] * secondPartStake;
      firstBet -= secondPartStake;
    } else {
      firstBet -= secondPartStake;
    }
    final += firstBet;
  }

  if (result[1] > 0) {
    let secondBet = result[1] * stake;
    let secondPartStake = 0;
    if (betType === 'Double stakes about') {
      secondPartStake = secondBet < stake * 2 ? secondBet : stake * 2;
    } else {
      secondPartStake = secondBet < stake ? secondBet : stake;
    }
    if (result[0] > 0) {
      secondBet += result[0] * secondPartStake;
      secondBet -= secondPartStake;
    } else {
      secondBet -= secondPartStake;
    }
    final += secondBet;
  }
  return [final, 2];
};

const calculateCombination = (
  combination,
  stake,
  isUpAndDown,
  applyEachWay,
  multipleOptions,
  betType
) => {
  const baseOdds = combination.map((odds) => odds[0]);

  if (applyEachWay === 'Yes') {
    const eachWayOdds = combination.map((odds) => odds[1]);

    if (multipleOptions === '1') {
      const baseOddsResult = baseOdds.reduce((a, b) => a * b);

      const baseOddsTotal = baseOddsResult * stake;

      const eachWayOddsResult = eachWayOdds.reduce((a, b) => a * b);
      const eachWayOddsTotal = eachWayOddsResult * stake;

      return [baseOddsTotal + eachWayOddsTotal, 2];
    }
    let currentStake = stake;
    let betResult = 0;
    combination.forEach((oddsPair) => {
      const mainBet = oddsPair[0] * currentStake;
      const placeBet = oddsPair[1] * currentStake;
      betResult = mainBet + placeBet;
      currentStake = betResult / 2;
    });
    return [betResult, 2];
  }

  if (isUpAndDown) {
    const [baseOddsSSATotal, baseOddsSSANumberOfBets] = calculateSSA(baseOdds, stake, betType);
    return [baseOddsSSATotal, baseOddsSSANumberOfBets];
  }

  let baseOddsResult = baseOdds.reduce((a, b) => a * b);
  baseOddsResult *= stake;
  return [baseOddsResult, 1];
};

function makeCombination(
  input,
  comboLength,
  start,
  stake,
  length,
  betType,
  applyEachWay,
  multipleOptions,
  isUpAndDown = false
) {
  const result = [];
  const finalResult = [0, 0];

  result.length = length;

  function combine(input, comboLength, start, stake, isUpAndDown = false) {
    if (comboLength === 0) {
      const combinationResult = calculateCombination(
        result,
        stake,
        isUpAndDown,
        applyEachWay,
        multipleOptions,
        betType
      );
      finalResult[0] += combinationResult[0];
      finalResult[1] += combinationResult[1];
      return;
    }
    for (let i = start; i <= input.length - comboLength; i++) {
      result[result.length - comboLength] = input[i];
      combine(input, comboLength - 1, i + 1, stake, isUpAndDown);
    }
  }

  combine(input, comboLength, start, stake, isUpAndDown);

  return finalResult;
}

const getTheOutputResult = (stake, stakeType, total, numberOfBets) => {
  if (stake <= 0) {
    return ['-', '-', '-'];
  }

  if (stakeType === '2') {
    stake /= numberOfBets;
    total /= numberOfBets;
  }

  if (stake < 0.01) {
    return ['-', `min stake is ${0.01 * numberOfBets}(${numberOfBets} bets)`, '-'];
  }

  return [
    total.toFixed(2),
    `${numberOfBets * stake} (${numberOfBets} bets of ${stake.toFixed(2)})`,
    (total - numberOfBets * stake).toFixed(2),
  ];
};

const doTheCalculations = (
  betType,
  typesOfBetType,
  applyEachWay,
  multipleOptions,
  selectionsOdds,
  stake
) => {
  let total = 0;
  let numberOfBets = 0;
  let combinationLength = 0;
  typesOfBetType.map((type) => {
    if (type === 0) {
      // it means that the betType is Accumulator
      combinationLength = selectionsOdds.length;
      type = combinationLength;
    }
    if (type === 'SSA' || type === 'DSA') {
      combinationLength = 2;
      type = combinationLength;
      const [combineTotal, combineNumberOfBets] = makeCombination(
        selectionsOdds,
        type,
        0,
        stake,
        combinationLength,
        betType,
        applyEachWay,
        multipleOptions,
        true
      );
      total += combineTotal;
      numberOfBets += combineNumberOfBets;
      return;
    }
    combinationLength = type;
    const [combineTotal, combineNumberOfBets] = makeCombination(
      selectionsOdds,
      type,
      0,
      stake,
      combinationLength,
      betType,
      applyEachWay,
      multipleOptions
    );
    total += combineTotal;
    numberOfBets += combineNumberOfBets;
  });
  return [total, numberOfBets];
};

const getSelectionOddsAndOutcomes = (rows, deadHeatRows, applyRuleFour, applyEachWay, addNotes) => {
  const selectionsOdds = [];
  const outcomes = [];
  rows.map((row, index) => {
    if (row !== null) {
      selectionsOdds[index] = [];

      const [outcome, oddsValue, placeOdds, ruleFour] = getOutcomeRow(
        row,
        addNotes,
        applyEachWay,
        applyRuleFour
      );
      let odds = oddsValue;
      outcomes.push(outcome);

      const placeOddsVal = placeOdds && placeOdds.split('/');
      // let odds = Number(convertToDecimal(row));

      const eachWayOdds = (placeOddsValue) => {
        let newOdds = odds;
        newOdds -= 1;
        newOdds *= placeOddsValue[0];
        newOdds /= placeOddsValue[1];
        newOdds += 1;
        return newOdds;
      };

      if (applyRuleFour) {
        const deductionAmount = ruleFour;
        odds--;
        odds *= 1 - deductionAmount / 100;
        odds++;
      }

      switch (outcome) {
        case 'winner':
          selectionsOdds[index][0] = odds;
          if (applyEachWay === 'Yes') {
            selectionsOdds[index][1] = eachWayOdds(placeOddsVal);
          }
          break;
        case 'placed':
          selectionsOdds[index][0] = 0;
          if (applyEachWay === 'Yes') {
            selectionsOdds[index][1] = eachWayOdds(placeOddsVal);
          }
          break;
        case 'lost':
          selectionsOdds[index][0] = 0;
          if (applyEachWay === 'Yes') {
            selectionsOdds[index][1] = 0;
          }
          break;
        case 'void':
          selectionsOdds[index][0] = 1;
          if (applyEachWay === 'Yes') {
            selectionsOdds[index][1] = 1;
          }
          break;
        case 'dead_heat':
          if (deadHeatRows[index].children) {
            // eslint-disable-next-line no-case-declarations
            const [tiedPosition, deadHeatValue, placesOffered] = getDeadHeatRow(
              deadHeatRows[index]
            );

            if (applyEachWay === 'Yes' && tiedPosition !== '1') {
              selectionsOdds[index][0] = 0;
            } else {
              selectionsOdds[index][0] = odds / deadHeatValue;
            }

            if (applyEachWay === 'Yes') {
              const ewOdds = eachWayOdds(placeOddsVal);
              const freePlaces = placesOffered - tiedPosition + 1;
              const filledPlaces = tiedPosition + deadHeatValue - 1;
              if (placesOffered < tiedPosition) {
                selectionsOdds[index][1] = 0;
              } else if (filledPlaces > placesOffered) {
                selectionsOdds[index][1] = (ewOdds * freePlaces) / deadHeatValue; // ewOdds / difference
              } else {
                selectionsOdds[index][1] = ewOdds;
              }
            }
          }
          break;
        default:
          break;
      }
    }
  });
  return [selectionsOdds, outcomes];
};

const calculateForAlphabet = (stake, selectionsOdds, betType, applyEachWay, multipleOptions) => {
  let total = 0;
  let totalNumberOfBets = 0;

  const firstPatentOdds = selectionsOdds.slice(0, 3);
  const secondPatentOdds = selectionsOdds.slice(3, 6);
  const yankeeOdds = selectionsOdds.slice(1, 5);

  const [firstPatentTotal, firstPatentBetCount] = doTheCalculations(
    'Alphabet',
    betType.find(({ name }) => name === 'Patent').types,
    applyEachWay,
    multipleOptions,
    firstPatentOdds,
    stake
  );

  const [secondPatentTotal, secondPatentBetCount] = doTheCalculations(
    'Alphabet',
    betType.find(({ name }) => name === 'Patent').types,
    applyEachWay,
    multipleOptions,
    secondPatentOdds,
    stake
  );

  const [yankeeTotal, yankeeBetCount] = doTheCalculations(
    'Alphabet',
    betType.find(({ name }) => name === 'Yankee').types,
    applyEachWay,
    multipleOptions,
    yankeeOdds,
    stake
  );

  const [accumulatorTotal, accumulatorBetCount] = doTheCalculations(
    'Alphabet',
    betType.find(({ name }) => name === 'Accumulator').types,
    applyEachWay,
    multipleOptions,
    selectionsOdds,
    stake
  );

  total = firstPatentTotal + secondPatentTotal + yankeeTotal + accumulatorTotal;
  totalNumberOfBets =
    firstPatentBetCount + secondPatentBetCount + yankeeBetCount + accumulatorBetCount;

  return [total, totalNumberOfBets];
};

const generateUnionJackGrid = (selectionsOdds) => {
  const unionJackOdds = [];

  unionJackOdds[0] = selectionsOdds.slice(0, 3);
  unionJackOdds[1] = selectionsOdds.slice(3, 6);
  unionJackOdds[2] = selectionsOdds.slice(6, 9);
  unionJackOdds[3] = [];
  unionJackOdds[4] = [];
  unionJackOdds[5] = [];
  for (let i = 3; i < 6; i++) {
    const j = i - 3;
    unionJackOdds[i].push(selectionsOdds[j]);
    unionJackOdds[i].push(selectionsOdds[j + 3]);
    unionJackOdds[i].push(selectionsOdds[j + 6]);
  }
  unionJackOdds[6] = [];
  unionJackOdds[6].push(selectionsOdds[0]);
  unionJackOdds[6].push(selectionsOdds[4]);
  unionJackOdds[6].push(selectionsOdds[8]);

  unionJackOdds[7] = [];
  unionJackOdds[7].push(selectionsOdds[2]);
  unionJackOdds[7].push(selectionsOdds[4]);
  unionJackOdds[7].push(selectionsOdds[6]);

  return unionJackOdds;
};

const calculateForUnionJack = (stake, selectionsOdds, betType, applyEachWay, multipleOptions) => {
  const unionJackOdds = generateUnionJackGrid(selectionsOdds);

  let total = 0;
  let totalNumberOfBets = 0;

  unionJackOdds.map((odds) => {
    const [returnAmount, numberOfBets] = doTheCalculations(
      betType.name,
      betType.types,
      applyEachWay,
      multipleOptions,
      odds,
      stake
    );
    total += returnAmount;
    totalNumberOfBets += numberOfBets;
  });

  return [total, totalNumberOfBets];
};

const calculateFoldOptions = (
  selectionsOdds,
  curretFoldRows,
  betType,
  applyEachWay,
  multipleOptions,
  stakeType
) => {
  const foldOutput = [];

  let finalReturn = 0;
  let finalOutlay = 0;
  let finalProfit = 0;
  curretFoldRows.map((foldRow, index) => {
    const typesOfBet = [index + 1];
    const stake = Number(foldRow.children[1].children[0].value);

    let returnAndBetCount = [];

    returnAndBetCount = doTheCalculations(
      betType,
      typesOfBet,
      applyEachWay,
      multipleOptions,
      selectionsOdds,
      stake
    );

    const [totalReturnOutput, totalOutlayOutput, profitOutput] = getTheOutputResult(
      stake,
      stakeType,
      returnAndBetCount[0],
      returnAndBetCount[1]
    );

    if (!isNaN(Number(totalReturnOutput)) && !isNaN(Number(profitOutput))) {
      finalReturn += Number(totalReturnOutput);
      finalProfit += Number(profitOutput);
      finalOutlay += Number(totalOutlayOutput.split(' ')[0]);
    }

    foldOutput.push([totalOutlayOutput, totalReturnOutput, profitOutput]);
  });

  return [foldOutput, [finalOutlay, finalReturn, finalProfit]];
};

module.exports = {
  getSelectionOddsAndOutcomes,
  doTheCalculations,
  getTheOutputResult,
  calculateForAlphabet,
  calculateForUnionJack,
  calculateFoldOptions,
};
