import { SYSTEM } from "../config/system.mjs"
import CthulhuEternalRoll from "../documents/roll.mjs"

export default class CthulhuEternalProtagonist 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 }),
        feature: new fields.StringField({ required: true, nullable: false, initial: "" }),
        max: new fields.NumberField({ ...requiredInteger, initial: 3, min: 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
      }, {}),
    )

    schema.wp = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      max: new fields.NumberField({ ...requiredInteger, initial: 3, min: 0 }),
      exhausted: new fields.BooleanField({ required: true, initial: false })
    })

    schema.hp = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
      max: new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 }),
      stunned: new fields.BooleanField({ required: true, initial: false }),
      unconscious: new fields.BooleanField({ required: true, initial: false }),
      dead: new fields.BooleanField({ required: true, initial: false })
    })

    schema.san = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      recovery: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      violence: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }),
      helplessness: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }),
      breakingPoint: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      breakingPointReached: new fields.BooleanField({ required: true, initial: false }),
      insanity: new fields.StringField({ required: true, nullable: false, initial: "none", choices: SYSTEM.INSANITY }),
    })

    schema.damageBonus = new fields.NumberField({ ...requiredInteger, initial: 0, min: -2 })

    schema.resources = new fields.SchemaField({
      value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), // Unused but kept for compatibility
      permanentRating: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      hand: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      currentHand: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      stowed: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      currentStowed: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      storage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      currentStorage: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
      checks: new fields.ArrayField(new fields.BooleanField(), { required: true, initial: [false, false, false], min: 3, max: 3 }),
      nbValidChecks: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
    })

    schema.biodata = new fields.SchemaField({
      age: new fields.NumberField({ ...requiredInteger, initial: 15, min: 6 }),
      archetype: new fields.StringField({ required: true, nullable: false, initial: "" }),
      height: new fields.NumberField({ ...requiredInteger, initial: 170, min: 50 }),
      gender: new fields.StringField({ required: true, nullable: false, initial: "" }),
      home: new fields.StringField({ required: true, nullable: false, initial: "" }),
      birthplace: new fields.StringField({ required: true, nullable: false, initial: "" }),
      eyes: new fields.StringField({ required: true, nullable: false, initial: "" }),
      hair: new fields.StringField({ required: true, nullable: false, initial: "" }),
      harshness: new fields.StringField({ required: true, nullable: false, initial: "normal", choices: SYSTEM.HARSHNESS }),
      adaptedToViolence: new fields.BooleanField({ required: true, initial: false }),
      adaptedToHelplessness: new fields.BooleanField({ required: true, initial: false })
    })

    return schema
  }

  /** @override */
  static LOCALIZATION_PREFIXES = ["CTHULHUETERNAL.Protagonist"]

  prepareDerivedData() {
    super.prepareDerivedData();

    if (!game.user.isGM) {
      return
    }

    let updates = {}
    if (this.wp.max !== this.characteristics.pow.value) {
      updates[`system.wp.max`] = this.characteristics.pow.value
    }
    let hpMax = Math.round((this.characteristics.con.value + this.characteristics.str.value) / 2)
    if (this.hp.max !== hpMax) {
      updates[`system.hp.max`] = hpMax
    }

    // Get Unnatural skill for MAX SAN
    let unnatural = this.parent.items.find(i => i.type === "skill" && i.name.toLowerCase() === game.i18n.localize("CTHULHUETERNAL.Skill.Unnatural").toLowerCase())
    let minus = 0
    if (unnatural) {
      minus = unnatural.system.skillTotal
    }
    let maxSan = Math.max(99 - minus, 0)
    if (this.san.max !== maxSan) {
      updates[`system.san.max`] = maxSan
    }

    let recoverySan = this.characteristics.pow.value * 5
    if (recoverySan > this.san.max) {
      recoverySan = this.san.max
    }
    if (this.san.recovery !== recoverySan) {
      updates[`system.san.recovery`] = recoverySan
    }

    let dmgBonus = 0
    if (this.characteristics.str.value <= 4) {
      dmgBonus = -2
    } else if (this.characteristics.str.value <= 8) {
      dmgBonus = -1
    } else if (this.characteristics.str.value <= 12) {
      dmgBonus = 0
    } else if (this.characteristics.str.value <= 16) {
      dmgBonus = 1
    } else if (this.characteristics.str.value <= 40) {
      dmgBonus = 2
    }
    if (this.damageBonus !== dmgBonus) {
      updates[`system.damageBonus`] = dmgBonus
    }

    // BP (Breaking Point) management
    if (!this.san.breakingPointReached && this.san.value <= this.san.breakingPoint) {
      updates[`system.san.breakingPointReached`] = true
      this.san.breakingPointReached = true // Force local update to true
      let w = game.users.find(u => u.character?.name === this.parent?.name)
      if (w) {
        ChatMessage.create({
          content: `<p>${game.i18n.format("CTHULHUETERNAL.Label.breakingPointReached", { bp: this.san.breakingPoint, san: this.san.value })}</p>`,
          speaker: ChatMessage.getSpeaker({ actor: this.parent }),
          // Get the user id of the actor owner
          whisper: [w.id]
        })
      }
    }

    // Unconsciousness management
    if (!this.hp.unconscious && this.hp.value <= 2) {
      updates[`system.hp.unconscious`] = true
    }
    if (this.hp.unconscious && this.hp.value > 2) {
      updates[`system.hp.unconscious`] = false
    }

    // Dead management
    if (!this.hp.dead && this.hp.value <= 0) {
      updates[`system.hp.dead`] = true
    }
    if (this.hp.dead && this.hp.value > 0) {
      updates[`system.hp.dead`] = false
    }

    // Sanity check
    if (this.san.value > this.san.max) {
      updates[`system.san.value`] = this.san.max
    }
    if (this.wp.value > this.wp.max) {
      updates[`system.wp.value`] = this.wp.max
    }
    if (this.hp.value > this.hp.max) {
      updates[`system.hp.value`] = this.hp.max
    }

    if (this.resources.permanentRating < 0) {
      updates[`system.resources.permanentRating`] = 0
    }

    let resourceIndex = Math.max(Math.min(this.resources.permanentRating, 20), 0)
    let breakdown = SYSTEM.RESOURCE_BREAKDOWN[resourceIndex]
    if (this.resources.hand !== breakdown.hand) {
      updates[`system.resources.hand`] = breakdown.hand
    }
    if (this.resources.stowed !== breakdown.stowed) {
      updates[`system.resources.stowed`] = breakdown.stowed
    }
    if (this.resources.storage !== breakdown.storage) {
      updates[`system.resources.storage`] = breakdown.storage + (this.resources.permanentRating - resourceIndex)
    }
    if (this.resources.nbValidChecks !== breakdown.checks) {
      updates[`system.resources.nbValidChecks`] = breakdown.checks
    }

    if (Object.keys(updates).length > 0) {
      this.parent.update(updates)
    }
  }

  async applySANConsequences(rollData) {
    let msgData = {
      sanType: rollData.sanType,
      sanLoss: rollData.sanLoss,
      actorId: this.parent.id,
      actorName: this.parent.name,
      adaptedToHelplessness: this.biodata.adaptedToHelplessness,
      adaptedToViolence: this.biodata.adaptedToViolence,
      ...rollData
    }
    let updates = {}
    let template = ""
    // Manage temporary insanity
    if (rollData.sanLoss >= 5) {
      rollData.resetMsg = false
      if (rollData.sanType === "violence" && !this.biodata.adaptedToViolence) {
        updates[`system.san.violence`] = [false, false, false]
        rollData.resetMsg = "CTHULHUETERNAL.Label.sanViolenceReset"
      }
      if (rollData.sanType === "helplessness" && !this.biodata.adaptedToHelplessness) {
        updates[`system.san.helplessness`] = [false, false, false]
        rollData.resetMsg = "CTHULHUETERNAL.Label.sanHelplessnessReset"
      }
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-temp-insanity.hbs"

    } else if (rollData.sanLoss === 0) {   // Manage if sanLoss is 0
      rollData.resetMsg = false
      if (rollData.sanType === "violence" && !this.biodata.adaptedToViolence) {
        updates[`system.san.violence`] = [false, false, false]
        rollData.resetMsg = "CTHULHUETERNAL.Label.sanViolenceReset"
      }
      if (rollData.sanType === "helplessness" && !this.biodata.adaptedToHelplessness) {
        updates[`system.san.helplessness`] = [false, false, false]
        rollData.resetMsg = "CTHULHUETERNAL.Label.sanHelplessnessReset"
      }
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-0.hbs"

    } else if (rollData.sanType === "violence") {
      // Set the first false element of the violence array to true
      let violence = this.san.violence.slice()
      let index = violence.findIndex(v => !v)
      if (index !== -1) {
        violence[index] = true
        updates[`system.san.violence`] = violence
      }
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-1-4.hbs"
      // Check if all violence elements are true, if so, set adaptedToViolence to true
      if (violence.every(v => v)) {
        updates[`system.biodata.adaptedToViolence`] = true
        updates[`system.san.violence`] = [false, false, false]
        msgData.adaptedToViolence = true
      }
    } else if (rollData.sanType === "helplessness") {
      // If sanType is "helplessness" and adapted to helplessness, set the first false element of the helplessness array to true
      let helplessness = this.san.helplessness.slice()
      let index = helplessness.findIndex(h => !h)
      if (index !== -1) {
        helplessness[index] = true
        updates[`system.san.helplessness`] = helplessness
      }
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-1-4.hbs"
      // Check if all helplessness elements are true, if so, set adaptedToHelplessness to true
      if (helplessness.every(h => h)) {
        updates[`system.biodata.adaptedToHelplessness`] = true
        updates[`system.san.helplessness`] = [false, false, false]
        msgData.adaptedToHelplessness = true
      }
    } else if (rollData.sanType === "unnatural") {
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-unnatural.hbs"
    } else {
      template = "systems/fvtt-cthulhu-eternal/templates/chat-san-loss-none.hbs"
    }

    let content = await foundry.applications.handlebars.renderTemplate(template, msgData)
    let msg = await ChatMessage.create({
      content: content,
      speaker: ChatMessage.getSpeaker({ actor: this.parent }),
      whisper: game.users.filter(u => u.isGM).map(u => u.id),
    })
    await msg.setFlag("fvtt-cthulhu-eternal", "rollData", msgData)

    if (Object.keys(updates).length > 0) {
      this.parent.update(updates)
    }

  }

  async modifySAN(rollData) {
    let updates = {}
    let san = Math.max(Math.min(this.san.value + rollData.sanLoss, this.san.max), 0)
    if (this.san.value !== san) {
      updates[`system.san.value`] = san
      rollData.sanValue = san
      const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-san-type-request.hbs", rollData)
      let msg = await ChatMessage.create({
        content: content,
        speaker: ChatMessage.getSpeaker({ actor: this.parent }),
        whisper: game.users.filter(u => u.isGM).map(u => u.id)
      })
      await msg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData)
    }
    if (Object.keys(updates).length > 0) {
      this.parent.update(updates)
    }
  }

  isStunned() {
    return this.hp.stunned
  }

  isLowWP() {
    return this.wp.value <= 2
  }

  isZeroWP() {
    return this.wp.value === 0
  }

  isExhausted() {
    return this.wp.exhausted
  }

  modifyWP(value) {
    let updates = {}
    let wp = Math.max(Math.min(this.wp.value + value, this.wp.max), 0)
    if (this.wp.value !== wp) {
      updates[`system.wp.value`] = wp
    }
    if (Object.keys(updates).length > 0) {
      this.parent.update(updates)
    }
  }

  setBP() {
    let updates = {}
    let bp = Math.max(this.san.value - this.characteristics.pow.value, 0)
    if (this.san.breakingPoint !== bp) {
      updates[`system.san.breakingPoint`] = bp
      updates[`system.san.breakingPointReached`] = false // Reset breaking point reached
    }
    if (Object.keys(updates).length > 0) {
      this.parent.update(updates)
    }
  }

  /** */
  /**
   * Rolls a dice for a character.
   * @param {("save"|"resource|damage")} rollType The type of the roll.
   * @param {number} rollItem The target value for the roll. Which caracteristic or resource. If the roll is a damage roll, this is the id of the item.
   * @returns {Promise<null>} - A promise that resolves to null if the roll is cancelled.
   */
  async roll(rollType, rollItem) {

    if (this.hp.dead) {
      // Warn with chat message
      ChatMessage.create({
        content: `<p>${game.i18n.format("CTHULHUETERNAL.Label.deadWarning", { con: this.characteristics.con.value })}</p>`,
        speaker: ChatMessage.getSpeaker({ actor: this.parent })
      })
      return null
    }
    if (this.hp.unconscious) {
      // Warn with chat message
      ChatMessage.create({
        content: `<p>${game.i18n.localize("CTHULHUETERNAL.Label.unconsciousWarning")}</p>`,
        speaker: ChatMessage.getSpeaker({ actor: this.parent })
      })
      return null
    }
    if (this.hp.stunned && rollType === "skill") {
      // Warn with chat message
      ChatMessage.create({
        content: `<p>${game.i18n.localize("CTHULHUETERNAL.Label.stunnedWarning")}</p>`,
        speaker: ChatMessage.getSpeaker({ actor: this.parent })
      })
      return null
    }

    let opponentTarget
    const hasTarget = opponentTarget !== undefined

    let roll = await CthulhuEternalRoll.prompt({
      rollType,
      rollItem,
      isLowWP: this.isLowWP(),
      isZeroWP: this.isZeroWP(),
      isExhausted: this.isExhausted(),
      actorId: this.parent.id,
      actorName: this.parent.name,
      actorImage: this.parent.img,
      hasTarget,
      previousResultType: rollItem.resultType,
      target: opponentTarget
    })
    if (!roll) return null

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

  }
}
