import { ITEM_TYPES, RDD_CONFIG } from "../constants.js";
import { RollBasicParts } from "../roll/roll-basic-parts.mjs";
import { ChatUtility } from "../chat-utility.js";
import RollDialog from "../roll/roll-dialog.mjs";
import { Targets } from "./targets.js";
import { RdDUtility } from "../rdd-utility.js";
import { ROLL_TYPE_POSSESSION } from "../roll/roll-constants.mjs";
import { TokenActor } from "../technical/actor-token.mjs";

export const ACTIONS_POSSESSION = {
  ATTAQUE: 'attaque',
  POSSEDER: 'posseder',
  CONJURER: 'conjurer',
  DEFENSE: 'defense',
  DEFENSE_POSSESSION: 'defense-possession',
  DEFENSE_CONJURATION: 'defense-conjuration'
}

export class RdDPossessionV2 {

  static init() {
  }

  static $isInverse(entite, victime) {
    return !entite.actor.isEntiteNonIncarnee() && victime.actor.isEntiteNonIncarnee()
  }

  /* -------------------------------------------- */
  static findPossession(entite, victime) {
    if (RdDPossessionV2.$isInverse(entite, victime)) {
      return RdDPossessionV2.findPossession(victime, entite)
    }
    return victime.actor.itemTypes[ITEM_TYPES.possession].find(it => it.system.entiteid == entite.id)
  }

  static async createPossessionIfMissing(entite, victime) {
    if (RdDPossessionV2.$isInverse(entite, victime)) {
      return await RdDPossessionV2.createPossessionIfMissing(victime, entite)
    }
    const existing = RdDPossessionV2.findPossession(entite, victime)
    if (!existing) {
      await victime.actor.createEmbeddedDocuments('Item', [{
        name: `Possession de ${entite.name}`,
        type: ITEM_TYPES.possession,
        img: RDD_CONFIG.icons.possession,
        system: {
          entiteid: entite.actor.id,
          entitetokenid: entite.token.id,
          victimeid: victime.actor.id,
          victimetokenid: victime.token.id,
          compteur: 0
        }
      }])
    }
  }

  static getTypePossession(active, opponent) {
    const itemPossession = RdDPossessionV2.findPossession(active, opponent)
    const isEntite = active.actor.isEntiteNonIncarnee() && !opponent.actor.isEntiteNonIncarnee()
    const compteur = itemPossession?.system.compteur ?? 0

    return {
      isEntite: isEntite,
      isPersonnage: active.actor.isPersonnage(),
      isCompteurPossession: Math.sign(compteur) >= 0,
      compteur: Math.abs(compteur)
    }
  }

  static getTypePossessionAction(active, opponent, action) {
    const possession = RdDPossessionV2.getTypePossession(active, opponent)
    possession.isAttaque = RdDPossessionV2.isAttaque(action)
    possession.action = RdDPossessionV2.$getAction(possession.isAttaque, possession.isEntite)
    return possession
  }

  static $getAction(isAttaque, isEntite) {
    if (isAttaque) {
      return isEntite ? ACTIONS_POSSESSION.POSSEDER : ACTIONS_POSSESSION.CONJURER
    }
    return isEntite ? ACTIONS_POSSESSION.DEFENSE_CONJURATION : ACTIONS_POSSESSION.DEFENSE_POSSESSION
  }

  static actionTitle(action) {
    switch (action) {
      case ACTIONS_POSSESSION.POSSEDER: return "tente de posséder"
      case ACTIONS_POSSESSION.CONJURER: return "tente de conjurer"
      case ACTIONS_POSSESSION.DEFENSE_POSSESSION: return "résiste à la possession de"
      case ACTIONS_POSSESSION.DEFENSE_CONJURATION: return "résiste à la conjuration de"
      case ACTIONS_POSSESSION.DEFENSE: return "résiste à "
    }
    return "lutte contre"
  }

  static isDefense(action) {
    return [ACTIONS_POSSESSION.DEFENSE, ACTIONS_POSSESSION.DEFENSE_POSSESSION, ACTIONS_POSSESSION.DEFENSE_CONJURATION].includes(action)
  }
  static isAttaque(action) {
    return [ACTIONS_POSSESSION.ATTAQUE, ACTIONS_POSSESSION.POSSEDER, ACTIONS_POSSESSION.CONJURER].includes(action)
  }

