

/**
 * Handles combat-related dice rolls for Vermine2047.
 * Uses d10-based system with success counting as per official rules.
 */
export class VermineFight {

  /**
   * Performs a d10-based test according to Vermine2047 official rules.
   * Each die result is compared to a difficulty threshold.
   * Each result >= difficulty counts as 1 Success.
   * 
   * @param {number} enemyAchievement - Opponent's achievement score
   * @param {number} enemyConservation - Opponent's conservation score
   * @param {string} skillKey - The skill key being tested
   * @param {number} skill - The skill value
   * @param {Object} params - Additional test parameters
   * @param {number} [params.difficulty=7] - Difficulty threshold (3-10)
   * @param {boolean} [params.spleen] - Whether to use spleen rule
   * @param {boolean} [params.purpose] - Whether to use purpose rule
   * @param {number} [params.usure] - Wear/usage modifier
   * @param {number} [params.trait] - Trait bonus
   * @param {boolean} [params.specialization] - Whether specialization applies
   * @param {Actor} actor - The actor performing the test
   * @returns {Promise<Object>} Roll data including successes count
   */
  async performTest(enemyAchievement, enemyConservation, skillKey, skill, params, actor) {
    // Use d10 as per Vermine2047 official rules
    const basePool = Math.max(skill, 1)
    const dicePool = (params.spleen != undefined || params.purpose != undefined) ? String(basePool + 1) : String(basePool)
    const difficulty = params.difficulty || 7; // Default difficulty
    const r = new Roll(dicePool + `d10`);
    let diceString = '';
    let dicePoolHint = '';
    let discardedRoll = false;
    let bonus = 0;
    let bonusText = '/+';
    let currentSkillScore = skill;


    r.roll(); // dice are rolled
    if (params.usure != undefined) {
      currentSkillScore += params.usure;
      bonus += params.usure;
    }

    if (params.specialization != undefined) {
      currentSkillScore += 2;
    }

    if (params.trait != undefined) {
      currentSkillScore += params.trait;
      bonus += params.trait;
    }

    bonusText += bonus;

    let targetText = game.i18n.format('VERMINE.Selected') + ' : ' + game.i18n.format(skillKey) + " " + skill + bonusText;
    if (params.specialization != undefined) {
      targetText += " (S)";
    }
    // tri par ordre croissant
    r.terms[0].results.sort((a, b) => a.result - b.result);

    if (params.purpose != undefined) {
      discardedRoll = r.terms[0].results.shift();
      dicePoolHint = ' - ' + game.i18n.format('VERMINE.PurposeTrait');
    } else if (params.spleen != undefined) {
      discardedRoll = r.terms[0].results.pop();
      dicePoolHint = ' - ' + game.i18n.format('VERMINE.SpleenTrait');
    }
    const discardedRollText = (discardedRoll.result != undefined) ? '<div class="discarded-roll">' + discardedRoll.result + '</div>' : "";

    // Count successes (dice >= difficulty) as per Vermine2047 rules
    let successes = 0;
    for (let i = 0; i < r.terms[0].results.length; i++) {
      let result = r.terms[0].results[i].result;
      if (result >= difficulty) {
        successes++;
      }
      diceString += '<li class="roll die d10 die-' + i + (result >= difficulty ? ' success' : '') + '">' + result + '</li>';
    }

    let hintText = game.i18n.format('VERMINE.ConfrontationHint');

    // Build a dynamic html using the variables from above.
    const html = `
            <div class="vermine2047 roll confrontation">
                <div class="dice-roll">
                    <div class="dice-result">
                        <div class="dice-formula">
                        ` + dicePool + `d10 ` + dicePoolHint + `
                        </div>
                        <div class="dice-tooltip expanded">
                            <section class="tooltip-part">
                                <div class="parameters">
                                  ` + targetText + `
                                </div>
                                <div class="dice flexrow flex-between items-center">
                                    <ol class="dice-rolls">` + diceString + `</ol>
                                    <div class="discards text-right">` + discardedRollText + `</div>
                                </div>
                            </section>
                        </div>` +
      `<p class="step1-text" id="step1">` + hintText + `</p>
                          <div class="row">
                              <a class="inline-block button add-to-achievement">` + game.i18n.format('VERMINE.Achievement') + `</a>
                              <a class="inline-block button add-to-conservation">` + game.i18n.format('VERMINE.Conservation') + `</a>
                              <a class="inline-block button reset"><i class="fa-solid fa-rotate-right"></i></a>
                              <a class="inline-block button resolve"><i class="fa-solid fa-check"></i></a>
                          </div>
                          <div class="success-count">
                            <strong>` + game.i18n.format('VERMINE.success_count') + `:</strong> <span class="success-value">` + successes + `</span>
                          </div>
                    </div>
                </div>
            </div>
        `;

    // Check if the dice3d module exists (Dice So Nice). If it does, post a roll in that and then
    // send to chat after the roll has finished. If not just send to chat.
    if (game.dice3d) {
      game.dice3d.showForRoll(r).then((displayed) => {
        this.sendToChat(html, r, actor);
      });
    } else {
      this.sendToChat(html, r, actor);
    };

    // Return roll data for further processing with d10 success counting
    return {
      roll: r,
      successes: successes,
      difficulty: difficulty,
      dicePool: parseInt(dicePool, 10),
      total: r.total
    };

  }

