import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";

/**
 * difficultés au delà de -10
 */
const levelDown = [
  { level: -11, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 90 },
  { level: -12, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 70 },
  { level: -13, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 50 },
  { level: -14, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 30 },
  { level: -15, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 10 },
  { level: -16, score: 1, norm: 1, sign: 0, part: 0, epart: 0, etotal: 2 }
];
const levelImpossible = { score: 0, norm: 0, sign: 0, part: 0, epart: 0, etotal: 1 };

const reussiteNormale = { code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) };
const reussiteSignificative = { code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) };
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }

const reussites = [
  { code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
  { code: "epart", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: false, ptTache: -2, ptQualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) },
  { code: "echec", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.norm && roll < target.etotal) },
  reussiteNormale,
  reussiteSignificative,
  { code: "part", isPart: true, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 3, ptQualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) },
  { code: "error", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
];

/* -------------------------------------------- */
const CARAC_MAXIMUM_RESOLUTION = 40;
/* -------------------------------------------- */
export class RdDResolutionTable {
  static resolutionTable = this.build()

  /* -------------------------------------------- */
  static build() {
    let table = []
    for (var caracValue = 0; caracValue <= CARAC_MAXIMUM_RESOLUTION; caracValue++) {
      table[caracValue] = this._computeRow(caracValue);
    }
    return table;
  }

  /* -------------------------------------------- */
  static computeChances(carac, level) {
    if (level < -16) {
      return levelImpossible;
    }
    if (level < -10) {
      return levelDown.find(it => it.level == level);
    }
    const percentage = RdDResolutionTable.computePercentage(carac, level);
    return this._computeCell(level, percentage);
  }

  /* -------------------------------------------- */
  static _computeRow(caracValue) {
    let dataRow = [
      this._computeCell(-10, Math.max(Math.floor(caracValue / 4), 1)),
      this._computeCell(-9, Math.max(Math.floor(caracValue / 2), 1))
    ]
    for (var diff = -8; diff <= 22; diff++) {
      dataRow[diff + 10] = this._computeCell(diff, RdDResolutionTable.computePercentage(caracValue, diff));
    }
    return dataRow;
  }

  /* -------------------------------------------- */
  static _computeCell(level, percentage) {
    return {
      level: level,
      score: percentage,
      norm: Math.min(99, percentage),
      sign: this._reussiteSignificative(percentage),
      part: this._reussitePart(percentage),
      epart: this._echecParticulier(percentage),
      etotal: this._echecTotal(percentage)
    };
  }

  /* -------------------------------------------- */
  static getResultat(code) {
    let resultat = reussites.find(r => code == r.code);
    if (resultat == undefined) {
      resultat = reussites.find(r => r.code == "error");
    }
    return resultat;
  }

  static actorChatName(actor) {
    return actor ?? game.user.name;
  }

