import { ChatUtility } from "./chat-utility.js";
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./combat/initiative.mjs";
import { RdDItem } from "./item.js";
import { BASE_SANS_COMPETENCE, CATEGORIES_COMPETENCES } from "./item/base-items.js";
import { Misc } from "./misc.js";

const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
["Epée à 1 main", "Epée à 2 mains", "Hache à 1 main", "Hache à 2 mains", "Lance", "Masse à 1 main", "Masse à 2 mains"]];

const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20, 30, 30, 40, 40, 60, 60, 100, 100, 100, 100, 100, 100, 100, 100, 100];
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
  { niveau: 0, nombreMax: 100 },
  { niveau: 1, nombreMax: 10 },
  { niveau: 2, nombreMax: 9 },
  { niveau: 3, nombreMax: 8 },
  { niveau: 4, nombreMax: 7 },
  { niveau: 5, nombreMax: 6 },
  { niveau: 6, nombreMax: 5 },
  { niveau: 7, nombreMax: 4 },
  { niveau: 8, nombreMax: 3 },
  { niveau: 9, nombreMax: 2 },
  { niveau: 10, nombreMax: 1 },
  { niveau: 11, nombreMax: 1 },
];

/* -------------------------------------------- */

function _buildCumulXP() {
  let cumulXP = { "-11": 0 };
  let cumul = 0;
  for (let i = 0; i <= xp_par_niveau.length; i++) {
    let level = i - 10;
    cumul += xp_par_niveau[i];
    cumulXP[level] = cumul;
  }
  return cumulXP;
}

const competence_xp_cumul = _buildCumulXP();

export class RdDItemCompetence extends RdDItem {

  static get ITEM_TYPE() { return ITEM_TYPES.competence }

