import { ChatUtility } from "../chat-utility.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { RdDInitiative } from "./initiative.mjs";
import { PasseArme } from "./passe-arme.mjs";

/* -------------------------------------------- */
const premierRoundInit = [
  { pattern: 'hast' },
  { pattern: 'lance' },
  { pattern: 'baton' },
  { pattern: 'doubledragonne' },
  { pattern: 'esparlongue' },
  { pattern: 'epeedragonne' },
  { pattern: 'epeebatarde' },
  { pattern: 'epeecyane' },
  { pattern: 'epeesorde' },
  { pattern: 'grandehache' },
  { pattern: 'bataille' },
  { pattern: 'epeegnome' },
  { pattern: 'masse' },
  { pattern: 'gourdin' },
  { pattern: 'fleau' },
  { pattern: 'dague' },
  { pattern: 'autre' },
];

/* -------------------------------------------- */
export class RdDCombat extends Combat {

  static init() {
    /* -------------------------------------------- */
    Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombat.pushInitiativeOptions(html, options); });
    Hooks.on("updateCombat", (combat, change, options, userId) => { PasseArme.onUpdateCombat(combat, change, options, userId) });
    Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() })
    Hooks.on("deleteCombat", (combat, html, id) => { combat.onDeleteCombat() })

    for (let i = 0.0; i < premierRoundInit.length; i++) {
      premierRoundInit[i].init = 5.99 - i / 100
    }
  }


  static getCombatant(actorId, tokenId) {
    return game.combat?.combatants.find(it => it.actorId == actorId &&
      it.tokenId == tokenId
    )
  }

  static getRangInitiativeCombatant(actorId, tokenId) {
    const combatant = RdDCombat.getCombatant(actorId, tokenId)
    return combatant?.system.init?.rang
  }
  /* -------------------------------------------- */
  async nextRound() {
    await this.finDeRound();
    return await super.nextRound();
  }

  /* -------------------------------------------- */
  async onPreDeleteCombat() {
    if (Misc.isFirstConnectedGM()) {
      await this.finDeRound({ terminer: true })
      ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
      game.messages.filter(m => ChatUtility.getMessageData(m, 'rollData') != undefined && ChatUtility.getMessageData(m, 'rollData') != undefined)
        .forEach(it => it.delete())
      RdDEmpoignade.deleteAllEmpoignades()
    }
  }
  async onDeleteCombat() {
    if (Misc.isFirstConnectedGM()) {
      if (game.combats.size <= 1) {
        game.actors.forEach(actor => actor.resetItemUse())
      }
    }
  }

  /* -------------------------------------------- */
  async finDeRound(options = { terminer: false }) {
    this.combatants.map(it => RdDCombat.getActorCombatant(it, { warning: false }))
      .filter(it => it != undefined)
      .forEach(async actor => {
        await actor.finDeRound(options)
        await actor.resetItemUse()
      })
  }

  static getActorCombatant(combatant, options = { warning: true }) {
    if (!combatant.actor) {
      if (options.warning) {
        ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`)
      }
      return undefined
    }
    else if (!combatant.actor.isActorCombat()) {
      if (options.warning) {
        ui.notifications.warn(`L'acteur ${combatant.name} ne peut pas combattre!`)
      }
      return undefined
    }
    return combatant.actor
  }

  static bonusArme(arme) {
    return (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
  }

  static etatGeneral(actor) {
    return actor.getEtatGeneral() ?? 0;
  }


  /** ***********************************************************************************
   * @override lance l'initiative de plusieurs combattants (tous/ous les PNJs) en une fois
   */
  async rollInitiative(ids, messageOptions = {}) {
    console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions)
    ids = typeof ids === "string" ? [ids] : ids
    Promise.all(ids.map(id => this.rollInitRdD(id, undefined, messageOptions)))
    return this
  }

  async rollInitRdD(id, formule, messageOptions = {}) {
    const combatant = this.combatants.get(id);
    const actor = RdDCombat.getActorCombatant(combatant)
    if (actor) {
      formule = formule ?? RdDCombat.getFirstInitRollFormula(actor)
      const init = await RdDInitiative.roll(formule)

      await this.updateEmbeddedDocuments("Combatant", [{
        _id: combatant.id,
        initiative: init.init, 'system.init': init
      }])

      // Send a chat message
      let rollMode = messageOptions.rollMode || game.settings.get("core", "messageMode");
      let messageData = foundry.utils.mergeObject({
        speaker: {
          scene: canvas?.scene?.id,
          actor: combatant.actor?.id,
          token: combatant.token.id,
          alias: combatant.token?.name,
          sound: CONFIG.sounds.dice,
        },
        flavor: `${combatant.token?.name} a une initiative de ${init.value} : ${messageOptions.info}<br>`
      },
        messageOptions);
      init.roll.toMessage(messageData, { rollMode, create: true });

      RdDCombat.processPremierRoundInit();
    }
    return this;
  }

  static getFirstInitRollFormula(actor) {
    const actions = actor.listActions()
    if (actions.length > 0) {
      const action = actions[0]
      const init = RdDCombat.getInitData(actor, action)
      const ajustement = RdDCombat.bonusArme(action.arme) + RdDCombat.etatGeneral(actor)
      return RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);
    }

    return RdDInitiative.formule('autre', 10, 0, actor.getEtatGeneral() ?? 0);
  }

  /* -------------------------------------------- */
  static processPremierRoundInit() {
    // Check if we have the whole init !
    if (Misc.isFirstConnectedGM() && game.combat.current.round == 1) {
      let initMissing = game.combat.combatants.find(it => !it.initiative);
      if (!initMissing) { // Premier round !
        for (let combatant of game.combat.combatants) {
          if (combatant.initiativeData?.arme?.type == "arme") {
            // TODO: get init data premier round
            const action = combatant.initiativeData.arme;
            const fromArme = Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround)
            const initData = premierRoundInit.find(it => fromArme.includes(initData.pattern))
            if (initData) {
              let msg = `<h4>L'initiative de ${combatant.actor.getAlias()} a été modifiée !</h4>
                      <hr>
                      <div>
                        Etant donné son ${action.name}, son initative pour ce premier round est désormais de ${initData.init}.
                      </div>`
              ChatMessage.create({ content: msg })
              game.combat.setInitiative(combatant.id, initData.init)
            }
          }
        }
      }
    }
  }

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

  static applyInitiativeCommand(combatantId, command, commandValue) {
    switch (command) {
      case 'delta': return RdDCombat.incDecInit(combatantId, commandValue);
      case 'autre': return RdDCombat.rollInitiativeAction(combatantId,
        { name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
    }
  }

  static async incDecInit(combatantId, incDecValue) {
    const combatant = game.combat.combatants.get(combatantId)
    if (combatant?.initiative && incDecValue != 0) {
      const value = combatant.system.init.value + incDecValue
      const newInit = combatant.initiative + incDecValue / 100;
      await game.combat.updateEmbeddedDocuments("Combatant", [{
        _id: combatantId,
        initiative: newInit,
        'system.init.value': value,
        'system.init.init': newInit,
      }])
    }
  }

  /* -------------------------------------------- */
  static pushInitiativeOptions(html, options) {
    for (let i = 0; i < options.length; i++) {
      let option = options[i]
      if (option.name == 'COMBAT.CombatantReroll') { // Replace !
        option.name = "Sélectionner l'initiative..."
        option.condition = true
        option.icon = '<i class="far fa-question-circle"></i>'
        option.callback = target => {
          RdDCombat.displayInitiativeMenu(html, target.data('combatant-id'))
        }
      }
    }
    options = [
      { name: "Incrémenter initiative", condition: true, icon: '<i class="fa-solid fa-plus"></i>', callback: target => { RdDCombat.incDecInit(target.data('combatant-id'), +1); } },
      { name: "Décrémenter initiative", condition: true, icon: '<i class="fa-solid fa-minus"></i>', callback: target => { RdDCombat.incDecInit(target.data('combatant-id'), -1); } }
    ].concat(options);
  }

  /* -------------------------------------------- */
  static async rollInitiativeAction(combatantId, action) {
    const combatant = game.combat.combatants.get(combatantId)
    const actor = RdDCombat.getActorCombatant(combatant)

    if (actor == undefined) { return }

    const init = RdDCombat.getInitData(actor, action)
    const ajustement = RdDCombat.bonusArme(actor, action.arme) + RdDCombat.etatGeneral(actor)
    const formule = RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);

    await game.combat.rollInitRdD(combatantId, formule, init);
    combatant.initiativeData = { action, formule } // pour reclasser l'init au round 0
  }

  static getInitData(actor, action) {
    if (actor.getSurprise() == "totale") { return { phase: RdDInitiative.PHASES.totale, info: "Surprise Totale", carac: 0, niveau: 0 } }
    if (actor.getSurprise() == "demi") { return { phase: RdDInitiative.PHASES.demi, info: "Demi Surprise", carac: 0, niveau: 0 } }
    if (action.action == 'autre') { return { phase: RdDInitiative.PHASES.autre, info: "Autre Action", carac: 0, niveau: 0 } }
    if (action.action == 'possession') { return { phase: RdDInitiative.PHASES.possession, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
    if (action.action == 'haut-reve') { return { phase: RdDInitiative.PHASES.draconic, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }

    const comp = action.comp
    return {
      phase: RdDInitiative.phaseArme(comp?.system.categorie, action.arme),
      info: action.label,
      carac: actor.getCaracInit(comp),
      niveau: comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(action.main) ? -8 : -6)
    }
  }

  /* -------------------------------------------- */
  static displayInitiativeMenu(html, combatantId) {
    const combatant = game.combat.combatants.get(combatantId)
    const actor = RdDCombat.getActorCombatant(combatant, { warning: false })
    if (actor) {
      const actions = RdDCombat.listActionsActorCombatant(actor)
      // Build the relevant submenu
      const menuItems = actions.map(action => {
        return {
          name: action.system.competence,
          icon: "<i class='fas fa-dice-d6'></i>",
          callback: target => { RdDCombat.rollInitiativeAction(combatantId, action) }
        }
      })
      if (menuItems.length > 0) {
        new ContextMenu(html, ".directory-list", menuItems).render();
      }
    }
  }

  /* -------------------------------------------- */
  static listActionsActorCombatant(actor) {
    const possessions = actor.listActionsPossessions()
    const actions = possessions.length > 0
      ? possessions
      : actor.listActions()

    return Misc.indexed(actions)
  }
}