  async sendToChat(content, roll, actor) {
    let conf = {
      user: game.user._id,
      content: content,
      roll: roll,
      // sound: 'sounds/dice.wav'
    };

    if (actor)
      conf.speaker = ChatMessage.getSpeaker({ actor: actor });
    // Send's Chat Message to foundry, if items are missing they will appear as false or undefined and this not be rendered.
    ChatMessage.create(conf).then((msg) => {
      return msg;
    });
  }

  static instance = null;

  static get() {
    if (!VermineFight.instance)
      VermineFight.instance = new VermineFight();
    return VermineFight.instance;
  }


  // data injected to char data
  static previousValues = {
    dicePool: 4,
    skills: [],
    cskills: [],
    cephalic: false,
    achievementReroll: 0,
    conservationReroll: 0
  };

  static rollerTemplate = 'systems/vermine2047/templates/fight.html';
  static CombatResultTemplate = 'systems/vermine2047/templates/fight-result.html';

}

export class VermineCombat extends Combat {
  _encounterCheck() {
    // encounter check
  }

  async rollInitiative(ids, {formula=null, updateTurn=true, messageMode, messageOptions={}}={}) {
    // roll initiative
    return super.rollInitiative(ids, {formula, updateTurn, messageMode, messageOptions});
  }

  nextRound() {
    /*let combatants = this.combatants.contents
    for (let c of combatants) {
      let actor = game.actors.get( c.actorId )
      actor.clearRoundModifiers()
    }*/
    super.nextRound();
  }

  /************************************************************************************/
  startCombat() {
    /*let combatants = this.combatants.contents
    for (let c of combatants) {
      let actor = game.actors.get( c.actorId )
      actor.storeVitaliteCombat()
    }*/

    return super.startCombat();
  }

  /************************************************************************************/
  /**
   * Nettoyage lors de la suppression du combat (bouton « Terminer » / suppression).
   * Doit appeler le handler de base : c'est lui qui re-rend le CombatTracker
   * (`ui.combat.render({combat: null})`) — sans cela le tracker reste figé sur
   * l'ancien ordre de tour jusqu'au rechargement de la page (F5).
   * @param {object} options
   * @param {string} userId
   */
  _onDelete(options, userId) {
    super._onDelete(options, userId);
  }
}

