import Logs from '../libs/logs.js';
import { eventSolutions } from './eventSolutions.js';

/**
 * 精确浮点数字
 * @param {number} number
 * @param {number} x
 * @returns {number}
 */
const fixFloat = (number, x = 2) => {
  return parseFloat(number.toFixed(x));
}

/**
 * 计算输赢比例
 * 与其他方法中的输赢逻辑相反
 * 正数为输
 * 负数为赢
 */
const CROSS_TYPE_MAP = { w: -1, l: 1, a: 1, h: 0.5, d: 0, r: 0, v: 0 };
const lossProportion = (sol) => {
  const {
    cross_type, odds_side_a, odds_side_b,
    rebate_side_a, rebate_side_b,
    rebate_type_side_a, rebate_type_side_b,
  } = sol;
  const typeList = cross_type.split('_').map(part => {
    return part.split('').map(key => CROSS_TYPE_MAP[key]);
  }).map(([a, b])=> a * b);

  let loss_proportion_a = 0, loss_proportion_b = 0;
  if (typeList[0] >= 0) {
    loss_proportion_a = typeList[0] * (1 - rebate_side_a);
  }
  else {
    if (rebate_type_side_a == 1) {
      loss_proportion_a = typeList[0] * (odds_side_a + rebate_side_a);
    }
    else {
      loss_proportion_a = typeList[0] * odds_side_a * (1 + rebate_side_a);
    }
  }

  if (typeList[1] >= 0) {
    loss_proportion_b = typeList[1] * (1 - rebate_side_b);
  }
  else {
    if (rebate_type_side_b == 1) {
      loss_proportion_b = typeList[1] * (odds_side_b + rebate_side_b);
    }
    else {
      loss_proportion_b = typeList[1] * odds_side_b * (1 + rebate_side_b);
    }
  }

  return { loss_proportion_a, loss_proportion_b };
}

/**
 * 不同组合的金额计算
 */
const HandicapCalc = function (data) {
  const { i, g, a, b, c, A, B, C, TA, TB, TC, w, l } = data;
  const t = w + l;
  const k1 = TA == 1 ? a + A : a * (1 + A);
  const k2 = 1 - A;
  const k3 = TB == 1 ? b + B : b * (1 + B);
  const k4 = 1 - B;
  const k5 = TC == 1 ? c + C : c * (1 + C);
  const k6 = 1 - C;

  const calcTemplate = (handlers) => {
    if (i > 2 || i < 0) {
      return {};
    }
    if (i === 2) {
      const z = g;
      const m = t + k6 * z;
      const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
      const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
      return { x, y, z };
    };
    return handlers[i]?.() ?? {};
  };

  return {
    la_wh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const m = t + k2 * x;
          const z = m / (2 * k5 + k6);
          const y = (k5 + k6) * m / (k3 * k5 + k3 * k6 / 2);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((k1 + k2) * t + (k2 * k4 - k1 * k3 / 2) * y) / (k1 * k5 - k2 * k6);
          const x = ((k5 + k6) * t + (k4 * k5 - k3 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
          return { x, y, z };
        }
      ]);
    },
    la_dr_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const m = t + k2 * x;
          const z = m / k5;
          const y = (k5 + k6) * m / (k3 * k5);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((k1 + k2) * t + k2 * k4 * y) / (k1 * k5 - k2 * k6);
          const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6);
          return { x, y, z };
        }
      ]);
    },
    la_lh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const m = t + k2 * x;
          const z = (2 * k3 + k4) * m / (2 * k3 * k5 - k4 * k6);
          const y = (k5 + k6) * m / (k3 * k5 - k4 * k6 / 2);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((k1 + k2) * t + (k2 * k4 + k1 * k4 / 2) * y) / (k1 * k5 - k2 * k6);
          const x = ((k5 + k6) * t + (k4 * k5 + k4 * k6 / 2) * y) / (k1 * k5 - k2 * k6);
          return { x, y, z };
        }
      ]);
    },
    lh_dr_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = (t + k2 * x / 2) / k5;
          const y = ((k5 + k6) * t + (k5 + k6 / 2) * k2 * x) / (k3 * k5);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((2 * k1 + k2) * t + k2 * k4 * y) / (2 * k1 * k5 - k2 * k6);
          const x = ((k5 + k6) * t + k4 * k5 * y) / (k1 * k5 - k2 * k6 / 2);
          return { x, y, z };
        }
      ]);
    },
    lh_lh_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const z = ((2 * k3 + k4) * t + (k3 + k4) * k2 * x) / (2 * k3 * k5 - k4 * k6);
          const y = ((k5 + k6) * t + (k5 + k6 / 2) * k2 * x) / (k3 * k5 - k4 * k6 / 2);
          return { x, y, z };
        },
        () => {
          const y = g;
          const z = ((2 * k1 + k2) * t + (k1 + k2) * k4 * y) / (2 * k1 * k5 - k2 * k6);
          const x = ((k5 + k6) * t + (k5 + k6 / 2) * k4 * y) / (k1 * k5 - k2 * k6 / 2);
          return { x, y, z };
        }
      ]);
    },
    la_la_wa() {
      return calcTemplate([
        () => {
          const x = g;
          const m = t + k2 * x;
          const z = (k3 + k4) * m / (k3 * k5 - k4 * k6);
          const y = (k5 + k6) * m / (k3 * k5 - k4 * k6);
          return { x, y, z };
        },
        () => {
          const y = g;
          const m = t + k4 * y;
          const z = (k1 + k2) * m / (k1 * k5 - k2 * k6);
          const x = (k5 + k6) * m / (k1 * k5 - k2 * k6);
          return { x, y, z };
        }
      ]);
    },
    la_wa_rv() {
      return calcTemplate([
        () => {
          const x = g;
          const y = (k2 * x + t) / k3;
          const z = 0;
          return { x, y, z };
        },
        () => {
          const y = g;
          const x = (k4 * y + t) / k1;
          const z = 0;
          return { x, y, z };
        }
      ]);
    }
  }
}