  /* -------------------------------------------- */
  static async rollData(rollData) {
    rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData);
    return rollData
  }

  /* -------------------------------------------- */
  static async roll(caracValue, finalLevel, rollData = {}) {
    let chances = foundry.utils.deepClone(this.computeChances(caracValue, finalLevel));
    this._updateChancesWithBonus(chances, rollData.bonus, finalLevel);
    this._updateChancesFactor(chances, rollData.diviseurSignificative);
    chances.showDice = rollData.showDice;
    chances.rollMode = rollData.current?.rollmode?.key ?? game.settings.get("core", "messageMode")

    let rolled = await this.rollChances(chances, rollData.diviseurSignificative, rollData.forceDiceResult);
    rolled.caracValue = caracValue;
    rolled.finalLevel = finalLevel;
    rolled.bonus = rollData.bonus;
    rolled.factorHtml = Misc.getFractionOneN(rollData.diviseurSignificative);

    if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
      rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
      rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
    }
    return rolled;
  }

  /* -------------------------------------------- */
  static findNiveauNecessaire(carac, rolled) {
    if (carac == 0) {
      return NaN;
    }
    if (rolled >= carac) {
      const upper = Math.ceil(rolled / carac);
      return 2 * upper - 10
    }
    if (rolled > Math.floor(carac / 2)) {
      return -8
    }
    if (rolled > Math.floor(carac / 4)) {
      return -9
    }
    if (rolled > 1) {
      return -10
    }
    return -11;
  }

  /* -------------------------------------------- */
  static _updateChancesFactor(chances, diviseur) {
    if (chances.level > -11 && diviseur && diviseur > 1) {
      let newScore = Math.floor(chances.score / diviseur);
      foundry.utils.mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
    }
  }

  /* -------------------------------------------- */
  static _updateChancesWithBonus(chances, bonus, finalLevel) {
    if (bonus && finalLevel > -11) {
      let newScore = Number(chances.score) + bonus;
      foundry.utils.mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
    }
  }

  /* -------------------------------------------- */
  static replaceParticuliereDemiSurprise(chances) {
    foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'part'), { overwrite: true });
  }

  /* -------------------------------------------- */
  static significativeRequise(chances) {
    chances.roll = Math.min(chances.part + 1, chances.sign)
    foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'sign'), { overwrite: true });
  }

  /* -------------------------------------------- */
  static succesRequis(chances) {
    chances.roll = chances.score;
    foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'norm'), { overwrite: true });
  }

  /* -------------------------------------------- */
  static async rollChances(chances, diviseur, forceDiceResult = -1) {
    chances.forceDiceResult = forceDiceResult <= 0 || forceDiceResult > 100 ? undefined : { total: forceDiceResult };
    chances.roll = await RdDDice.rollTotal("1d100", chances);
    foundry.utils.mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
    return chances;
  }

  /* -------------------------------------------- */
  static computePercentage(carac, diff) {
    if (diff < -16) return 0
    if (diff < -10) return 1
    if (diff == -10) return Math.max(Math.floor(carac / 4), 1)
    if (diff == -9) return Math.max(Math.floor(carac / 2), 1)
    return Math.max(Math.floor(carac * (diff + 10) / 2), 1);
  }

  /* -------------------------------------------- */
  static computeReussite(chances, roll, diviseur) {
    const reussite = reussites.find(x => x.condition(chances, roll))
    if (diviseur > 1 && reussite.isSuccess) {
      if (chances.norm < roll * diviseur) {
        return reussiteInsuffisante
      }
      return reussiteSignificative
    }
    return reussite
  }

  /* -------------------------------------------- */
  static _reussiteSignificative(percentage) {
    return Math.floor(percentage / 2);
  }

  /* -------------------------------------------- */
  static _reussitePart(percentage) {
    return Math.ceil(percentage / 5);
  }

  /* -------------------------------------------- */
  static _echecParticulier(percentage) {
    const epart = Math.ceil(percentage / 5) + 80;
    return epart >= 100 ? 101 : epart;
  }

  /* -------------------------------------------- */
  static _echecTotal(percentage) {
    const etotal = Math.ceil(percentage / 10) + 91;
    return percentage >= 100 ? 101 : Math.min(etotal, 100);
  }

  /* -------------------------------------------- */
  static subTable(carac, level, delta = { carac: 2, level: 5 }) {
    return {
      carac,
      level,
      minCarac: carac - (delta?.carac ?? 2),
      maxCarac: carac + (delta?.carac ?? 2),
      minLevel: level - (delta?.level ?? 5),
      maxLevel: level + (delta?.level ?? 5)
    };
  }

  /* -------------------------------------------- */
  static async buildHTMLTable({ carac: carac, level: level, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11 }) {
    let colonnes = maxLevel - minLevel;
    minCarac = Math.max(minCarac, 1);
    maxCarac = Math.min(maxCarac, minCarac + 20);
    minLevel = Math.max(minLevel, -10);
    maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes);
    return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', {
      carac: carac,
      difficulte: level,
      min: minLevel,
      rows: Misc.intArray(minCarac, maxCarac + 1),
      cols: Misc.intArray(minLevel, maxLevel + 1)
    });
  }

}