import { Distance } from "../combat/distance.mjs"
import { RDD_CONFIG } from "../constants.js"
import { RdDInitiative } from "../combat/initiative.mjs"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { Misc } from "../misc.js"
import { RdDBonus } from "../rdd-bonus.js"
import { CARACS } from "../rdd-carac.js"
import { RdDCombat } from "../combat/rdd-combat.js"
import { RdDEmpoignade } from "../combat/rdd-empoignade.js"
import { DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "./roll-constants.mjs"
import RollDialog from "./roll-dialog.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { PART_DIFF } from "./roll-part-diff.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { PART_SIGN } from "./roll-part-sign.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"

export const PART_ATTAQUE = 'attaque'

const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)

const FILTER_ATTAQUE_EMPOIGNADE = attaque => attaque.arme?.isEmpoignade()
const FILTER_ATTAQUE_RANG = rang => attaque => !attaque.arme?.isEmpoignade() && (attaque.rang == rang || rang == undefined)
const FILTER_ATTAQUE_EMPOIGNE = attaque => attaque.arme?.isUtilisableEmpoigne() && ATTAQUE_TYPE_MELEE.includes(attaque.main)

export class RollPartAttaque extends RollPartSelect {

  get code() { return PART_ATTAQUE }
  get section() { return ROLLDIALOG_SECTION.CHOIX }

  get rollTypes() { return [ROLL_TYPE_ATTAQUE] }

  visible(rollData) { return RollPart.isRollType(rollData, ROLL_TYPE_ATTAQUE) }

