import { STATUSES } from "../settings/status-effects.js";
import { ITEM_TYPES, renderTemplate } from "../constants.js";
import { ChatUtility } from "../chat-utility.js";
import { RdDRollResult } from "../rdd-roll-result.js";
import { RdDRoll } from "../rdd-roll.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import { RdDInitiative } from "./initiative.mjs";
import { RdDCombat } from "./rdd-combat.js";

/* -------------------------------------------- */
export class RdDEmpoignade {

  /* -------------------------------------------- */
  static init() {
  }

  /* -------------------------------------------- */
  static getActorEmpoignade(actorId) {
    const actor = game.actors.get(actorId)
    if (actor == undefined) {
      ui.notifications.warn(`Impossible de retrouver l'acteur ${actorId}, l'empoignade ne peut pas être continuée.`)
    }
    return actor
  }

  static isCombatantEmpoignade(actorId, tokenId) {
    const combatant = RdDCombat.getCombatant(actorId, tokenId)
    return combatant && RdDInitiative.PHASES.empoignade.rang == combatant?.system.init?.rang
  }

  static async ajustementEmpoignade(attacker, defender, adjust = 1) {
    let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
    if (empoignade?.system.empoigneurid == defender.id) {
      empoignade = RdDEmpoignade.getEmpoignade(defender, attacker)
      return await RdDEmpoignade.$ajustementEmpoignade(empoignade, defender, attacker, - adjust);
    }
    return await RdDEmpoignade.$ajustementEmpoignade(empoignade, attacker, defender, adjust);
  }