  static async rollAttaquePossession(actor) {
    const selectedToken = RdDUtility.getSelectedToken(actor)
    Targets.selectOneTargetToken(async target => {
      RollDialog.create(
        {
          ids: {
            actorId: actor.id,
            actorTokenId: selectedToken.id,
            opponentId: target.actor.id,
            opponentTokenId: target.id
          },
          passeArme: foundry.utils.randomID(16),
          type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.ATTAQUE } },
        },
        {
          onRollDone: RollDialog.onRollDoneClose,
          callbacks: [
            async (roll) => await RdDPossessionV2.createPossessionIfMissing(roll.active, roll.opponent),
            async (roll) => RdDPossessionV2.$setParticuliereFinesse(roll),
            async (roll) => await RdDPossessionV2.chatMessageDefensePossession(roll)
          ]
        })
    })
  }

  static async rollConjurerPossession(actor, possession) {
    if (possession.system.possede){
      ui.notifications.warn(`${actor.name} est totalement possédé, impossible de conjurer l'entité`)
      return
    }
    RollDialog.create(
      {
        ids: {
          actorId: possession.system.victimeid,
          actorTokenId: possession.system.victimetokenid,
          opponentId: possession.system.entiteid,
          opponentTokenId: possession.system.entitetokenid
        },
        passeArme: foundry.utils.randomID(16),
        type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.ATTAQUE } },
      },
      {
        onRollDone: RollDialog.onRollDoneClose,
        callbacks: [
          async (roll) => await RdDPossessionV2.createPossessionIfMissing(roll.active, roll.opponent),
          async (roll) => RdDPossessionV2.$setParticuliereFinesse(roll),
          async (roll) => await RdDPossessionV2.chatMessageDefensePossession(roll)
        ]
      })
  }

  static $setParticuliereFinesse(roll) {
    if (roll.rolled.isPart) {
      roll.particuliere = RDD_CONFIG.particuliere.finesse.key
    }
  }

  static async rollDefensePossession(savedRoll, chatMessage) {
    RollBasicParts.restore(savedRoll)
    const attackerRoll = savedRoll.attackerRoll
    RollDialog.create(
      {
        ids: savedRoll.ids,
        passeArme: attackerRoll.passeArme,
        type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.DEFENSE } },
        selected: { diff: { value: attackerRoll.selected?.diff?.value ?? 0 } },
        attackerRoll: attackerRoll
      },
      {
        onRollDone: RollDialog.onRollDoneClose, callbacks: [
          async roll => await RdDPossessionV2.onRollDefense(roll),
          ChatUtility.remover(chatMessage)
        ]
      });
  }


  static async onRollDefense(defense) {
    if (defense.rolled.isEchec) {
      await RdDPossessionV2.addPointPossession(defense.opponent, defense.active)
    }
    RdDPossessionV2.resetPossession(defense)
  }


  static async onMarquerPointPossession(roll) {
    roll.type = {}
    await RdDPossessionV2.addPointPossession(roll.opponent, roll.active)
    RdDPossessionV2.resetPossession(roll)

    await ChatMessage.create({
      // message privé: du défenseur à lui même (et aux GMs)
      speaker: ChatMessage.getSpeaker({ actor: roll.active.actor, token: roll.active.token }),
      alias: roll.opponent.name,
      whisper: ChatUtility.getOwners(roll.active.actor),
      content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/possession/chat-message-marquer.hbs`, roll)
    })
  }

  static resetPossession(roll) {
    roll.type.possession = foundry.utils.mergeObject(
      roll.type.possession ?? {},
      RdDPossessionV2.getTypePossession(roll.opponent, roll.active))
  }

  static async addPointPossession(entite, victime, points = 1) {
    if (RdDPossessionV2.$isInverse(entite, victime)) {
      return await RdDPossessionV2.addPointPossession(victime, entite, - points)
    }
    const existing = RdDPossessionV2.findPossession(entite, victime)
    if (existing) {
      const compteur = (existing.system.compteur ?? 0) + points
      await victime.actor.updateEmbeddedDocuments('Item', [{ _id: existing.id, 'system.compteur': compteur }])
    }
  }

  static async chatMessageDefensePossession(attackerRoll) {

    const defense = RollBasicParts.prepareDefense(attackerRoll)
    defense.type = {
      possession: RdDPossessionV2.getTypePossessionAction(defense.active, defense.opponent, ACTIONS_POSSESSION.DEFENSE)
    }

    const chatDemandeDefense = await ChatMessage.create({
      // message privé: du défenseur à lui même (et aux GMs)
      speaker: ChatMessage.getSpeaker({ actor: defense.active.actor, token: defense.active.token }),
      alias: attackerRoll.active.name,
      whisper: ChatUtility.getOwners(defense.active.actor),
      content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/possession/chat-demande-defense.hbs', defense)
    });
    ChatUtility.setMessageData(chatDemandeDefense, 'demande-defense', true)
    // // flag pour garder les jets d'attaque/defense
    ChatUtility.setMessageData(chatDemandeDefense, 'rollData', {
      ids: defense.ids,
      attackerRoll: RollDialog.saveParts(attackerRoll),
      passeArme: defense.passeArme
    })
  }

  static async onPossession(actor, possession) {
    if (Math.abs(possession.system.compteur) >= 2) {
      await ChatMessage.create({
        alias: actor.getAlias(),
        whisper: ChatUtility.getOwners(actor),
        content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/possession/chat-fin-possession.hbs', {
          possession,
          active: TokenActor.fromActor(actor),
          opponent: TokenActor.fromActor(game.actors.get(possession.system.entiteid))
        })
      })

      if (possession.system.compteur <= -2) {
        await actor.deleteEmbeddedDocuments('Item', [possession.id])
      }
      if (possession.system.compteur >= 2) {
        await actor.updateEmbeddedDocuments('Item', [{ _id: possession.id, 'system.possede': true }])
      }
    }
  }
}
