/**
 * Donjon & Cie - Systeme FoundryVTT
 *
 * Donjon & Cie est un jeu de role edite par John Doe.
 * Ce systeme FoundryVTT est une implementation independante et n'est pas
 * affilie a John Doe.
 *
 * @author    LeRatierBretonnien
 * @copyright 2025–2026 LeRatierBretonnien
 * @license   CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
 */

import { DonjonEtCieUtility } from "./donjon-et-cie-utility.mjs";
import { DonjonEtCieRollDialog } from "./applications/donjon-et-cie-roll-dialog.mjs";

export class DonjonEtCieActor extends Actor {
  static defaultPnjAttack = {
    nom: "Attaque",
    degats: "1d6",
    notes: ""
  };

  prepareDerivedData() {
    super.prepareDerivedData();

    const pv = this.system.sante?.pv;
    if (pv && pv.value > pv.max) {
      pv.max = pv.value;
    }
  }

  async _preUpdate(changed, options, user) {
    if (
      foundry.utils.hasProperty(changed, "system.magie.focus.delta")
      && !foundry.utils.hasProperty(changed, "system.magie.focus.resultat")
      && !foundry.utils.hasProperty(changed, "system.magie.focus.sceneId")
    ) {
      foundry.utils.setProperty(changed, "system.magie.focus.resultat", 0);
      foundry.utils.setProperty(changed, "system.magie.focus.sceneId", "");
    }

    return super._preUpdate(changed, options, user);
  }

  getCharacteristicEntries() {
    return DonjonEtCieUtility.getCharacteristicEntries(this.system);
  }

  getSectionData() {
    return DonjonEtCieUtility.buildActorSections(this);
  }

  getFavorEntries() {
    return DonjonEtCieUtility.getFavorEntries(this.system);
  }

  #getStoredArmorContext() {
    if (this.type === "pnj") {
      const stored = Number(this.system.defense?.armure?.resultatProtection ?? 0);
      return {
        label: "ARM",
        hasArmor: true,
        before: stored,
        update: async (value) => this.update({ "system.defense.armure.resultatProtection": Math.max(0, Number(value ?? 0)) })
      };
    }

    const armors = [...this.items.filter((item) => item.type === "armure")].sort((a, b) => {
      const equippedScore = Number(Boolean(b.system.equipee)) - Number(Boolean(a.system.equipee));
      if (equippedScore) return equippedScore;

      const protectionScore = Number(b.system.resultatProtection ?? 0) - Number(a.system.resultatProtection ?? 0);
      if (protectionScore) return protectionScore;

      return a.name.localeCompare(b.name, "fr", { sensitivity: "base" });
    });

    const armor = armors.find((item) => item.system.equipee || Number(item.system.resultatProtection ?? 0) > 0) ?? null;
    if (!armor) {
      return {
        label: "Armure",
        hasArmor: false,
        before: 0,
        update: null
      };
    }

