import { Grammar } from "./grammar.js";
import { RdDItemCompetence } from "./item-competence.js";
import { ITEM_TYPES } from "./constants.js";
import { Misc } from "./misc.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDItem } from "./item.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";

export const VOIES_DRACONIC = [
  { code: 'O', label: "Voie d'Oniros", short: 'Oniros', ordre: 'a' },
  { code: 'H', label: "Voie d'Hypnos", short: 'Hypnos', ordre: 'b' },
  { code: 'N', label: "Voie de Narcos", short: 'Narcos', ordre: 'c' },
  { code: 'T', label: "Voie de Thanatos", short: 'Thanatos', ordre: 'd' },
  { code: 'O/H/N/T', label: "Oniros/Hypnos/Narcos/Thanatos", short: 'O/H/N/T', ordre: 'e' },
  { code: 'O/H/N', label: "Oniros/Hypnos/Narcos", short: "O/H/N", ordre: 'f' }
]

/* -------------------------------------------- */
export class RdDItemSort extends RdDItem {
  static preloadHandlebars() {
    Handlebars.registerHelper('itemSort-codeDraconic', voie => RdDItemSort.getCode(voie))
    Handlebars.registerHelper('itemSort-shortDraconic', voie => RdDItemSort.getShortVoie(voie))
    Handlebars.registerHelper('itemSort-diffReve', sort => RdDItemSort.diffReve(sort))
    Handlebars.registerHelper('itemSort-coutReve', sort => RdDItemSort.coutReve(sort))
  }

  static lancements(sort) { return sort?.system.lancements.length ?? 0 }

  static findEnchantement(actor) { return RdDItemSort.findSort(actor, 'Enchantement', ['Narcos', 'N']) }
  static findPermanence(actor) { return RdDItemSort.findSort(actor, 'Permanence', ['Narcos', 'N']) }
  static findPurification(actor) { return RdDItemSort.findSort(actor, 'Purification', ['Narcos', 'N']) }

  static findSort(actor, name, draconics) {
    return actor.itemTypes[ITEM_TYPES.sort].find(it => Grammar.includesLowerCaseNoAccent(it.name, name)
      && (draconics == undefined || draconics.includes(it.system.draconic)))
  }
  static async changeLancementsSort(sort, changement) {
    await sort?.update({ 'system.lancements': changement(sort.system.lancements) });
  }

  static toVar(value) {
    return value ? value.replace('variable', 'var') : ''
  }

  static computeTotalXP(sorts) {
    return sorts.map(sort => RdDItemSort.isDifficulteVariable(sort) ? 70 : -10 * Misc.toInt(sort.system.difficulte))
      .reduce(Misc.sum(), 0)
  }

  static isSortOnCoord(sort, coord) {
    let tmr = TMRUtility.getTMR(coord)
    const caseTMR = sort.system.caseTMR.toLowerCase();
    const caseTMRspeciale = sort.system.caseTMRspeciale.toLowerCase();

    return caseTMR.includes('variable')
      || caseTMRspeciale.includes('variable')
      || (caseTMR == tmr.type)
      || (caseTMR.includes('special') && caseTMRspeciale.includes(coord.toLowerCase()))
  }

  static getCaseTMR(sort) {
    const caseTMR = sort.system.caseTMR.toLowerCase();
    const caseTMRspeciale = sort.system.caseTMRspeciale.toLowerCase();
    if (caseTMR.includes('variable') || caseTMRspeciale.includes('variable') || caseTMR.includes('special')) {
      return sort.system.caseTMRspeciale
    }
    return sort.system.caseTMR
  }


  static diffReve(sort) {
    if (!sort?.system) return '';
    return RdDItemSort.toVar((sort.system.difficulte.match(/\-?(\d)+/) ? 'R' : 'R ') + sort.system.difficulte)
  }
  static coutReve(sort) {
    if (!sort?.system) return '';
    return RdDItemSort.toVar((Number.isInteger(sort.system.ptreve) || sort.system.ptreve.match(/(\d)+\+?/) ? 'r' : 'r ') + sort.system.ptreve)
  }
  static getDraconicsSort(competencesDraconic, sort) {

    function getVoies() {
      switch (Grammar.toLowerCaseNoAccent(sort.name)) {
        case "lecture d'aura":
        case "detection d'aura":
          return competencesDraconic
        case "annulation de magie":
          return competencesDraconic.filter(it => !RdDItemCompetence.isThanatos(it))
        default:
          return sort.system.draconic.split('/').map(voie => RdDItemCompetence.getVoieDraconic(competencesDraconic, voie))
      }
    }
    return getVoies()
      .filter(it => it.system.niveau > -11)
      .sort(Misc.descending(it => it.system.niveau))
  }

  static getBestDraconicSort(competencesDraconic, sort) {
    return RdDItemSort.getDraconicsSort(competencesDraconic, sort).find(it => true)
  }