/**
 * const z = g;
      const m = t + k6 * z;
      const x = (k3 + k4) * m / (k1 * k3 - k2 * k4);
      const y = (k1 + k2) * m / (k1 * k3 - k2 * k4);
      return { x, y, z };
 */

/**
 * 根据预期盈利计算下注金额
 */
const calcGoldsWithTarget = (data) => {
  const {
    inner_base: g,
    odds_side_a: a,
    odds_side_b: b,
    odds_side_c: c,
    rebate_side_a: A,
    rebate_side_b: B,
    rebate_side_c: C,
    rebate_type_side_a: TA,
    rebate_type_side_b: TB,
    rebate_type_side_c: TC,
    inner_index: i,
    cross_type: t,
    win_target: w,
    loss_out: l = 0,
  } = data;
  const calc = new HandicapCalc({ i, g, a, b, c, A, B, C, TA, TB, TC, w, l });
  const { x, y, z } = calc?.[t]() ?? {};
  return {
    gold_side_a: fixFloat(x),
    gold_side_b: fixFloat(y),
    gold_side_c: fixFloat(z),
  }
}

/**
 * 根据预期盈利计算金额和内盘盈亏
 */
const calcWinResultWithTarget = (data) => {
  const { inner_base, inner_rebate, win_target, sol1, sol2 } = data;
  const {
    cross_type: crossType1,
    odds_side_a: oddsA1,
    odds_side_b: oddsB1,
    odds_side_c: oddsC1,
    rebate_side_a: rebateA1,
    rebate_side_b: rebateB1,
    rebate_side_c: rebateC1,
    rebate_type_side_a: rebateTypeA1,
    rebate_type_side_b: rebateTypeB1,
    rebate_type_side_c: rebateTypeC1,
    inner_index: inner_index_1,
  } = sol1;
  const {
    gold_side_a: goldA1,
    gold_side_b: goldB1,
    gold_side_c: goldC1,
  } = calcGoldsWithTarget({ ...sol1, inner_base, win_target });

  let loss_out_1 = 0, win_inner_1 = 0;
  switch (inner_index_1) {
    case 0:
      loss_out_1 = goldB1 * (1 - rebateB1) + goldC1 * (1 - rebateC1);
      win_inner_1 = inner_base * (oddsA1 + 1);
      break;
    case 1:
      loss_out_1 = goldA1 * (1 - rebateA1) + goldC1 * (1 - rebateC1);
      win_inner_1 = inner_base * (oddsB1 + 1);
      break;
    case 2:
      const { loss_proportion_a: lpA1, loss_proportion_b: lpB1 } = lossProportion(sol1);
      loss_out_1 = goldA1 * lpA1 + goldB1 * lpB1;
      win_inner_1 = inner_base * (oddsC1 + 1)
      break;
  }

  win_inner_1 = fixFloat(win_inner_1);

  const {
    cross_type: crossType2,
    odds_side_a: oddsA2,
    odds_side_b: oddsB2,
    odds_side_c: oddsC2,
    rebate_side_a: rebateA2,
    rebate_side_b: rebateB2,
    rebate_side_c: rebateC2,
    rebate_type_side_a: rebateTypeA2,
    rebate_type_side_b: rebateTypeB2,
    rebate_type_side_c: rebateTypeC2,
    inner_index: inner_index_2,
  } = sol2;
  const {
    gold_side_a: goldA2,
    gold_side_b: goldB2,
    gold_side_c: goldC2,
  } = calcGoldsWithTarget({ ...sol2, inner_base, win_target, loss_out: loss_out_1 });

  let loss_out_2 = 0, win_inner_2 = 0, inner_base_key;
  switch (inner_index_2) {
    case 0:
      inner_base_key = 'goldA2';
      loss_out_2 = inner_base + goldB2 * (1 - rebateB2) + goldC2 * (1 - rebateC2) + loss_out_1;
      win_inner_2 = win_inner_1 * (oddsA2 + 1);
      break;
    case 1:
      inner_base_key = 'goldB2';
      loss_out_2 = inner_base + goldA2 * (1 - rebateA2) + goldC2 * (1 - rebateC2) + loss_out_1;
      win_inner_2 = win_inner_1 * (oddsB2 + 1);
      break;
    case 2:
      const { loss_proportion_a: lpA2, loss_proportion_b: lpB2  } = lossProportion(sol2);
      inner_base_key = 'goldC2';
      loss_out_2 = inner_base + goldA2 * lpA2 + goldB2 * lpB2 + loss_out_1;
      win_inner_2 = win_inner_1 * (oddsC2 + 1);
      break;
  }

  const win_inner = fixFloat(win_inner_2 - loss_out_2);

  const goldsInfo = { goldA1, goldB1, goldC1, goldA2, goldB2, goldC2 }

  if (goldsInfo[inner_base_key]) {
    goldsInfo[inner_base_key] = win_inner_1;
  }

  const result = {
    bet_info: [
      {
        cross_type: crossType1,
        gold_side_a: goldsInfo.goldA1,
        gold_side_b: goldsInfo.goldB1,
        gold_side_c: goldsInfo.goldC1,
        odds_side_a: oddsA1,
        odds_side_b: oddsB1,
        odds_side_c: oddsC1,
        rebate_side_a: rebateA1,
        rebate_side_b: rebateB1,
        rebate_side_c: rebateC1,
        rebate_type_side_a: rebateTypeA1,
        rebate_type_side_b: rebateTypeB1,
        rebate_type_side_c: rebateTypeC1,
        inner_index: inner_index_1,
      },
      {
        cross_type: crossType2,
        gold_side_a: goldsInfo.goldA2,
        gold_side_b: goldsInfo.goldB2,
        gold_side_c: goldsInfo.goldC2,
        odds_side_a: oddsA2,
        odds_side_b: oddsB2,
        odds_side_c: oddsC2,
        rebate_side_a: rebateA2,
        rebate_side_b: rebateB2,
        rebate_side_c: rebateC2,
        rebate_type_side_a: rebateTypeA2,
        rebate_type_side_b: rebateTypeB2,
        rebate_type_side_c: rebateTypeC2,
        inner_index: inner_index_2,
      }
    ],
    win_inner,
    inner_base,
    inner_rebate,
  }

  return result;
}