    return {
      label: armor.name,
      hasArmor: true,
      before: Number(armor.system.resultatProtection ?? 0),
      update: async (value) => armor.update({ "system.resultatProtection": Math.max(0, Number(value ?? 0)) })
    };
  }

  async adjustNumericField(path, delta) {
    const current = Number(foundry.utils.getProperty(this, path) ?? 0);
    let next = current + Number(delta);

    if (path === "system.sante.pv.value") {
      const max = Number(this.system.sante?.pv?.max ?? next);
      next = Math.max(0, Math.min(next, max));
    } else {
      next = Math.max(0, next);
    }

    return this.update({ [path]: next });
  }

  async applyIncomingDamage(damage, { useArmor = false } = {}) {
    const incoming = Math.max(0, Number(damage ?? 0));
    const pvBefore = Number(this.system.sante?.pv?.value ?? 0);
    const pvMax = Number(this.system.sante?.pv?.max ?? pvBefore);
    const armor = this.#getStoredArmorContext();
    const armorBefore = useArmor ? Number(armor.before ?? 0) : 0;
    const armorAbsorbed = Math.min(incoming, armorBefore);
    const armorAfter = Math.max(armorBefore - armorAbsorbed, 0);
    const hpDamage = Math.max(incoming - armorAbsorbed, 0);
    const pvAfter = Math.max(pvBefore - hpDamage, 0);

    if (useArmor && armor.hasArmor && armor.update && armorAfter !== armorBefore) {
      await armor.update(armorAfter);
    }

    if (hpDamage !== 0) {
      await this.update({ "system.sante.pv.value": pvAfter });
    }

    return {
      incoming,
      useArmor,
      armorLabel: armor.label,
      armorAvailable: armor.hasArmor,
      armorBefore,
      armorAbsorbed,
      armorAfter,
      hpDamage,
      pvBefore,
      pvAfter,
      pvMax
    };
  }

  async rollCharacteristic(key) {
    return DonjonEtCieRollDialog.createCharacteristic(this, key);
  }

  async useFavorService(departmentKey) {
    return game.system.donjonEtCie.rolls.useFavorService(this, departmentKey);
  }

  async rollInitiative() {
    return DonjonEtCieRollDialog.createInitiative(this);
  }

  async rollHitDice() {
    return game.system.donjonEtCie.rolls.rollHitDice(this);
  }

  async rollWeapon(itemId) {
    const item = this.items.get(itemId);
    if (item) return DonjonEtCieRollDialog.createWeapon(this, item);
  }

  async rollDamage(itemId) {
    const item = this.items.get(itemId);
    if (item) return DonjonEtCieRollDialog.createDamage(this, item);
  }

  async rollSpell(itemId) {
    const item = this.items.get(itemId);
    if (item) return DonjonEtCieRollDialog.createSpell(this, item);
  }

  async rollUsage(itemId) {
    const item = this.items.get(itemId);
    if (item) return DonjonEtCieRollDialog.createUsage(item);
  }

  async resetUsage(itemId) {
    const item = this.items.get(itemId);
    if (item?.type === "entrainement") return item.resetUsageDie();
  }

  getPnjAttacks() {
    if (this.type !== "pnj") return [];

    const attacks = Array.isArray(this.system.attaques) ? this.system.attaques : [];
    if (attacks.length) return attacks.map((attack, index) => ({
      index,
      nom: attack.nom || `Attaque ${index + 1}`,
      degats: attack.degats || "",
      notes: attack.notes || ""
    }));

    const legacy = this.system.attaque;
    if (legacy) {
      return [{
        index: 0,
        nom: legacy.nom || "Attaque",
        degats: legacy.degats || "",
        notes: legacy.notes || ""
      }];
    }

    return [{ index: 0, ...foundry.utils.deepClone(this.constructor.defaultPnjAttack) }];
  }

  async createPnjAttack() {
    if (this.type !== "pnj") return null;
    const attaques = this.getPnjAttacks().map(({ nom, degats, notes }) => ({ nom, degats, notes }));
    attaques.push(foundry.utils.deepClone(this.constructor.defaultPnjAttack));
    return this.update({ "system.attaques": attaques });
  }

  async deletePnjAttack(index) {
    if (this.type !== "pnj") return null;
    const attaques = this.getPnjAttacks().map(({ nom, degats, notes }) => ({ nom, degats, notes }));
    attaques.splice(Number(index), 1);
    if (!attaques.length) attaques.push(foundry.utils.deepClone(this.constructor.defaultPnjAttack));
    return this.update({ "system.attaques": attaques });
  }

  #createPnjResourceProxy({ label, deltaPath, protectionPath = null }) {
    const delta = Number(foundry.utils.getProperty(this, deltaPath) ?? 0);
    const protection = protectionPath ? Number(foundry.utils.getProperty(this, protectionPath) ?? 0) : 0;

    return {
      actor: this,
      type: protectionPath ? "armure" : "ressource",
      name: `${this.name} · ${label}`,
      system: {
        delta,
        resultatProtection: protection
      },
      update: async (data) => {
        const updateData = {};
        if (Object.hasOwn(data, "system.delta")) {
          updateData[deltaPath] = data["system.delta"];
        }
        if (protectionPath && Object.hasOwn(data, "system.resultatProtection")) {
          updateData[protectionPath] = data["system.resultatProtection"];
        }
        return Object.keys(updateData).length ? this.update(updateData) : this;
      }
    };
  }

  async rollPnjArmor() {
    return DonjonEtCieRollDialog.createUsage(this.#createPnjResourceProxy({
      label: "ARM",
      deltaPath: "system.defense.armure.delta",
      protectionPath: "system.defense.armure.resultatProtection"
    }));
  }

  async rollPnjCourage() {
    return DonjonEtCieRollDialog.createUsage(this.#createPnjResourceProxy({
      label: "COU",
      deltaPath: "system.defense.courage.delta"
    }));
  }

  async rollPnjAttackDamage(index = 0) {
    const attack = this.getPnjAttacks()[Number(index)] ?? null;
    const attackName = attack?.nom || "Attaque";
    const attackDamage = attack?.degats || "";
    if (!attackDamage) return null;

    return DonjonEtCieRollDialog.createDamage(this, {
      name: `${this.name} · ${attackName}`,
      type: "attaque",
      system: {
        degats: attackDamage,
        portee: attack?.notes || ""
      }
    });
  }
}
