import { orderBy } from "lodash";

import { Constants } from "@dndbeyond/character-rules-engine/es";
import { DiceOperations } from "@dndbeyond/pocket-dimension-dice/enums";
import {
  RollDicePayload,
  DiceNotationSet,
} from "@dndbeyond/pocket-dimension-dice/types";

interface Hack__OrderableDieValueInfo {
  idx: number;
  value: number;
}

/**
 * generate lookup of dieTerm.dice idx to DieTerm
 * for RollValueContract dieType
 *
 * @param rollRequestResult
 */
export function hack__generateDiceRollResultTermLookup(
  rollRequestResult: RollDicePayload["data"]
): Record<number, DiceNotationSet> {
  const hack__diceRollResultTermLookup: Record<number, DiceNotationSet> = {};

  let dieCurrentIdx: number = 0;
  rollRequestResult.rolls[0].diceNotation.set.forEach((diceNotationSet) => {
    diceNotationSet?.dice.forEach(() => {
      hack__diceRollResultTermLookup[dieCurrentIdx] = diceNotationSet;
      dieCurrentIdx++;
    });
  });

  return hack__diceRollResultTermLookup;
}

/**
 * generate lookup of RollResult.values array idx to DiceRollExcludeTypeEnum
 * for RollValueContract excludeType
 *
 * @param rollRequestResult
 */
export function hack__generateDiceRollResultValueExcludeTypeLookup(
  rollRequestResult: RollDicePayload["data"]
): Record<number, Constants.DiceRollExcludeTypeEnum> {
  const hack__diceRollResultValueExcludeTypeLookup: Record<
    number,
    Constants.DiceRollExcludeTypeEnum
  > = {};

  //RollResult | undefined - RollResult is not exported from Dice;
  //  only expect to have one single rollRequest.
  const rollResult = rollRequestResult.rolls[0].result;

  let dieGroupStartIdx: number = 0;

  rollRequestResult.rolls[0].diceNotation.set.forEach((diceNotationSet) => {
    let diceCount = diceNotationSet?.count;

    //determine sortOrder based on dieTerm.operation
    let sortOrder: "asc" | "desc" | null = null;
    switch (diceNotationSet?.operation) {
      case DiceOperations.Min:
        sortOrder = "asc";
        break;
      case DiceOperations.Max:
      default:
        sortOrder = "desc";
    }

    let results =
      rollResult?.values.slice(
        dieGroupStartIdx,
        dieGroupStartIdx + (diceCount ?? 0)
      ) ?? [];
    let hack__orderableDieValueInfos: Array<Hack__OrderableDieValueInfo> =
      results.map((value, idx) => ({
        idx: dieGroupStartIdx + idx,
        value,
      }));
    let orderedResults = orderBy(
      hack__orderableDieValueInfos,
      (valueInfo) => valueInfo.value,
      sortOrder
    );

    //build-up hack__diceRollResultValueExcludeTypeLookup
    orderedResults
      .slice(0, diceNotationSet?.operand)
      .forEach((diceNotationSet) => {
        hack__diceRollResultValueExcludeTypeLookup[diceNotationSet.idx] =
          Constants.DiceRollExcludeTypeEnum.ALL;
      });
    orderedResults
      .slice(diceNotationSet?.operand)
      .forEach((diceNotationSet) => {
        hack__diceRollResultValueExcludeTypeLookup[diceNotationSet.idx] =
          Constants.DiceRollExcludeTypeEnum.NONE;
      });

    //move diceGroupStartIdx
    dieGroupStartIdx += diceCount ?? 0;
  });

  return hack__diceRollResultValueExcludeTypeLookup;
}