export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {

  /** @override */
  static DEFAULT_OPTIONS = {
    actions: {
      deleteCombatant: VermineCombatTracker.#onDeleteCombatant,
      deleteCombat: VermineCombatTracker.#onDeleteCombat
    }
  };

  /** @override */
  static PARTS = {
    main: {
      template: "systems/vermine2047/templates/combat-tracker.hbs",
      root: true
    }
  };

  /** @override */
  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.user = game.user;

    const combat = this.viewed;
    const hasCombat = combat !== null;
    const combats = this.combats;
    const currentIdx = combats.indexOf(combat);
    const isPlayerTurn = combat?.combatant?.players?.includes(game.user);
    const canControl = combat?.turn && combat.turn.between(1, combat.turns.length - 2)
      ? combat.canUserModify(game.user, "update", { turn: 0 })
      : combat?.canUserModify(game.user, "update", { round: 0 });

    Object.assign(context, {
      combat,
      hasCombat,
      combats: combats.map((c, i) => ({ id: c.id, name: c.name, label: i + 1, active: i === currentIdx })),
      combatCount: combats.length,
      currentIndex: currentIdx + 1,
      previousId: combats[currentIdx - 1]?.id,
      nextId: combats[currentIdx + 1]?.id,
      round: combat?.round,
      displayCycle: combats.length > 7,
      control: isPlayerTurn && canControl,
      linked: combat?.scene !== null,
      initiativeIcon: CONFIG.Combat.initiativeIcon,
      css: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
      cssClass: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
      cssId: "combat",
      tabName: "combat",
      labels: {
        scope: game.i18n.localize(`COMBAT.${combat?.scene ? "Linked" : "Unlinked"}`)
      },
      turns: []
    });

    // Tracker part : construit les tours + gestion des décimales d'initiative.
    let hasDecimals = false;
    if ( combat ) {
      for ( const [i, combatant] of combat.turns.entries() ) {
        if ( !combatant.visible ) continue;
        const turn = await this._prepareTurnContext(combat, combatant, i);
        if ( turn.hasDecimals ) hasDecimals = true;
        context.turns.push(turn);
      }
    }
    const precision = CONFIG.Combat.initiative.decimals;
    context.turns.forEach(t => {
      if ( Number.isFinite(t.initiative) ) t.initiative = t.initiative.toFixed(hasDecimals ? precision : 0);
    });
    context.hasDecimals = hasDecimals;
    return context;
  }

  /** @override */
  async _prepareTurnContext(combat, combatant, index) {
    const turn = await super._prepareTurnContext(combat, combatant, index);
    const actor = combatant.actor;
    turn.attitude = combatant.getFlag("world", "attitude") || "active";
    turn.isPlayer = actor?.hasPlayerOwner ?? false;
    turn.isNpc = !!actor && !actor.hasPlayerOwner;
    turn.owner = combatant.isOwner;
    turn.defeated = combatant.isDefeated;
    turn.hasRolled = combatant.initiative !== null;
    turn.hasResource = combatant.resource !== null && combatant.resource !== undefined;
    turn.effects = (turn.effects?.icons ?? []).map(e => e.img);
    return turn;
  }

  _onRender(context, options) {
    super._onRender(context, options);
    this.element.querySelectorAll("[data-attitude]").forEach(el => {
      el.addEventListener("click", this._setStatut.bind(this));
    });
  }

  /**
   * @description Use to put an attitude to an actor
   * @param {*} event 
   */

  async _setStatut(event) {
    event.preventDefault();
    event.stopPropagation();
    const btn = event.currentTarget;
    const attitude = btn.dataset.attitude;
    const li = btn.closest(".combatant");
    const combat = this.viewed;
    const combatant = combat.combatants.get(li.dataset.combatantId);
    let flag = combatant.getFlag("world", "attitude");
    if (flag == attitude) {
      await combatant.setFlag("world", "attitude", null);
    } else {
      await combatant.setFlag("world", "attitude", attitude);
    }

    let actor = await game.actors.get(combatant.actorId)
    // combatant attitude set
  }

  /**
   * Supprime un combatant du combat en cours.
   * @param {PointerEvent} event
   * @param {HTMLElement} target
   */
  static async #onDeleteCombatant(event, target) {
    const combatantId = target.closest("[data-combatant-id]")?.dataset?.combatantId
    const combatant = this.viewed?.combatants.get(combatantId)
    if (!combatant) return
    return combatant.delete()
  }

  /**
   * Supprime le combat affiché (bouton poubelle de l'en-tête).
   * @param {PointerEvent} event
   * @param {HTMLElement} target
   */
  static async #onDeleteCombat() {
    const combat = this.viewed
    if (!combat) return
    const deleted = await combat.delete()
    this.viewed = null
    return deleted
  }
}

export class VermineCombatant extends Combatant {
  constructor(data, context) {
    super(data, context)
    if (this.token) {
      this.attitude = this.token.flags.world?.attitude || "active"
    } else {
      this.attitude = "active"
    }
  }
  _getInitiativeFormula() {
    // Personnage : jet de Réaction (Réflexes + Vigilance) contre la
    // Difficulté du statut de combat — les Réussites = valeur de Réaction.
    // PNJ et Créature : n'ont pas de caractéristiques/compétences — ils
    // roulent leur pool de Réaction (computed.reaction) contre la même
    // Difficulté, majoré de leur bonus de Réaction.
    const actor = this.actor
    if ( actor?.type === 'npc' || actor?.type === 'creature' ) {
      const reaction = parseInt(actor.system?.computed?.reaction, 10) || 0
      const difficulty = parseInt(actor.system?.combatStatus?.difficulty, 10) || 7
      const bonus = parseInt(actor.system?.computed?.reactionBonus, 10) || 0
      return `${reaction}d10cs>=${difficulty}+${bonus}`
    }
    return String(CONFIG.Combat.initiative.formula || game.system.initiative)
  }
}