  static async $ajustementEmpoignade(empoignade, attacker, defender, adjust) {
    const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16);
    const empFin = (empoignade?.system.pointsemp ?? 0) + adjust
    if (empoignade) {
      empoignade.system.pointsemp = empFin;
      await RdDEmpoignade.$updateEtatEmpoignade(empoignade, attacker, defender);
    }
    else {
      await RdDEmpoignade.$createEtatEmpoignade({
        name: `Empoignade de ${attacker.name} sur ${defender.name}`,
        type: ITEM_TYPES.empoignade,
        system: {
          description: "",
          empoignadeid: empId,
          empoigneurid: attacker.id,
          empoigneid: defender.id,
          pointsemp: empFin,
          empoigneurname: attacker.name,
          empoignename: defender.name
        }
      }, attacker, defender);
    }
    if (adjust != 0 && empFin == 2) {
      await RdDEmpoignade.proposerEntrainerAuSol(attacker, defender, empoignade);
    }
    const result = RdDEmpoignade.getEmpoignadeById(defender, empId);
    const defGrappled = result.system.pointsemp == (result.system.empoigneid == defender.id ? 2 : -2);
    const attGrappled = result.system.pointsemp == (result.system.empoigneurid == attacker.id ? -2 : 2);
    const grappling = Math.abs(result.system.pointsemp) > 0;
    await defender.setEffect(STATUSES.StatusGrappling, grappling && !defGrappled);
    await attacker.setEffect(STATUSES.StatusGrappling, grappling && !attGrappled);
    await defender.setEffect(STATUSES.StatusGrappled, defGrappled);
    await attacker.setEffect(STATUSES.StatusGrappled, attGrappled);
    return result
  }

  /* -------------------------------------------- */
  static registerChatCallbacks(html) {
    $(html).on("click", '.defense-empoignade-cac', event => {
      const chatMessage = ChatUtility.getChatMessage(event);
      const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
      let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
      RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
    })
    $(html).on("click", '.defense-empoignade-esquive', event => {
      const chatMessage = ChatUtility.getChatMessage(event);
      const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
      let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
      RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
    })
    $(html).on("click", '.empoignade-poursuivre', event => {
      const attacker = RdDEmpoignade.getActorFromEventTag(event, 'data-attackerId')
      const defender = RdDEmpoignade.getActorFromEventTag(event, 'data-defenderId')
      if (attacker && defender) {
        RdDEmpoignade.onAttaqueEmpoignadeValidee(attacker, defender)
      }
    })
    $(html).on("click", '.empoignade-entrainer-sol', event => {
      const chatMessage = ChatUtility.getChatMessage(event);
      const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
      RdDEmpoignade.entrainerAuSol(rollData)
      ChatUtility.remover(chatMessage)()
    })
    $(html).on("click", '.empoignade-projeter-sol', event => {
      const chatMessage = ChatUtility.getChatMessage(event);
      const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
      RdDEmpoignade.projeterAuSol(rollData)
      ChatUtility.remover(chatMessage)()
    })
    $(html).on("change", '.empoignade-perte-endurance', event => {
      const chatMessage = ChatUtility.getChatMessage(event);
      const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
      if (event.currentTarget.value && event.currentTarget.value != "none") {
        RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
        ChatUtility.remover(chatMessage)()
      }
    })
  }

  static getActorFromEventTag(event, tag) {
    return RdDEmpoignade.getActorEmpoignade(event.currentTarget.attributes[tag]?.value ?? '<inconnu>')
  }

  /* -------------------------------------------- */
  static checkEmpoignadeEnCours(actor) {
    // TODO: autoriser la perception? la comédie/séduction?
    if (RdDEmpoignade.isEmpoignadeEnCours(actor)) {
      ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.")
      return true
    }
    return false
  }

  /* -------------------------------------------- */
  static $storeRollEmpoignade(msg, rollData) {
    ChatUtility.setMessageData(msg, "empoignade-roll-data", RdDEmpoignade.$reduceActorToIds(rollData))
  }

  static $reduceActorToIds(rollData) {
    rollData.attacker = { id: rollData.attacker.id }
    rollData.defender = { id: rollData.defender.id }
    return rollData
  }

  /* -------------------------------------------- */
  static $readRollEmpoignade(msg) {
    return RdDEmpoignade.$replaceIdsWithActors(ChatUtility.getMessageData(msg, 'empoignade-roll-data'))
  }

  static $replaceIdsWithActors(rollData) {
    rollData.attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    rollData.defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
    return rollData
  }

  /* -------------------------------------------- */
  static isEmpoignadeEnCours(actor) {
    return actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.pointsemp > 0)
  }

  /* -------------------------------------------- */
  static getEmpoignadeById(actor, id) {
    return actor?.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
  }

  /* -------------------------------------------- */
  static getEmpoignade(attacker, defender) {
    if (attacker && defender) {
      const empoignade = attacker.itemTypes[ITEM_TYPES.empoignade].find(it =>
        (it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
        (it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
      )
      if (empoignade) {
        return foundry.utils.deepClone(empoignade)
      }
    }
    return undefined
  }

  /* -------------------------------------------- */
  static getMalusTaille(emp, attacker, defender) {
    // Si pas empoigné, alors 0
    if (emp.system.pointsemp == 0) {
      return 0
    }
    // p135: Malus de -1 par point de taille de différence de taille au delà de 1 (donc -2 pour une différence de 3, ...)
    const diffTaille = attacker.system.carac.taille.value - defender.system.carac.taille.value;
    const diffTailleAbs = Math.abs(diffTaille)
    const signDiff = diffTaille > 0 ? 1 : -1

    if (diffTailleAbs > 2) {
      return signDiff * (diffTailleAbs - 1)
    }
    return 0
  }

  static isActionAutorisee(mode, attacker, defender) {
    if (!defender || !attacker) {
      return false
    }

    const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker
    if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
      ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
      return false
    }
    return true
  }

  static isActionDefenseur(mode) {
    switch (mode) {
      case "liberer":
      case "contrer-empoigner":
        return true;
    }
    return false;
  }

  /* -------------------------------------------- */
  static async onAttaqueEmpoignade(attacker, defender) {
    if (!RdDEmpoignade.isActionAutorisee("empoigner", attacker, defender)) {
      return
    }
    let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
    const isNouvelle = empoignade == undefined;
    empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
    if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) {
      await ChatMessage.create(ChatUtility.adaptVisibility(
        { content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.hbs`, { attacker: attacker, defender: defender }) },
        { actor: attacker }
      ))
    } else {
      await this.onAttaqueEmpoignadeValidee(attacker, defender)
    }
  }

  /* -------------------------------------------- */
  static async onAttaqueEmpoignadeValidee(attacker, defender) {
    let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
    const isNouvelle = empoignade == undefined;
    empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))

    let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"

    if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
      return
    }

    let rollData = {
      mode, empoignade, attacker, defender,
      isEmpoignade: true,
      competence: attacker.getCompetenceCorpsACorps(),
      selectedCarac: attacker.system.carac.melee,
      malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
    }
    if (attacker.isCreatureOuEntite()) {
      MappingCreatureArme.setRollDataCreature(rollData)
    }
    if (empoignade.system.pointsemp >= 2) {
      await RdDEmpoignade.proposerEntrainerAuSol(attacker, defender, empoignade)
    } else {
      await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle)
    }
  }

  static async proposerEntrainerAuSol(attacker, defender, empoignade) {
    if (!empoignade.system.ausol) {
      const mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
      const rollData = {
        mode, empoignade, attacker, defender,
        isEmpoignade: true,
        competence: attacker.getCompetenceCorpsACorps(),
        selectedCarac: attacker.system.carac.melee,
        malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
      }
      const msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.hbs');
      RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
    }
  }

  /* -------------------------------------------- */
  static async onAttaqueEmpoignadeFromItem(empoignade) {
    const attacker = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
    const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
    if (attacker && defender) {
      await this.onAttaqueEmpoignadeValidee(attacker, defender)
    }
  }

  static async onImmobilisation(attacker, empoignade) {
    const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
    if (defender) {
      const empDefenseur = defender.itemTypes[ITEM_TYPES.empoignade]
        .find(it => it.system.empoignadeid == empoignade.system.empoignadeid);
      await defender.updateEmbeddedDocuments('Item', [{
        _id: empDefenseur.id,
        'system.immobilise': true
      }])
      const rollData = {
        mode: "immobilise",
        empoignade, attacker, defender,
        isEmpoignade: true,
        competence: attacker.getCompetenceCorpsACorps()
      }
      const msg = await ChatMessage.create({
        whisper: ChatUtility.getOwners(attacker),
        content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.hbs`, rollData)
      })
      RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
    }
  }

  /* -------------------------------------------- */
  static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) {
    const dialog = await RdDRoll.create(attacker, rollData,
      { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs' },
      {
        name: 'jet-empoignade',
        label: 'Empoigner',
        callbacks: [{ action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, isNouvelle) },]
      });
    dialog.render(true);
  }

  /* -------------------------------------------- */
  static async $onRollEmpoignade(rollData, isNouvelle = false) {
    const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)

    if (attacker && defender) {
      if (rollData.rolled.isSuccess && isNouvelle) {
        RdDEmpoignade.$createEtatEmpoignade(rollData.empoignade)
      }

      rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
      if (rollData.rolled.isPart) {
        rollData.particuliere = "finesse";
      }
      let msg = await RdDRollResult.displayRollData(rollData, defender, 'chat-empoignade-resultat.hbs');
      RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
    }
  }

  /* -------------------------------------------- */
  static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
    const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
    if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
      return
    }
    let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)

    if (!empoignade) {
      ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!")
      return
    }

    empoignade = foundry.utils.deepClone(empoignade)
    let defenderRoll = {
      mode, attacker, defender, empoignade, attackerRoll,
      diffLibre: attackerRoll.diffLibre,
      attaqueParticuliere: attackerRoll.particuliere,
      competence: defender.getCompetence(competenceName),
      surprise: defender.getSurprise(true),
      carac: defender.system.carac,
      selectedCarac: defender.system.carac[carac],
      malusTaille: RdDEmpoignade.getMalusTaille(empoignade, defender, attacker),
      show: {}
    };
    await RdDEmpoignade.$rollDefenseEmpoignade(defender, defenderRoll);
  }

  /* -------------------------------------------- */
  static async $rollDefenseEmpoignade(defender, defenderRoll) {
    const dialog = await RdDRoll.create(defender, defenderRoll,
      { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.hbs' },
      {
        name: 'empoignade',
        label: 'Contrer',
        callbacks: [
          { action: async (r) => await RdDEmpoignade.$onRollContrerLiberer(r) }
        ]
      }
    );
    dialog.render(true);
  }

  /* -------------------------------------------- */
  static async $onRollContrerLiberer(rollData) {
    const empoignade = rollData.empoignade

    if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
      empoignade.system.pointsemp++
      RdDEmpoignade.$updateEtatEmpoignade(empoignade)
    }
    if (rollData.mode == "contrer-liberer" && !rollData.rolled.isSuccess) {
      empoignade.system.pointsemp--
      RdDEmpoignade.$updateEtatEmpoignade(empoignade)
    }

    await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.hbs')
    if (empoignade.system.pointsemp >= 2) {
      let msg = await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.hbs');
      RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
    }
  }

  /* -------------------------------------------- */
  static async $createEtatEmpoignade(empoignade) {
    console.log("CREATE Empoignade", empoignade)

    const attacker = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
    const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)

    if (attacker && defender) {
      // Creer l'empoignade sur attaquant et defenseur
      await attacker.creerObjetParMJ(empoignade)
      await defender.creerObjetParMJ(empoignade)
    }
  }

  /* -------------------------------------------- */
  static async $updateEtatEmpoignade(empoignade, attacker, defender) {
    attacker = attacker ?? RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
    defender = defender ?? RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)

    if (attacker && defender) {
      const belligerants = [attacker, defender]
      const removeEmp = empoignade.system.pointsemp == 0

      if (removeEmp) {
        const emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
        return await attacker.deleteEmbeddedDocuments('Item', [emp.id])
      }
      else {
        await Promise.all(
          belligerants.map(async belligerant => {
            const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
            return await belligerant.updateEmbeddedDocuments('Item', [{
              _id: emp.id,
              "system.pointsemp": empoignade.system.pointsemp,
              "system.ausol": empoignade.system.ausol
            }])
          }))
      }
    }
  }

  /* -------------------------------------------- */
  static async $deleteEmpoignade(empoignade) {
    console.log("DELETE Empoignade", empoignade)

    const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
    if (defender) {
      const emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
      await defender.deleteEmbeddedDocuments('Item', [emp.id])
    }
  }

  /* -------------------------------------------- */
  static async entrainerAuSol(rollData) {
    const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
    if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
      return
    }
    const empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)

    empoignade.system.ausol = true
    await this.$updateEtatEmpoignade(empoignade)

    await attacker.setEffect(STATUSES.StatusProne, true);
    await defender.setEffect(STATUSES.StatusProne, true);

    const msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.hbs');
    RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
  }

  /* -------------------------------------------- */
  static async projeterAuSol(rollData) {
    const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
    if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
      return
    }
    let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)

    await defender.setEffect(STATUSES.StatusProne, true);
    await this.$deleteEmpoignade(empoignade)

    let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.hbs');
    RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
  }

  /* -------------------------------------------- */
  static async perteEndurance(rollData, perteMode) {
    const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
    const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
    if (perteMode == "none" || !RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
      return
    }
    const perteEndurance = await RdDEmpoignade.$calcPerteEnd(perteMode, defender.system.sante.endurance.value)
    await defender.santeIncDec("endurance", -perteEndurance)
    await RdDRollResult.displayRollData({ attacker, defender, perteEndurance },
      attacker,
      'chat-empoignade-perte-endurance.hbs')
  }

  static async $calcPerteEnd(perteMode, endValue) {
    switch (perteMode) {
      case "none": return 0
      case "end0": return endValue
      case "end1": return (endValue - 1)
      case "3/4": return Math.floor(3 * endValue / 4)
      case "1/2": return Math.floor(endValue / 2)
      case "1/4": return Math.floor(endValue / 4)
    }
    const rolled = await (new Roll(perteMode).evaluate())
    return rolled.total
  }
  /* -------------------------------------------- */
  static async deleteAllEmpoignades() {
    for (let actor of game.actors) {
      let empIds = actor.itemTypes["empoignade"].map(it => it.id)
      await actor.deleteEmbeddedDocuments('Item', empIds)
    }
  }

  /* -------------------------------------------- */
  static async deleteLinkedEmpoignade(actorId, empoignade) {
    const actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid
    const actor = RdDEmpoignade.getActorEmpoignade(actorDeleteId)
    const empoignadeOpposant = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
    if (actor && empoignadeOpposant) {
      await actor.deleteEmbeddedDocuments('Item', [empoignadeOpposant.id])
    }
  }

  /* -------------------------------------------- */
  static async createEmpoignade(attacker, defender) {
    return await Item.create({
      name: "Empoignade de " + attacker.name + ' sur ' + defender.name,
      type: ITEM_TYPES.empoignade,
      system: {
        description: "",
        empoignadeid: foundry.utils.randomID(16),
        empoigneurid: attacker.id,
        empoigneid: defender.id,
        pointsemp: 0,
        empoigneurname: attacker.name,
        empoignename: defender.name
      }
    },
      {
        temporary: true
      })
  }
}