import { ChatUtility } from "../chat-utility.js"
import RollDialog, { ALL_ROLL_TYPES } from "./roll-dialog.mjs"
import { RdDCarac } from "../rdd-carac.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { RdDResolutionTable } from "../rdd-resolution-table.js"
import { RDD_CONFIG, renderTemplate } from "../constants.js"
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"
import { RollTypeMeditation } from "./roll-type-meditation.mjs"
import { PART_DEFENSE } from "./roll-part-defense.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RdDRollTables } from "../rdd-rolltables.js"
import { PasseArme } from "../combat/passe-arme.mjs"
import { RdDEmpoignade } from "../combat/rdd-empoignade.js"
import { RdDPossessionV2 } from "../combat/rdd-possession-v2.mjs"
import { Misc } from "../misc.js"
import { RollBasicParts } from "./roll-basic-parts.mjs"
import { Apprecier } from "../moral/apprecier.mjs"
import { Remote } from "../remote.mjs"

export default class ChatRollResult {
  static init() {
    ChatRollResult.instance = new ChatRollResult()

    Hooks.on('renderChatLog', (log, html, chatLog) => ChatRollResult.instance.chatListeners(html))
  }

  static onReady() {
    foundry.applications.handlebars.loadTemplates({
      'partial-appel-chance': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appel-chance.hbs',
      'partial-apprecier': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-apprecier.hbs',
      'partial-appreciation': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appreciation.hbs',
      'partial-attaque-particuliere': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-attaque-particuliere.hbs',
      'partial-choix-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-choix-maladresse.hbs',
      'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs',
      'partial-infojet': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-infojet.hbs',
      'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs',
      'partial-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-maladresse.hbs',
      'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.hbs',
    })
  }

  async display(roll, impacts) {
    this.prepareDisplay(roll)

    const chatMessage = await ChatMessage.create(ChatUtility.adaptVisibility(
      {
        content: await this.buildRollHtml(roll)
      },
      {
        actor: roll.active.actor,
        rollMode: roll.current?.rollmode?.key
      }
    ))

    await this.saveChatMessageRoll(chatMessage, roll, impacts)
    return chatMessage
  }

  prepareDisplay(roll) {
    roll.done = roll.done ?? {}
    roll.show = roll.show ?? {}
    roll.show.chance = this.$isAppelChancePossible(roll)
    roll.show.encaissement = this.$isShowEncaissement(roll)
    roll.show.recul = this.$getRecul(roll)
    roll.show.maladresse = this.$getMaladresse(roll)
  }

  $isAppelChancePossible(roll) {
    return roll.active.actor.isPersonnage() &&
      roll.rolled.isEchec &&
      RdDCarac.isActionPhysique(roll.current.carac?.key) &&
      !roll.type.appreciation
  }

  $isShowEncaissement(roll) {
    switch (roll.type.current) {
      case ROLL_TYPE_DEFENSE:
        return roll.rolled.isEchec
    }
    return false
  }

  $getMaladresse(roll) {
    switch (roll.type.current) {
      case ROLL_TYPE_DEFENSE:
        if (roll.rolled.isETotal) {
          const arme = roll.current[PART_DEFENSE].arme
          return arme ? 'avec-arme' : 'sans-arme'
        }
        break
      case ROLL_TYPE_ATTAQUE:
        if (roll.rolled.isETotal || (roll.rolled.isEchec && roll.active.surprise == 'demi')) {
          const arme = roll.current[PART_ATTAQUE].arme
          return arme.system.baseInit > 4 ? 'avec-arme' : 'sans-arme'
        }
        break
    }
    return undefined
  }

