import { SYSTEM } from "../config/system.mjs"
import { log } from "../utils.mjs"

/* -------------------------------------------- */
export class LethalFantasyCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {

  static PARTS = {
    "header": {
      "template": "systems/fvtt-lethal-fantasy/templates/combat-tracker-header-v2.hbs"
    },
    "tracker": {
      "template": "systems/fvtt-lethal-fantasy/templates/combat-tracker-v2.hbs"
    },
    "footer": {
      "template": "systems/fvtt-lethal-fantasy/templates/combat-tracker-footer-v2.hbs"
    }
  }

  static DEFAULT_OPTIONS = foundry.utils.mergeObject(super.DEFAULT_OPTIONS, {
    actions: {
      initiativePlus: LethalFantasyCombatTracker.#initiativePlus,
      initiativeMinus: LethalFantasyCombatTracker.#initiativeMinus,
      rollMonsterProgression: LethalFantasyCombatTracker.#rollMonsterProgression,
    },
  });

  async _prepareContext(options) {
    let data = await super._prepareContext(options);
    log("Combat Tracker Data", data);
    /*for (let u of data.turns) {
      let c = game.combat.combatants.get(u.id);
      u.progressionCount = c.system.progressionCount
      u.isMonster = c.actor.type === "monster"
    }
    log("Combat Data", data);*/
    return data;
  }

  static async #initiativePlus(ev) {
    ev.preventDefault();
    let cId = ev.target.closest(".combatant").dataset.combatantId;
    let c = game.combat.combatants.get(cId);
    await c.update({ 'initiative': c.initiative + 1 });
  }

  static async #initiativeMinus(ev) {
    ev.preventDefault();
    let cId = ev.target.closest(".combatant").dataset.combatantId;
    let c = game.combat.combatants.get(cId);
    let newInit = Math.max(c.initiative - 1, 0);
    await c.update({ 'initiative': newInit });
  }

  /**
   * Roll progression dice for all monster combatants that are eligible this round.
   * @param {Event} ev Click event.
   */
  static async #rollMonsterProgression(ev) {
    ev.preventDefault();
    await game.combat.rollMonsterProgression();
  }
}

export class LethalFantasyCombat extends Combat {

  /**
 * Return the Array of combatants sorted into initiative order, breaking ties alphabetically by name.
 * @returns {Combatant[]}
 */
  setupTurns() {
    log("Setup Turns....");
    this.turns ||= [];

    // Determine the turn order and the current turn
    const turns = this.combatants.contents.sort(this.sortCombatantsLF);
    if (this.turn !== null) this.turn = Math.clamp(this.turn, 0, turns.length - 1);

    // Update state tracking
    let c = turns[this.turn];
    this.current = this._getCurrentState(c);
    if (!this.previous) this.previous = this.current;

    // Return the array of prepared turns
    return this.turns = turns;
  }

  async startCombat() {
    this._playCombatSound("startEncounter")
    const updateData = { round: 0, turn: 0 }
    Hooks.callAll("combatStart", this, updateData)
    await this.update(updateData)
    return this
  }

