import { ROLL_TYPE, SYSTEM } from "../config/system.mjs"
import LethalFantasyRoll from "../documents/roll.mjs"
import LethalFantasyUtils from "../utils.mjs"

export default class LethalFantasyCharacter extends foundry.abstract.TypeDataModel {
  static defineSchema() {
    const fields = foundry.data.fields
    const requiredInteger = { required: true, nullable: false, integer: true }
    const schema = {}

    schema.description = new fields.HTMLField({ required: true, textSearch: true })
    schema.notes = new fields.HTMLField({ required: true, textSearch: true })

    // Carac
    const characteristicField = (label) => {
      const schema = {
        value: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 }),
        percent: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 100 }),
        attackMod: new fields.NumberField({ ...requiredInteger, initial: 0 }),
        defenseMod: new fields.NumberField({ ...requiredInteger, initial: 0 })
      }
      return new fields.SchemaField(schema, { label })
    }

    schema.characteristics = new fields.SchemaField(
      Object.values(SYSTEM.CHARACTERISTICS).reduce((obj, characteristic) => {
        obj[characteristic.id] = characteristicField(characteristic.label)
        return obj
      }, {}),
    )

    // Save
    const saveField = (label) => {
      const schema = {
        value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
      }
      return new fields.SchemaField(schema, { label })
    }
    schema.saves = new fields.SchemaField(
      Object.values(SYSTEM.SAVES).reduce((obj, save) => {
        obj[save.id] = saveField(save.label)
        return obj
      }, {}),
    )

    // Challenges
    const challengeField = (label) => {
      const schema = {
        value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
      }
      return new fields.SchemaField(schema, { label })
    }
    schema.challenges = new fields.SchemaField(
      Object.values(SYSTEM.CHALLENGES).reduce((obj, save) => {
        obj[save.id] = challengeField(save.label)
        return obj
      }, {}),
    )

    schema.hp = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
    })

    schema.perception = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      bonus: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })
    schema.grit = new fields.SchemaField({
      starting: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })
    schema.luck = new fields.SchemaField({
      earned: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      current: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })
    schema.movement = new fields.SchemaField({
      walk: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      jog: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      sprint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      run: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      armorAdjust: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
    })
    schema.biodata = new fields.SchemaField({
      class: new fields.StringField({ required: true, nullable: false, initial: "" }),
      level: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 }),
      mortal: new fields.StringField({ required: true, nullable: false, initial: "" }),
      alignment: new fields.StringField({ required: true, nullable: false, initial: "" }),
      age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
      height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }),
      eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
      hair: new fields.StringField({ required: true, nullable: false, initial: "" })
    })
    schema.developmentPoints = new fields.SchemaField({
      total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })    
    schema.spellMiraclePoints = new fields.SchemaField({
      total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      used: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })

    const moneyField = (label) => {
      const schema = {
        value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
      }
      return new fields.SchemaField(schema, { label })
    }
    schema.moneys = new fields.SchemaField(
      Object.values(SYSTEM.MONEY).reduce((obj, save) => {
        obj[save.id] = moneyField(save.label)
        return obj
      }, {}),
    )

    return schema
  }

  /** @override */
  static LOCALIZATION_PREFIXES = ["LETHALFANTASY.Character"]

  prepareDerivedData() {
    super.prepareDerivedData();
    let grit = 0
    for (let c in this.characteristics) {
      if (SYSTEM.CHARACTERISTICS_MAJOR[c.id]) {
        grit += this.characteristics[c].value
      }
    }
    this.grit.starting = Math.round(grit / 6)
  }
  
  /**
   * Rolls a dice for a character.
   * @param {("save"|"resource|damage")} rollType The type of the roll.
   * @param {number} rollTarget The target value for the roll. Which caracteristic or resource. If the roll is a damage roll, this is the id of the item.
   * @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
   * @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
   */
  async roll(rollType, rollTarget, rollAdvantage = "=") {
    let rollValue
    let opponentTarget
    switch (rollType) {
      case ROLL_TYPE.SAVE:
        rollValue = this.caracteristiques[rollTarget].valeur
        opponentTarget = game.user.targets.first()
        break
      case ROLL_TYPE.DAMAGE:
        rollValue = this.parent.items.get(rollTarget).system.degats
        opponentTarget = game.user.targets.first()
        break
      default:
        // Handle other cases or do nothing
        break
    }
    await this._roll(rollType, rollTarget, rollValue, opponentTarget, rollAdvantage)
  }

  /**
   * Rolls a dice for a character.
   * @param {("save"|"resource|damage")} rollType The type of the roll.
   * @param {number} rollTarget The target value for the roll. Which caracteristic or resource. If the roll is a damage roll, this is the id of the item.
   * @param {number} rollValue The value of the roll. If the roll is a damage roll, this is the dice to roll.
   * @param {Token} opponentTarget The target of the roll : used for save rolls to get the oppponent's malus.
   * @param {"="|"+"|"++"|"-"|"--"} rollAdvantage If there is an avantage (+), a disadvantage (-), a double advantage (++), a double disadvantage (--) or a normal roll (=).
   * @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
   */
  async _roll(rollType, rollTarget, rollValue, opponentTarget = undefined, rollAdvantage = "=") {
    const hasTarget = opponentTarget !== undefined
    let roll = await LethalFantasyRoll.prompt({
      rollType,
      rollTarget,
      rollValue,
      actorId: this.parent.id,
      actorName: this.parent.name,
      actorImage: this.parent.img,
      hasTarget,
      target: opponentTarget,
      rollAdvantage,
    })
    if (!roll) return null

    await roll.toMessage({}, { rollMode: roll.options.rollMode })
  }
}
