const crypto = require('crypto');
const Logs = require('../libs/logs');
const IOR_KEYS_MAP = require('./iorKeys');
const { getSetting } = require('./settings');
const { eventSolutions } = require('./eventSolutions');

/**
 * 筛选最优赔率
 */
function getOptimalSelections(odds, rules) {
  const results = [];
  const { obRebateRatio, hgRebateRatio } = getSetting();
  const rebateMap = {
    ob: 1 + obRebateRatio / 100,
    hg: 1 + hgRebateRatio / 100,
  };

  rules.forEach((rule, index) => {
    let validOptions = [];

    for (let i = 0; i < 3; i++) {
      const innerIndex = i;
      const selection = [];
      let isValid = true;
      for (let j = 0; j < 3; j++) {
        const key = rule[j];
        const item = odds[key];

        if (!item) {
          isValid = false;
          break;
        }
        if (j === innerIndex) {
          if (!('ps' in item)) {
            isValid = false;
            break;
          }
          selection.push({
            k: key,
            p: 'ps',
            v: item.ps,
            o: item,
          });
        }
        else {
          const candidates = ['ob', 'hg'].filter((k) => k in item);
          if (candidates.length === 0) {
            isValid = false;
            break;
          }
          // Logs.out('candidates', candidates)
          const best = candidates.reduce((a, b) => {
            const aValue = (item[a]-1)*rebateMap[a];
            const bValue = (item[b]-1)*rebateMap[b];
            const seletcted = aValue > bValue ? a : b;
            return seletcted;
          });
          // Logs.out('best', item, best)
          selection.push({
            k: key,
            p: best,
            v: item[best],
            o: item,
          });
        }
      }

      if (isValid) {
        validOptions.push(selection);
      }
    }

    if (validOptions.length > 0) {
      const iors = validOptions.reduce((a, b) => {
        const sumA = a.reduce((sum, x) => sum + x.v, 0);
        const sumB = b.reduce((sum, x) => sum + x.v, 0);
        return sumA > sumB ? a : b;
      });
      results.push({ rule, iors, index });
    }
  });

  return results;
}

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

/**
 * 盘口排序
 */
const priority = { ps: 1, ob: 2, hg: 3 };
const sortCpr = (cpr) => {
  const temp = [...cpr];
  temp.sort((a, b) => priority[a.p] - priority[b.p]);
  return temp;
}

/**
 * 添加返佣
 */
const attachRebate = (ior) => {
  const { obRebateRatio, hgRebateRatio } = getSetting();
  const { p } = ior;
  let rebate = 0;
  if (p == 'ps') {
    rebate = 0;
  }
  else if (p == 'ob') {
    rebate = obRebateRatio;
  }
  else if (p == 'hg') {
    rebate = hgRebateRatio;
  }
  return { ...ior, r: rebate };
}

const eventsCombination = (passableEvents) => {
  const { minProfitAmount, innerDefaultAmount, innerRebateRatio } = getSetting();
  const solutions = [];
  passableEvents.forEach(events => {
    const { odds, info } = events;
    Object.keys(IOR_KEYS_MAP).forEach(iorGroup => {
      const rules = IOR_KEYS_MAP[iorGroup];
      const optimalSelections = getOptimalSelections(odds, rules);

      optimalSelections.forEach(selection => {
        const { rule, iors, index } = selection;
        const [, , , crossType] = rule;
        const oddsSideA = attachRebate(iors[0]);
        const oddsSideB = attachRebate(iors[1]);
        const oddsSideC = attachRebate(iors[2]);
        const innerIndex = iors.findIndex(item => item.p == 'ps');
        if (!oddsSideA || !oddsSideB || !oddsSideC) {
          return;
        }
        const cpr = [ oddsSideA, oddsSideB, oddsSideC ];
        const betInfo = {
          cross_type: crossType,
          inner_index: innerIndex,
          inner_base: innerDefaultAmount,
          inner_rebate: fixFloat(innerRebateRatio / 100, 3),
          odds_side_a: fixFloat(oddsSideA.v - 1),
          odds_side_b: fixFloat(oddsSideB.v - 1),
          odds_side_c: fixFloat(oddsSideC.v - 1),
          rebate_side_a: parseFloat((oddsSideA.r / 100).toFixed(4)),
          rebate_side_b: parseFloat((oddsSideB.r / 100).toFixed(4)),
          rebate_side_c: parseFloat((oddsSideC.r / 100).toFixed(4)),
        };
        const sol = eventSolutions(betInfo);
        if (sol?.win_average > minProfitAmount) {
          const id = info.id;
          const sortedCpr = sortCpr(cpr);
          const keys = sortedCpr.map(item => `${item.k}`).join('_');
          const sid = crypto.createHash('sha1').update(`${id}_${keys}`).digest('hex');
          const crpGroup = `${id}_${sortedCpr[0].k}`;
          const timestamp = Date.now();
          solutions.push({sid, sol, cpr, info, group: crpGroup, rule: `${iorGroup}:${index}`, timestamp});
        }
      });
    });
  });
  return solutions.sort((a, b) => {
    return b.sol.win_average - a.sol.win_average;
  });
}

module.exports = { eventsCombination };