  async rollInitiative(ids, options) {
    ids = typeof ids === "string" ? [ids] : ids;

    for (let cId of ids) {
      const c = this.combatants.get(cId);
      const playerOwner = game.users.find(u => u.active && !u.isGM && u.character?.id === c.actor.id);
      if (game.user.isGM && playerOwner) {
        game.socket.emit(`system.${SYSTEM.id}`, { type: "rollInitiative", userId: playerOwner.id, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
      } else {
        await c.actor.system.rollInitiative(this.id, c.id);
      }
    }

    return this;
  }

  /** Roll progression dice for all eligible monster combatants this round. Called manually by the GM. */
  async rollMonsterProgression() {
    const currentRound = this.round;
    const monsters = this.combatants.filter(c => c.actor?.type === "monster" && !c.isDefeated);

    if (monsters.length === 0) {
      ui.notifications.warn("No monsters in combat.");
      return;
    }

    let rolled = 0;
    for (let c of monsters) {
      if (c.initiative !== null && currentRound >= c.initiative) {
        await c.actor.system.rollProgressionDice(this.id, c.id);
        rolled++;
      }
    }

    if (rolled === 0) {
      const earliest = monsters.reduce((min, c) => (c.initiative !== null && c.initiative < min) ? c.initiative : min, Infinity);
      if (earliest === Infinity) {
        ui.notifications.warn("Monsters have no initiative set. Roll initiative first.");
      } else {
        ui.notifications.info(`No monsters act yet — earliest monster initiative is ${earliest} (current round: ${currentRound}).`);
      }
    } else {
      this._monsterProgressionRolledRound = currentRound;
    }
  }

  async resetProgression(cId) {
    let c = this.combatants.get(cId);
    await c.update({ 'system.progressionCount': 0 });
  }

  async setCasting(cId) {
    let c = this.combatants.get(cId);
    await c.setFlag(SYSTEM.id, "casting", true);
  }

  async resetCasting(cId) {
    let c = this.combatants.get(cId);
    await c.setFlag(SYSTEM.id, "casting", false);
  }

  isCasting(cId) {
    let c = this.combatants.get(cId);
    return c.getFlag(SYSTEM.id, "casting");
  }

  async nextTurn() {
    let turn = this.turn ?? -1;
    let skipDefeated = this.settings.skipDefeated;

    // Determine the next turn number
    let next = null;
    for (let [i, t] of this.turns.entries()) {
      if (i <= turn) continue;
      if (skipDefeated && t.isDefeated) continue;
      next = i;
      break;
    }

    // Maybe advance to the next round
    let round = this.round;
    if ((this.round === 0) || (next === null) || (next >= this.turns.length)) {
      return this.nextRound();
    }

    // Update the document, passing data through a hook first
    const updateData = { round, turn: next };
    const updateOptions = { advanceTime: CONFIG.time.turnTime, direction: 1 };
    Hooks.callAll("combatTurn", this, updateData, updateOptions);
    return this.update(updateData, updateOptions);
  }

  async nextRound() {
    this.turnsDone = false

    let turn = this.turn === null ? null : 0; // Preserve the fact that it's no-one's turn currently.

    let advanceTime = Math.max(this.turns.length - this.turn, 0) * CONFIG.time.turnTime;
    advanceTime += CONFIG.time.roundTime;
    let nextRound = this.round + 1;

    let initOK = true;
    for (let c of this.combatants) {
      if (c.initiative === null) {
        initOK = false;
        break;
      }
    }
    if (!initOK) {
      ui.notifications.error("All combatants must have initiative rolled before the round can advance.");
      return this;
    }

    // Warn if eligible monsters have not rolled progression dice this round
    const eligibleMonsters = this.combatants.filter(
      c => c.actor?.type === "monster" && !c.isDefeated && c.initiative !== null && this.round >= c.initiative
    );
    if (eligibleMonsters.length > 0 && this._monsterProgressionRolledRound !== this.round) {
      const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-lethal-fantasy/templates/dialogs/combat-monsters-not-rolled.hbs", {message: game.i18n.localize("LETHALFANTASY.Combat.monstersNotRolledMsg")})
      const proceed = await foundry.applications.api.DialogV2.confirm({
        window: { title: game.i18n.localize("LETHALFANTASY.Combat.monstersNotRolledTitle") },
        content,
        yes: { label: game.i18n.localize("LETHALFANTASY.Combat.proceedYes") },
        no: { label: game.i18n.localize("LETHALFANTASY.Combat.proceedNo") },
        rejectClose: false,
      });
      if (!proceed) return this;
    }

    for (let c of this.combatants) {
      if (nextRound >= c.initiative) {
        if (c.actor.type === "monster") continue;  // Monsters roll manually via the "Roll Monsters" button
        const playerOwner = game.users.find(u => u.active && !u.isGM && u.character?.id === c.actor.id);
        if (game.user.isGM && playerOwner) {
          game.socket.emit(`system.${SYSTEM.id}`, { type: "rollProgressionDice", userId: playerOwner.id, progressionCount: c.system.progressionCount + 1, actorId: c.actor.id, combatId: this.id, combatantId: c.id });
        } else {
          await c.actor.system.rollProgressionDice(this.id, c.id);
        }
      }
    }

    // Update the document, passing data through a hook first
    const updateData = { round: nextRound, turn };
    const updateOptions = { advanceTime, direction: 1 };
    Hooks.callAll("combatRound", this, updateData, updateOptions);
    return this.update(updateData, updateOptions);
  }


  sortCombatantsLF(a, b) {
    return a.initiative - b.initiative;
  }
}
