/**
 * VermineTotemDice - Gestion des Dés de Totems (Humain / Adapté).
 *
 * Règles p. 121 :
 * - Influence : si un joueur possède strictement plus de Dés d'un Totem que
 *   de l'autre, ce Totem prend l'ascendant (+1D/-1D sur certains Domaines).
 * - Gains et pertes via les Instincts et les Interdits (1D ou 2D selon la
 *   gravité), avec des limites (3 Dés du même Totem, 5 Dés au maximum).
 */
export class VermineTotemDice {

  /**
   * Totem dominant d'un personnage.
   * @param {foundry.abstract.Document} actor
   * @returns {'human'|'adapted'|'neutral'}
   */
  static getInfluence(actor) {
    const human = actor.system?.adaptation?.totems?.human?.value || 0;
    const adapted = actor.system?.adaptation?.totems?.adapted?.value || 0;
    if (human > adapted) return "human";
    if (adapted > human) return "adapted";
    return "neutral";
  }

  /**
   * Modificateur (Bonus/Malus) d'influence pour une catégorie de compétence.
   * @param {foundry.abstract.Document} actor
   * @param {string} skillCategory catégorie (man, animal, tool, weapon, survival, world)
   * @returns {number} -1, 0 ou +1
   */
  static influenceModifier(actor, skillCategory) {
    const influence = this.getInfluence(actor);
    if (influence === "neutral" || !skillCategory) return 0;
    const cfg = CONFIG.VERMINE?.totemInfluence?.[influence];
    if (!cfg) return 0;
    if ((cfg.bonus || []).includes(skillCategory)) return 1;
    if ((cfg.malus || []).includes(skillCategory)) return -1;
    return 0;
  }

  /**
   * Applique un gain d'un Dé de Totem selon les règles d'échange et de limites.
   * Retourne un objet décrivant ce qui s'est passé (pour notification).
   * @param {foundry.abstract.Document} actor
   * @param {'human'|'adapted'} totem
   * @param {number} [count=1] 1 ou 2 dés (acte grave)
   * @returns {Promise<Object>}
   */
  static async gainDie(actor, totem, count = 1) {
    const human = actor.system.adaptation.totems.human;
    const adapted = actor.system.adaptation.totems.adapted;
    const actions = [];

    for (let i = 0; i < count; i++) {
      const step = this._gainOneDie(human.value, adapted.value, totem);
      if (step.unchanged) {
        actions.push({ ok: false, reason: "max" });
        break;
      }
      human.value = step.human;
      adapted.value = step.adapted;
      actions.push({ ok: true, exchanged: step.exchanged, replaced: step.replaced });
    }

    await actor.update({
      "system.adaptation.totems.human.value": human.value,
      "system.adaptation.totems.adapted.value": adapted.value
    });

    return this._summarize(actions, totem);
  }

  /**
   * Applique une perte d'un Dé de Totem.
   * Règle : seul l'Humain a des Interdits. Briser un Interdit Humain fait
   * perdre 1D Humain ; si le personnage n'en possède pas, il gagne 1D Adapté.
   * @param {foundry.abstract.Document} actor
   * @param {'human'} totem
   * @param {number} [count=1] 1 ou 2 dés (acte grave)
   * @returns {Promise<Object>}
   */
  static async loseDie(actor, totem, count = 1) {
    const human = actor.system.adaptation.totems.human;
    const adapted = actor.system.adaptation.totems.adapted;
    const actions = [];

    for (let i = 0; i < count; i++) {
      if (human.value > 0) {
        human.value -= 1;
        actions.push({ ok: true, lost: true });
      } else if (adapted.value < adapted.max && (human.value + adapted.value) < 5) {
        // Pas de Dé Humain : gagne un Dé Adapté à la place (limites appliquées).
        adapted.value += 1;
        actions.push({ ok: true, gainedAdapted: true });
      } else {
        actions.push({ ok: false, reason: "max" });
        break;
      }
    }

    await actor.update({
      "system.adaptation.totems.human.value": human.value,
      "system.adaptation.totems.adapted.value": adapted.value
    });

    return this._summarize(actions, totem);
  }

  /**
   * Ajoute un Dé du Totem donné en respectant les limites et les échanges.
   * Règles :
   * - 3 Dés max du même Totem, 5 Dés max au total.
   * - Si le joueur gagne un Dé qu'il ne peut conserver, il peut supprimer un
   *   Dé du Totem opposé à la place ; sinon le gain est annulé.
   * - Gagner un 4e Dé Adapté est impossible, mais supprime un Dé Humain.
   * @param {number} human
   * @param {number} adapted
   * @param {'human'|'adapted'} totem
   * @returns {Object}
   */
  static _gainOneDie(human, adapted, totem) {
    const limitTotem = 3;
    const opposite = totem === "human" ? "adapted" : "human";
    const total = human + adapted;
    const current = totem === "human" ? human : adapted;
    const oppositeVal = totem === "human" ? adapted : human;

    // Le Totem cible est déjà au maximum (3) : impossible d'empiler.
    if (current >= limitTotem) {
      if (oppositeVal > 0) {
        // Supprime un Dé du Totem opposé à la place (le gain n'augmente pas).
        const result = { human, adapted, unchanged: false, replaced: true };
        result[opposite] -= 1;
        return result;
      }
      return { human, adapted, unchanged: true };
    }

    // Limite totale atteinte (5) : peut remplacer un Dé opposé.
    if (total >= 5) {
      if (oppositeVal > 0) {
        const result = { human, adapted, unchanged: false, exchanged: true };
        result[totem] += 1;
        result[opposite] -= 1;
        return result;
      }
      return { human, adapted, unchanged: true };
    }

    // Gain normal.
    const result = { human, adapted, unchanged: false };
    result[totem] += 1;
    return result;
  }

  /**
   * Résume les actions appliquées en un libellé localisé.
   * @param {Array} actions
   * @param {'human'|'adapted'} totem
   * @returns {Object}
   */
  static _summarize(actions, totem) {
    const gained = actions.filter(a => a.ok && !a.exchanged && !a.replaced && !a.lost && !a.gainedAdapted).length;
    const exchanged = actions.filter(a => a.exchanged).length;
    const replaced = actions.filter(a => a.replaced).length;
    const lost = actions.filter(a => a.lost).length;
    const gainedAdapted = actions.filter(a => a.gainedAdapted).length;
    const refused = actions.some(a => !a.ok);
    return { totem, gained, exchanged, replaced, lost, gainedAdapted, refused };
  }
}