/**
 * 根据单关盈亏计算综合利润
 */
const calcTotalProfit = (sol1, sol2, inner_base, inner_rebate) => {
  const rebateInner = inner_base * inner_rebate;
  const winTarget1 = fixFloat(sol1.win_average - rebateInner);
  const winTarget2 = fixFloat(sol2.win_average - rebateInner);
  const winTarget = fixFloat(Math.min(winTarget1, winTarget2));

  const win1 = calcWinResultWithTarget({ inner_base, inner_rebate, win_target: winTarget1, sol1, sol2 })?.win_inner;
  const win2 = calcWinResultWithTarget({ inner_base, inner_rebate, win_target: winTarget2, sol1, sol2 })?.win_inner;

  const win_inner = fixFloat(Math.max(win1, win2), 2);

  const start = Math.max(winTarget, win_inner);
  const end = Math.min(winTarget, win_inner);

  const result = [];

  for (let i = start; i > end; i--) {
    const win_target = i;
    const goldsInfo = calcWinResultWithTarget({ inner_base, inner_rebate, win_target, sol1, sol2 });
    const { win_inner, ...goldsRest } = goldsInfo;
    const win_diff = Math.abs(fixFloat(win_target - goldsInfo.win_inner));
    const lastResult = result.at(-1);
    if (!lastResult?.win_diff || win_diff < lastResult.win_diff) {
      result.push({ win_target: fixFloat(win_target + rebateInner), win_diff, win_inner: fixFloat(win_inner + rebateInner), ...goldsRest });
    }
    else {
      break;
    }
  }

  return result.at(-1);
}




