const fixFloat = (number, x = 2) => {
  return parseFloat(number.toFixed(x));
}

const HandicapCalc = function (data) {
  const { i, g, a, b, c, w, l } = data;
  const t = w + l;

  const calcTemplate = (handlers) => {
    if (i > 2 || i < 0) {
      return {};
    }
    if (i === 2) {
      const z = g;
      const x = (b + 1) * (t + z) / (a * b - 1);
      const y = (a + 1) * (t + z) / (a * b - 1);
      return { x, y, z };
    };
    return handlers[i]?.() ?? {};
  };

  return {
    la_wh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (t + x) / (2 * c + 1);
          const y = (c + 1) * (t + x) / (c + 0.5) / b;
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((a + 1) * t + (1 - a * b / 2) * y) / (a * c - 1);
          const x = ((c + 1) * t + (c - b / 2) * y) / (a * c - 1);
          return { x, y, z };
        }
      ]);
    },
    la_dr_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (t + x) / c;
          const y = (t + x + z) / b;
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((a + 1) * t + y) / (a * c - 1);
          const x = ((c + 1) * t + c * y) / (a * c - 1);
          return { x, y, z };
        }
      ]);
    },
    la_lh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (2 * b + 1) * (t + x) / (2 * b * c - 1);
          const y = (c + 1) * (t + x) / (b * c - 0.5);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((a + 1) * t + (a / 2 + 1) * y) / (a * c - 1);
          const x = ((c + 1) * t + (c + 0.5) * y) / (a * c - 1);
          return { x, y, z };
        }
      ]);
    },
    lh_dr_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (t + x / 2) / c;
          const y = (t + x + z) / b;
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((2 * a + 1) * t + y) / (2 * a * c - 1);
          const x = ((c + 1) * t + c * y) / (a * c - 0.5);
          return { x, y, z };
        }
      ]);
    },
    lh_lh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = ((2 * b + 1) * t + (b + 1) * x) / (2 * b * c - 1);
          const y = ((c + 1) * t + (c + 0.5) * x) / (b * c - 0.5);
        },
        () => {
          const y = g;
          const z = ((2 * a + 1) * t + (a + 1) * y) / (2 * a * c - 1);
          const x = ((c + 1) * t + (c + 0.5) * y) / (a * c - 0.5);
          return { x, y, z };
        }
      ]);
    },
    la_la_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (b + 1) * (t + x) / (b * c - 1);
          const y = (c + 1) * (t + x) / (b * c - 1);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = (a + 1) * (t + y) / (a * c - 1);
          const x = (c + 1) * (t + y) / (a * c - 1);
          return { x, y, z };
        }
      ]);
    }
  }
}

const calcExternalHandicap = (data) => {
  const { gold_side_jc: g, odds_side_a: a, odds_side_b: b, odds_side_m: c, jc_index: i, cross_type: t, win_target: w, pre_loss } = data;
  const l = pre_loss ?? 0;
  const calc = new HandicapCalc({ i, g, a, b, c, w, l });
  const { x, y, z } = calc?.[t]() ?? {};
  return {
    gold_side_a: fixFloat(x),
    gold_side_b: fixFloat(y),
    gold_side_m: fixFloat(z),
    jc_index: i,
  }

}

const calcGoldsWithWinTarget = (data) => {
  const { gold_side_jc, win_target, sol1, sol2 } = data;
  const preInfo = calcExternalHandicap({ ...sol1, gold_side_jc, win_target });
  const { gold_side_a: goldA1, gold_side_b: goldB1, gold_side_m: goldM1, jc_index: jc_index_1 } = preInfo;
  let pre_loss = 0;
  switch (jc_index_1) {
    case 0:
      pre_loss = goldB1 + goldM1;
      break;
    case 1:
      pre_loss = goldA1 + goldM1;
      break;
    case 2:
      pre_loss = goldA1 + goldB1;
      break;
  }
  const nextInfo = calcExternalHandicap({ ...sol2, gold_side_jc, win_target, pre_loss });
  const { gold_side_a: goldA2, gold_side_b: goldB2, gold_side_m: goldM2, jc_index: jc_index_2 } = nextInfo;
  const jcWin = fixFloat(10000 * (sol1.odds_side_a + 1) * (sol2.odds_side_a + 1) - goldB1 - goldM1 - goldB2 - goldM2 - 10000);
  return {
    jcWin,
    goldA1,
    goldB1,
    goldM1,
    goldA2,
    goldB2,
    goldM2,
    jc_index_1,
    jc_index_2,
  }
}

export const calcTotalProfit = (sol1, sol2, gold_side_jc) => {

  const winTarget1 = sol1.win_average;
  const winTarget2 = sol2.win_average;
  const winTarget = fixFloat(Math.min(winTarget1, winTarget2), 2);

  const jcWin1 = calcGoldsWithWinTarget({ gold_side_jc, win_target: winTarget1, sol1, sol2 })?.jcWin;
  const jcWin2 = calcGoldsWithWinTarget({ gold_side_jc, win_target: winTarget2, sol1, sol2 })?.jcWin;
  const jcWin = fixFloat(Math.max(jcWin1, jcWin2), 2);

  const start = Math.max(winTarget, jcWin);
  const end = Math.min(winTarget, jcWin);
  const result = [];

  for (let i = start; i > end; i--) {
    const win_target = i;
    const goldsInfo = calcGoldsWithWinTarget({ gold_side_jc, win_target, sol1, sol2 });
    const win_diff = Math.abs(fixFloat(win_target - goldsInfo.jcWin));
    const lastResult = result.at(-1);
    if (!lastResult?.win_diff || win_diff < lastResult.win_diff) {
      result.push({ win_target: fixFloat(win_target), win_diff, ...goldsInfo });
    }
    else {
      break;
    }
  }
  return result.at(-1);
}