  loadRefs(rollData) {
    const refs = this.getRefs(rollData)
    const attaques = rollData.active.actor.listActions()
      .filter(it => !it.initOnly)
      .sort(Misc.descending(it => it.comp?.system.niveau ?? -8))
    refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData))
    this.filterAttaquesInitiative(rollData)
    refs.tactiques = TACTIQUES
    if (refs.attaques.length > 0) {
      const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData))
      this.$selectAttaque(rollData, attaque?.key)
    }
  }

  isAttaqueEmpoignade(it) {
    return it.arme.isEmpoignade()
  }

  restore(rollData) {
    const saved = this.getSaved(rollData) ?? {}
    if (saved.key) {
      this.setCurrent(rollData, {
        key: saved.key,
        tactique: saved.tactique,
        dmg: saved.dmg
      })
    }
  }

  store(rollData, targetData) {
    const current = this.getCurrent(rollData)
    this.setSaved(targetData, {
      key: current.key,
      tactique: current.tactique,
      dmg: current.dmg
    })
  }


  findAttaque(attaques, saved) {
    return attaques.find(it => it.key == saved.key) ??
      attaques.find(it => it.arme.id == (saved?.arme?.id ?? it.arme.id)
        && it.comp.id == (saved?.comp?.id ?? it.comp.id)
        && it.main == (saved?.main ?? it.main)
      )
  }

  choices(refs) { return refs.attaques }

  static $extractAttaque(attaque, rollData) {
    attaque.key = `${attaque.action}::${attaque.label}`
    attaque.tactique = TACTIQUES[0]
    attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0
    attaque.distance = Distance.ajustements(rollData.active?.token, rollData.opponent?.token, attaque)
    attaque.rang = RdDInitiative.phaseArme(attaque.comp?.system.categorie, attaque.arme)?.rang
    return attaque
  }

  prepareContext(rollData) {
    this.filterAttaquesInitiative(rollData)
    const current = this.getCurrent(rollData)
    current.dmg = RdDBonus.dmgRollV2(rollData, current)
  }

  filterAttaquesInitiative(rollData) {
    const refs = this.getRefs(rollData)
    const rang = RdDCombat.getRangInitiativeCombatant(rollData.ids.actorId, rollData.ids.actorTokenId)
    const isEmpoignade = RdDInitiative.PHASES.empoignade.rang == rang
    //const isEmpoignade = RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
    refs.isEmpoignadeEnCours = RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
    const filterAttaques = isEmpoignade ?
      FILTER_ATTAQUE_EMPOIGNADE
      : refs.isEmpoignadeEnCours
        ? FILTER_ATTAQUE_EMPOIGNE
        : FILTER_ATTAQUE_RANG(rang)
    refs.attaques = refs.all.filter(filterAttaques)
  }

  getAjustements(rollData) {
    const current = this.getCurrent(rollData)
    const tactique = current.tactique ? [{ label: current.tactique.label, value: current.tactique.attaque }] : []
    const surprise = rollData.opponent?.surprise ? [{ label: rollData.opponent.surprise.label, value: rollData.opponent.surprise.attaque }] : []
    return [...tactique, ...surprise]
  }


  $selectAttaque(rollData, key) {
    const tactique = this.getCurrent(rollData).tactique
    this.selectByKey(rollData, key)
    if (tactique) {
      this.getCurrent(rollData).tactique = tactique
    }
  }

  async _onRender(rollDialog, context, options) {
    const inputDiff = rollDialog.element.querySelector(`roll-section[name="diff"] input[name="diff"]`)
    const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`)
    const selectTactique = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tactique"]`)
    const checkMortalite = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="check-mortalite"]`)
    const utiliserDagueEmpoignade = rollDialog.element.querySelector(`roll-section[name="${this.code}"] a.utiliser-dague-empoignade`)
    const rollData = rollDialog.rollData
    const current = this.getCurrent(rollData)

    selectAttaque?.addEventListener("change", e => {
      const selectOptions = e.currentTarget.options
      const index = selectOptions.selectedIndex
      this.$selectAttaque(rollData, selectOptions[index]?.value)
      rollDialog.render()
    })

    selectTactique?.addEventListener("change", e => {
      const selectOptions = e.currentTarget.options
      const index = selectOptions.selectedIndex
      current.tactique = RdDBonus.find(selectOptions[index]?.value)
      rollDialog.render()
    })

    checkMortalite?.addEventListener("change", e => {
      current.dmg.mortalite = (e.currentTarget.checked ? RDD_CONFIG.encaissement.mortel : RDD_CONFIG.encaissement.nonmortel)
      rollDialog.render()
    })
    utiliserDagueEmpoignade?.addEventListener("click", e => {
      e.preventDefault()
      this.utiliserDagueEmpoignade(rollData)
    })
    inputDiff?.addEventListener("input", e => {
      this.getRefs(rollData).all.forEach(attaque => attaque.initialDiff = undefined)
    })
  }

  utiliserDagueEmpoignade(rollData) {
    RollDialog.create({
      ids: { actorId: rollData.ids.actorId, actorTokenId: rollData.ids.actorTokenId },
      type: { allowed: [ROLL_TYPE_COMP], current: ROLL_TYPE_COMP },
      selected: {
        carac: { key: CARACS.DEXTERITE, forced: true },
        comp: { key: 'Dague', forced: true },
        diff: { type: DIFF.IMPOSEE, value: -4 }
      }
    },
      { onRollDone: RollDialog.onRollDoneClose })
  }

  impactOtherPart(part, rollData) {
    if (this.visible(rollData)) {
      const current = this.getCurrent(rollData)
      switch (part.code) {
        case PART_CARAC: return part.filterCaracs(rollData, [current.carac?.key])
        case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
        case PART_DIFF: {
          if (Distance.typeAttaqueDistance(current)) {
            part.setDiff(rollData, { type: DIFF.DEFAUT })
          }
          else {
            if (!rollData.type.appelChance){
              part.setDiff(rollData, { type: DIFF.ATTAQUE, diff: current.initialDiff })
            }
            current.initialDiff = undefined
          }
          break
        }
        case PART_SIGN: return part.setArme(rollData, false, current.forceRequise)
      }
    }
    return undefined
  }
}