/**
 * ----------------------------
 * 计算第一关锁定之后的新利润
 * 第一关过关后，重新计算第二关金额
 * 结合第一关亏损计算第二关新利润
 * ----------------------------
 */

/**
 * 计算第二关利润
 */
const calcSecondProfit = (betInfo) => {
  const {
    inner_index, inner_odds_first,
    odds_side_a: a, odds_side_b: b, odds_side_c: c,
  } = betInfo;

  let odds_side_a, odds_side_b, odds_side_c;
  switch (inner_index) {
    case 0:
      odds_side_a = fixFloat((a+1) * (inner_odds_first+1) - 1);
      odds_side_b = b;
      odds_side_c = c;
      break;
    case 1:
      odds_side_a = a;
      odds_side_b = fixFloat((b+1) * (inner_odds_first+1) - 1);
      odds_side_c = c;
      break;
    case 2:
      odds_side_a = a;
      odds_side_b = b;
      odds_side_c = fixFloat((c+1) * (inner_odds_first+1) - 1);
      break;
  }

  return eventSolutions({ ...betInfo, odds_side_a, odds_side_b, odds_side_c }, true);
}

/**
 * 获取第一关内盘信息及外盘损失
 */
const getFirstInfo = (betInfo) => {
  const {
    inner_index,
    gold_side_a, gold_side_b, gold_side_c,
    odds_side_a, odds_side_b, odds_side_c,
    rebate_side_a, rebate_side_b, rebate_side_c,
  } = betInfo;

  let loss_out = 0, inner_ref_value = 0, inner_odds = 0;
  switch (inner_index) {
    case 0:
      loss_out = gold_side_b * (1 - rebate_side_b) + gold_side_c * (1 - rebate_side_c);
      inner_ref_value = gold_side_a;
      inner_odds = odds_side_a;
      break;
    case 1:
      loss_out = gold_side_a * (1 - rebate_side_a) + gold_side_c * (1 - rebate_side_c);
      inner_ref_value = gold_side_b;
      inner_odds = odds_side_b;
      break;
    case 2:
      const { loss_proportion_a, loss_proportion_b } = lossProportion(betInfo);
      loss_out = gold_side_a * loss_proportion_a + gold_side_b * loss_proportion_b;
      inner_ref_value = gold_side_c;
      inner_odds = odds_side_c;
      break;
  }
  return { loss_out, inner_ref_value, inner_odds };
}

/**
 * 结合第一关亏损计算第二关新利润
 */
const calcTotalProfitWithFixedFirst = (betInfo1, betInfo2, inner_base, inner_rebate) => {

  const { loss_out, inner_ref_value, inner_odds } = getFirstInfo(betInfo1);

  if (inner_base && inner_base != inner_ref_value) {
    Logs.out('inner_base is not equal to inner_ref_value', inner_base, inner_ref_value);
    throw new Error('内盘基准额度和内盘索引额度不一致');
  }
  else if (!inner_base) {
    inner_base = inner_ref_value;
  }

  const profitInfo = calcSecondProfit({ ...betInfo2, inner_base, inner_odds_first: inner_odds, inner_rebate });
  const { cross_type } = profitInfo;

  profitInfo.win_side_a = typeof(profitInfo.win_side_a) !== 'number' ? undefined : fixFloat(profitInfo.win_side_a - loss_out);
  profitInfo.win_side_b = typeof(profitInfo.win_side_b) !== 'number' ? undefined : fixFloat(profitInfo.win_side_b - loss_out);
  profitInfo.win_side_c = cross_type == 'la_wa_rv' ? 0 : (typeof(profitInfo.win_side_c) !== 'number' ? undefined : fixFloat(profitInfo.win_side_c - loss_out));
  profitInfo.win_average = fixFloat(profitInfo.win_average - loss_out);

  const { win_average_rate, win_profit_rate, ...profitInfoRest } = profitInfo;

  return profitInfoRest;
}


export {
  calcTotalProfit,
  calcTotalProfitWithFixedFirst,
  getFirstInfo,
};

export default {
  calcTotalProfit,
  calcTotalProfitWithFixedFirst,
  getFirstInfo,
};