  static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }

  static async onReady(){
    RdDItemCompetence.SANS_COMPETENCE = await Item.create(BASE_SANS_COMPETENCE, { temporary: true })
  }

  isNiveauBase() {
    return this.system.niveau == this.system.base && this.system.xp == 0
  }

  getBaseInit() {
    const carac = this.getInitCarac()
    if (carac == undefined) {
      return undefined
    }
    return RdDInitiative.ajustementInitiative(carac.value, this.system.niveau)
  }

  getInitCarac() {
    if (!this.actor) {
      return undefined
    }
    switch (this.system.categorie) {
      case CATEGORIES_COMPETENCES.melee.key: return this.actor.system.carac.melee
      case CATEGORIES_COMPETENCES.tir.key: return this.actor.system.carac.tir
      case CATEGORIES_COMPETENCES.lancer.key: return this.actor.system.carac.lancer
      case CATEGORIES_COMPETENCES.draconic.key: return this.actor.system.carac.reve
    }
    return undefined
  }

  async _preUpdate(changed, options, user) {
    await this.checkCompetenceXP(changed.system?.xp)
    return super._preUpdate(changed, options, user)
  }
  
  async checkCompetenceXP(newXP, display = true) {
    if (this.actor?.isPersonnage() && newXP && newXP != this.system.xp && newXP > 0) {
      const newNiv = this.system.niveau + 1
      let needed = RdDItemCompetence.getCompetenceNextXp(newNiv)
      if (newXP >= needed) {
        const xpData = {
          alias: this.actor.getAlias(),
          competence: this.name,
          niveau: newNiv,
          xp: newXP,
          archetype: this.system.niveau_archetype,
          archetypeWarning: newNiv > this.system.niveau_archetype
        }
        if (display) {
          await ChatMessage.create({
            whisper: ChatUtility.getOwners(this.actor),
            content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.hbs`, xpData)
          })
        }
        return xpData
      }
    }
  }

  /* -------------------------------------------- */
  static getLabelCategorie(category) {
    return CATEGORIES_COMPETENCES[category].label;
  }
  /* -------------------------------------------- */
  /* -------------------------------------------- */
  static getNiveauBase(category, itemType) {
    let categories = RdDItem.getCategories(itemType)
    return categories[category]?.base ?? 0;
  }

  /* -------------------------------------------- */
  static getCategorie(competence) {
    return competence?.system.categorie;
  }
  static isDraconic(competence) {
    return competence?.system.categorie == 'draconic';
  }

  /* -------------------------------------------- */
  static getVoieDraconic(competences, voie) {
    return RdDItemCompetence.findFirstItem(competences, voie, {
      preFilter: it => it.isCompetence() && RdDItemCompetence.isDraconic(it),
      description: 'Draconic',
    });
  }


  /* -------------------------------------------- */
  static isArmeUneMain(competence) {
    return competence.isCompetenceArme() && competence.name.toLowerCase().includes("1 main");
  }
  static isArme2Main(competence) {
    return competence.isCompetenceArme() && competence.name.toLowerCase().includes("2 main");
  }

  static isThanatos(competence) {
    return competence.isCompetencePersonnage() && Grammar.toLowerCaseNoAccent(competence.name).includes('thanatos');
  }

  /* -------------------------------------------- */
  static isMalusEncombrementTotal(competenceName) {
    return competenceName?.toLowerCase().match(/(natation|acrobatie)/) || 0;
  }

  /* -------------------------------------------- */
  static getListTronc(compName) {
    for (let troncList of competenceTroncs) {
      for (let troncName of troncList) {
        if (troncName == compName)
          return troncList;
      }
    }
    return []
  }

  /* -------------------------------------------- */
  static computeTotalXP(competences) {
    const total = competences.map(c => RdDItemCompetence.computeXP(c))
      .reduce(Misc.sum(), 0);
    const economieTronc = RdDItemCompetence.computeEconomieXPTronc(competences);
    return total - economieTronc;
  }

  /* -------------------------------------------- */
  static computeXP(competence) {
    const factor = RdDItemCompetence.isThanatos(competence) ? 2 : 1; // Thanatos compte double !
    const xpNiveau = RdDItemCompetence.computeDeltaXP(competence.system.base, competence.system.niveau ?? competence.system.base);
    const xp = competence.system.xp ?? 0;
    const xpSort = competence.system.xp_sort ?? 0;
    return factor * (xpNiveau + xp) + xpSort;
  }

  /* -------------------------------------------- */
  static computeEconomieXPTronc(competences) {
    return competenceTroncs.map(
      list => list.map(name => RdDItemCompetence.findCompetence(competences, name, { onMessage: message => { } }))
        // calcul du coût xp jusqu'au niveau 0 maximum
        .map(it => RdDItemCompetence.computeDeltaXP(it?.system.base ?? -11, Math.min(it?.system.niveau ?? -11, 0)))
        .sort(Misc.ascending())
        .splice(0, list.length - 1) // prendre toutes les valeurs sauf l'une des plus élevées
        .reduce(Misc.sum(), 0)
    ).reduce(Misc.sum(), 0);
  }

  /* -------------------------------------------- */
  static computeDeltaXP(from, to) {
    RdDItemCompetence._valideNiveau(from);
    RdDItemCompetence._valideNiveau(to);
    return competence_xp_cumul[to] - competence_xp_cumul[from];
  }

  /* -------------------------------------------- */
  static computeCompetenceXPCost(competence) {
    let xp = RdDItemCompetence.getDeltaXp(competence.system.base, competence.system.niveau ?? competence.system.base);
    xp += competence.system.xp ?? 0;
    if (compData.name.includes('Thanatos')) xp *= 2; /// Thanatos compte double !
    xp += competence.system.xp_sort ?? 0;
    return xp;
  }

  /* -------------------------------------------- */
  static computeEconomieCompetenceTroncXP(competences) {
    let economie = 0;
    for (let troncList of competenceTroncs) {
      let list = troncList.map(name => RdDItemCompetence.findCompetence(competences, name))
        .sort(Misc.descending(c => this.system.niveau)); // tri du plus haut au plus bas
      list.splice(0, 1); // ignorer la plus élevée
      list.map(c => c).forEach(c => {
        economie += RdDItemCompetence.getDeltaXp(c.system.base, Math.min(c.system.niveau, 0))
      });
    }
    return economie;
  }

  /* -------------------------------------------- */
  static levelUp(item, stressTransforme) {
    item.system.xpNext = RdDItemCompetence.getCompetenceNextXp(item.system.niveau);
    const xpManquant = item.system.xpNext - item.system.xp;
    item.system.isLevelUp = xpManquant <= 0;
    item.system.isStressLevelUp = (xpManquant > 0 && stressTransforme >= xpManquant && item.system.niveau < item.system.niveau_archetype);
    item.system.stressXpMax = 0;
    if (xpManquant > 0 && stressTransforme > 0 && item.system.niveau < item.system.niveau_archetype) {
      item.system.stressXpMax = Math.min(xpManquant, stressTransforme);
    }
    item.system.isArchetypeAtteint = item.system.niveau >= item.system.niveau_archetype;
  }

  /* -------------------------------------------- */
  static isNiveauBase(item) {
    return item.system.niveau == undefined || Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type);
  }

  /* -------------------------------------------- */
  static findCompetence(list, idOrName, options = {}) {
    if (idOrName == undefined || idOrName == "") {
      return RdDItemCompetence.SANS_COMPETENCE
    }
    options = foundry.utils.mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, { overwrite: false, inplace: false });
    return RdDItemCompetence.findFirstItem(list, idOrName, options);
  }

  /* -------------------------------------------- */
  static findCompetences(list, name, options = {}) {
    options = foundry.utils.mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, { overwrite: false, inplace: false });
    return Misc.findAllLike(name, list, options);
  }


  static findFirstItem(list, idOrName, options) {
    return list.find(it => it.id == idOrName && options.preFilter(it))
      ?? Misc.findFirstLike(idOrName, list, options);
  }

  /* -------------------------------------------- */
  static getCompetenceNextXp(niveau) {
    return RdDItemCompetence.getCompetenceXp(niveau + 1);
  }

  /* -------------------------------------------- */
  static getCompetenceXp(niveau) {
    RdDItemCompetence._valideNiveau(niveau);
    return niveau < -10 ? 0 : xp_par_niveau[niveau + 10];
  }

  /* -------------------------------------------- */
  static getDeltaXp(from, to) {
    RdDItemCompetence._valideNiveau(from);
    RdDItemCompetence._valideNiveau(to);
    return competence_xp_cumul[to] - competence_xp_cumul[from];
  }

  /* -------------------------------------------- */
  static _valideNiveau(niveau) {
    if (niveau < -11 || niveau > niveau_max) {
      console.warn(`Niveau ${niveau} en dehors des niveaux de compétences: [-11, ${niveau_max} ]`);
    }
  }

  /* -------------------------------------------- */
  static computeResumeArchetype(competences) {
    const computed = foundry.utils.deepClone(limitesArchetypes);
    computed.forEach(it => { it.nombre = 0; it.reste = it.nombreMax; });

    competences.map(it => Math.max(0, it.system.niveau_archetype))
      .filter(n => n > 0)
      .forEach(n => {
        computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0, nombre: 0 };
        computed[n].reste--;
        computed[n].nombre++;

      });
    return computed.filter(it => it.niveau > 0);
  }

  /* -------------------------------------------- */
  static triVisible(competences) {
    return competences
      ? competences.filter(it => !it.system.isHidden).sort((a, b) => RdDItemCompetence.compare(a, b))
      : []
  }

  static $positionTri(comp) {
    if (comp.name.startsWith("Survie")) {
      if (comp.name.includes("Cité")) return 0;
      if (comp.name.includes("Extérieur")) return 1;
      return 2;
    }
    if (comp.system.categorie.startsWith("melee")) {
      if (comp.name.includes("Corps")) return 0;
      if (comp.name.includes("Dague")) return 1;
      if (comp.name.includes("Esquive")) return 2;
      return 3;
    }
    if (comp.system.categorie.startsWith("draconic")) {
      if (comp.name.includes("Oniros")) return 0;
      if (comp.name.includes("Hypnos")) return 1;
      if (comp.name.includes("Narcos")) return 2;
      if (comp.name.includes("Thanatos")) return 3;
      return 4;
    }
    return 0;
  }

  static compare(a, b) {
    const diff = RdDItemCompetence.$positionTri(a) - RdDItemCompetence.$positionTri(b);
    return diff ? diff : a.name.localeCompare(b.name);
  }

}