  $getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) {
    switch (roll.type.current) {
      case ROLL_TYPE_DEFENSE:
        {
          const attaque = roll.attackerRoll
          if (attaque &&
            (roll.rolled.isEchec || !roll.current.defense.isEsquive) &&
            (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) {
            const taille = defender.getTaille()
            const impact = attacker.getForce() + roll.attackerRoll?.dmg.dmgArme
            return {
              raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
              taille: taille,
              impact: impact,
              chances: RdDResolutionTable.computeChances(10, taille - impact).norm,
              diff: taille - impact
            }
          }
          break
        }
      case ROLL_TYPE_ATTAQUE:
        {
          const attaque = roll
          if (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key) {
            return {
              raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
            }
          }
        }
    }
    return undefined
  }

  async buildRollHtml(roll) {
    const template = ALL_ROLL_TYPES.find(it => it.code == roll.type.current).chatResultTemplate
    const html = await renderTemplate(template, roll)
    return await RdDTextEditor.enrichHTML(html, undefined, { showLink: false })
  }

  async chatListeners(html) {
    $(html).on("click", '.appel-chance', event => this.onClickAppelChance(event))
    $(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event))
    $(html).on("click", '.button-defense', event => this.onClickDefense(event))
    $(html).on("click", '.button-defense-possession', event => this.onClickDefensePossession(event))
    $(html).on("click", '.marquer-point-possession', event => this.onClickMarquerPointPossession(event))
    $(html).on("click", '.encaissement', event => this.onClickEncaissement(event))
    $(html).on("click", '.resister-recul', event => this.onClickRecul(event))
    $(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event))
    $(html).on("click", '.faire-gouter', event => this.onClickFaireGouter(event))
    $(html).on("click", '.apprecier', event => this.onClickApprecier(event))
    $(html).on("click", '.monter-tmr-normale', event => this.onClickMonteeTMR(event, 'normal'))
    $(html).on("click", '.monter-tmr-rapide', event => this.onClickMonteeTMR(event, 'rapide'))
    $(html).on("click", '.tirer-maladresse', event => this.onClickTirerMaladresse(event))
  }

  getCombat(roll) {
    switch (roll.type.current) {
      case ROLL_TYPE_DEFENSE:
        return PasseArme.rddCombatForAttackerAndDefender(roll.ids.opponentId, roll.ids.opponentTokenId, roll.ids.actorTokenId)
      case ROLL_TYPE_ATTAQUE:
        return PasseArme.rddCombatForAttackerAndDefender(roll.ids.actorId, roll.ids.actorTokenId, roll.ids.opponentTokenId)
    }
    return undefined
  }

  getActiveActor(roll) {
    return roll.active?.actor ?? (roll.ids?.actorId ? game.actors.get(roll.ids.actorId) : undefined)
  }

  async updateChatMessage(chatMessage, savedRoll) {
    RollDialog.loadRollData(savedRoll)
    savedRoll.dmg = savedRoll.current.attaque?.dmg

    await this.saveChatMessageRoll(chatMessage, savedRoll)

    this.prepareDisplay(savedRoll)

    const content = await this.buildRollHtml(savedRoll)
    Remote.remoteCall({
      userId: chatMessage.author.id,
      documentName: chatMessage.documentName,
      documentId: chatMessage.id,
      method: 'update',
      args: [{ content: content }, { render: true }]
    })
  }

  async saveChatMessageRoll(chatMessage, roll, impacts = undefined) {
    const save = RollDialog.saveParts(roll, impacts)
    await ChatUtility.setMessageData(chatMessage, 'rollData', save)
  }

  loadChatMessageRoll(chatMessage) {
    const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
    RollBasicParts.restore(savedRoll)
    return savedRoll
  }

  onClickAppelChance(event) {
    event.preventDefault()
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)

    Misc.doIfOwner(actor, it => it.rollAppelChance(
      () => this.onAppelChanceSuccess(savedRoll, chatMessage),
      () => this.onAppelChanceEchec(savedRoll, chatMessage))
    )
  }

  async onAppelChanceSuccess(roll, chatMessage) {
    roll.type.retry = true
    roll.type.appelChance = true
    await this.updateChatMessage(chatMessage, roll)

    const callbacks = [ChatUtility.remover(chatMessage)]
    // TODO: annuler les effets...
    switch (roll.type.current) {
      case ROLL_TYPE_DEFENSE:
        this.getCombat(roll)?.doRollDefense(roll, callbacks)
        break
      case ROLL_TYPE_ATTAQUE:
        this.getCombat(roll)?.doRollAttaque(roll, callbacks)
        break
      default: {
        await RollDialog.create(roll, { onRollDone: RollDialog.onRollDoneClose, callbacks })
      }
    }
  }

  async onAppelChanceEchec(roll, chatMessage) {
    roll.type.retry = true
    await this.updateChatMessage(chatMessage, roll)
  }

  onClickAppelDestinee(event) {
    event.preventDefault()

    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)

    Misc.doIfOwner(actor, it => it.appelDestinee(async () => {
      savedRoll.type.retry = true
      RdDResolutionTable.significativeRequise(savedRoll.rolled)
      await this.updateChatMessage(chatMessage, savedRoll)
    }))
  }

  async onClickDefense(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, it => {
      const attackerRoll = savedRoll.attackerRoll
      RollDialog.loadRollData(attackerRoll)
      this.getCombat(attackerRoll)?.defenseV2(attackerRoll,
        [ChatUtility.remover(chatMessage)]
      )
    })
  }

  async onClickDefensePossession(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, it => RdDPossessionV2.rollDefensePossession(savedRoll, chatMessage))
  }

  async onClickMarquerPointPossession(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async it => {
      await RdDPossessionV2.onMarquerPointPossession(savedRoll)
      ChatUtility.remover(chatMessage)()
    })
  }

  async onClickEncaissement(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const isMessageDemande = ChatUtility.getMessageData(chatMessage, 'demande-defense')
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async defender => {
      const defenderToken = savedRoll.active.token
      const attaque = savedRoll.attackerRoll
      const attackerToken = savedRoll.ids.opponentTokenId ? canvas?.tokens?.get(savedRoll.ids.opponentTokenId) : undefined
      const attacker = attackerToken?.actor ?? game.actors.get(savedRoll.ids.opponentId)
      switch (attaque.dmg.mortalite) {
        case RDD_CONFIG.encaissement.empoignade:
          savedRoll.done = savedRoll.done ?? {}
          savedRoll.done.empoignade = await RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
          break
        case RDD_CONFIG.encaissement.entiteincarnee:
        case RDD_CONFIG.encaissement.nonmortel:
        case RDD_CONFIG.encaissement.mortel:
          await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
          break
      }
      if (isMessageDemande) {
        ChatUtility.remover(chatMessage)()
      } else {
        savedRoll.done.encaissement = true
        await this.updateChatMessage(chatMessage, savedRoll)
      }
    })
  }

  async onClickRecul(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async defender => {
      const attacker = game.actors.get(savedRoll.ids.opponentId)
      savedRoll.done.recul = await defender.encaisserRecul(attacker.getForce(), savedRoll.attackerRoll.dmg.dmgArme)
      await this.updateChatMessage(chatMessage, savedRoll)
    })
  }

  async onClickChoixParticuliere(event) {
    const choix = event.currentTarget.attributes['data-particuliere'].value
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async it => {
      savedRoll.particuliere = choix
      savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]]
      await this.updateChatMessage(chatMessage, savedRoll)
      await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll)
    })
  }

  async onClickFaireGouter(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async it => {
      if (!savedRoll.type.retry) {
        savedRoll.type.retry = true
        await this.updateChatMessage(chatMessage, savedRoll)
      }
      await new RollTypeCuisine().onFaireGouter(savedRoll)
    })
  }
  async onClickApprecier(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    Apprecier.onClickApprecier(savedRoll)
  }

  async onClickMonteeTMR(event, mode) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async it => {
      if (await new RollTypeMeditation().onMonteeTMR(savedRoll, mode)) {
        savedRoll.done.meditation = true
        await this.updateChatMessage(chatMessage, savedRoll)
      }
    })
  }

  async onClickTirerMaladresse(event) {
    const chatMessage = ChatUtility.getChatMessage(event)
    const typeMaladresse = event.currentTarget.attributes['data-maladresse'].value
    const savedRoll = this.loadChatMessageRoll(chatMessage)
    const actor = this.getActiveActor(savedRoll)
    Misc.doIfOwner(actor, async it => {
      savedRoll.maladresse = await RdDRollTables.getMaladresse({ arme: typeMaladresse == 'avec-arme', toChat: false })
      savedRoll.type.retry = true
      await this.updateChatMessage(chatMessage, savedRoll)
    })
  }
}