  static getOrdreCode(code) {
    return (VOIES_DRACONIC.find(it => it.code == code)?.ordre ?? '?')
  }

  static getCodeVoie(voie) {
    return VOIES_DRACONIC.find(it => [it.code, it.short, it.label].includes(voie))?.code ?? '?'
  }

  static getShortVoie(voie) {
    return VOIES_DRACONIC.find(it => [it.code, it.short, it.label].includes(voie))?.short ?? voie
  }

  static getCode(sort, codeVoies = ['O', 'H', 'N', 'T']) {
    switch (Grammar.toLowerCaseNoAccent(sort.name)) {
      case "lecture d'aura":
      case "detection d'aura":
        return RdDItemSort.$voiesConnues('O/H/N/T', codeVoies)
      case "annulation de magie":
        return RdDItemSort.$voiesConnues('O/H/N', codeVoies)
    }
    return RdDItemSort.getCodeVoie(sort.system.draconic)
  }

  static $voiesConnues(voiesSort, voies) {
    const codes = voies.filter(it => voiesSort.includes(it))
      .sort(Misc.ascending(it => RdDItemSort.getOrdreCode(it)))
    return Misc.join(codes ?? [''], '/');
  }

  /* -------------------------------------------- */
  static isDifficulteVariable(sort) {
    return sort && (sort.system.difficulte.toLowerCase() == "variable");
  }

  /* -------------------------------------------- */
  static isCoutVariable(sort) {
    return sort && !Number.isInteger(sort.system.ptreve) && (sort.system.ptreve.toLowerCase() == "variable" || sort.system.ptreve.indexOf("+") >= 0);
  }

  /* -------------------------------------------- */
  static setCoutReveReel(sort) {
    if (sort) {
      sort.system.ptreve_reel = this.isCoutVariable(sort) ? 1 : sort.system.ptreve;
    }
  }

  /* -------------------------------------------- */
  static getDifficulte(sort, variable) {
    if (sort && !RdDItemSort.isDifficulteVariable(sort)) {
      return Misc.toInt(sort.system.difficulte);
    }
    return variable;
  }

  /**
   * Retourne une liste de bonus/case pour un item-sheet
  * @param {} item 
  */
  static getBonusCaseList(item) {
    // Gestion spéciale case bonus
    if (item.type == ITEM_TYPES.sort) {
      return RdDItemSort.stringToBonuscases(item.system.bonuscase)
    }
    return [];
  }

  /* -------------------------------------------- */
  static async incrementBonusCase(actor, sort, coord) {
    let bonuscase = RdDItemSort.calculBonuscase(sort, coord)

    await actor.updateEmbeddedDocuments('Item', [{ _id: sort.id, 'system.bonuscase': bonuscase }]);
  }


  static calculBonuscase(sort, coord) {
    if (TMRUtility.isFleuve(coord)) {
      coord = FLEUVE_COORD
    }
    let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase)
    const existing = list.find(it => it.case == coord)
    const bonus = Number(existing?.bonus ?? 0) + 1
    if (existing) {
      existing.bonus = bonus
    }
    else {
      list.push({ case: coord, bonus: 1 })
    }
    return RdDItemSort.bonuscasesToString(list)
  }

  /* -------------------------------------------- */
  static getCaseBonus(sort, coord, actor) {
    const search = TMRUtility.isFleuve(coord)
      ? it => it.case == 'Fleuve'
      : it => it.case == coord;
    const bc = RdDItemSort.stringToBonuscases(sort.system.bonuscase)
      .find(search)
    let bonus = Number(bc?.bonus ?? 0);
    if (actor && ReglesOptionnelles.isUsing("plafonner-bonus-case-reve")) {
      const cap = Misc.toInt(actor.system.carac?.reve?.value);
      if (cap) bonus = Math.min(bonus, cap);
    }
    return bonus;
  }

  static bonuscasesToString(list) {
    return list.map(it => `${it.case}:${it.bonus}`)
      .sort(Misc.ascending())
      .join(',');
  }

  static stringToBonuscases(bonuscase) {
    if (bonuscase == undefined || bonuscase == '') {
      return []
    }
    return bonuscase.split(',')
      .map(it => it.split(':'))
      .map(it => { return { case: it[0], bonus: it[1] } });
  }

  static prepareSortEnReserve(sort, draconic, ptreve, coord) {
    return {
      type: ITEM_TYPES.sortreserve,
      name: sort.name,
      img: sort.img,
      system: { sortid: sort.id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
    };
  }

  static prepareSortAddLancement(sort, reveSort) {
    const precedents = sort.system.lancements ?? []
    const lancements = [...precedents, {
      timestamp: game.system.rdd.calendrier.getTimestamp(),
      reve: reveSort
    }]
    return lancements
  }
}