/**
 * 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 { DONJON_ET_CIE } from "./donjon-et-cie-config.mjs";

export class DonjonEtCieUtility {
  static defaultItemIcons = {
    arme: "systems/fvtt-donjon-et-cie/assets/icons/system/items/arme.svg",
    armure: "systems/fvtt-donjon-et-cie/assets/icons/system/items/armure.svg",
    trait: "systems/fvtt-donjon-et-cie/assets/icons/system/items/trait.svg",
    sortilege: "systems/fvtt-donjon-et-cie/assets/icons/system/items/sortilege.svg",
    equipement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/equipement.svg",
    entrainement: "systems/fvtt-donjon-et-cie/assets/icons/system/items/capacite.svg",
    other: "systems/fvtt-donjon-et-cie/assets/icons/system/items/autre.svg"
  };

  static async preloadHandlebarsTemplates() {
    return foundry.applications.handlebars.loadTemplates([
      "systems/fvtt-donjon-et-cie/templates/actors/employe-sheet.hbs",
      "systems/fvtt-donjon-et-cie/templates/actors/pnj-sheet.hbs",
      "systems/fvtt-donjon-et-cie/templates/items/item-sheet.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/characteristic-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/initiative-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/weapon-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/damage-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/spell-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/usage-roll.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-dialog.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/mission-pack-campaign-dialog.hbs",
      "systems/fvtt-donjon-et-cie/templates/dialogs/employes-dialog.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/roll-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/spell-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/chaos-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/hit-dice-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/damage-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/damage-application-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/favor-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/initiative-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/usage-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/item-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/mission-pack-card.hbs",
      "systems/fvtt-donjon-et-cie/templates/chat/welcome-card.hbs"
    ]);
  }

  static getCharacteristicEntries(system) {
    return Object.entries(DONJON_ET_CIE.characteristics).map(([key, metadata]) => ({
      key,
      label: metadata.label,
      short: metadata.short,
      value: system.caracteristiques?.[key]?.value ?? 0
    }));
  }

  static formatUsageDie(value) {
    return value ? `Δ${value}` : "—";
  }

  static getDefaultItemIcon(type) {
    return this.defaultItemIcons[type] ?? this.defaultItemIcons.other;
  }

  static getCurrentSceneId() {
    return canvas?.scene?.id ?? game.scenes?.current?.id ?? "global";
  }

  static getSceneDamageTargets() {
    const scene = canvas?.scene ?? game.scenes?.current;
    const tokens = scene?.tokens?.contents ?? [];
    
    const targetedTokens = game.user?.targets ?? new Set();
    const targetedTokenUuid = targetedTokens?.first()?.document.uuid ?? null;

    return tokens
      .map((token) => {
        const actor = token.actor;
        if (!actor || !["employe", "pnj"].includes(actor.type)) return null;

        const tokenName = token.name || actor.name;
        const actorName = actor.name || tokenName;
        const label = tokenName === actorName ? tokenName : `${tokenName} (${actorName})`;

        return {
          tokenId: token.id,
          tokenUuid: token.uuid,
          actorUuid: actor.uuid,
          label,
          isSelected: token.uuid === targetedTokenUuid
        };
      })
      .filter(Boolean)
      .sort((a, b) => a.label.localeCompare(b.label, "fr", { sensitivity: "base" }));
  }

  static getMagicResourceContext(actor) {
    const rank = Number(actor.system.anciennete?.rang ?? actor.system.sante?.dv ?? 0);
    const focusDelta = Number(actor.system.magie?.focus?.delta ?? 0);
    const focusResult = Number(actor.system.magie?.focus?.resultat ?? 0);
    const focusSceneId = actor.system.magie?.focus?.sceneId ?? "";
    const activeFocus = focusSceneId === this.getCurrentSceneId() ? focusResult : 0;
    const chaosDelta = Number(actor.system.magie?.chaos?.delta ?? 12);

    return {
      rank,
      focusDelta,
      focusLabel: this.formatUsageDie(focusDelta),
      focusSceneId,
      focusStoredResult: focusResult,
      focusActiveValue: activeFocus,
      focusIsActive: activeFocus > 0,
      focusDisplay: activeFocus > 0 ? `${activeFocus} (${this.formatUsageDie(focusDelta)})` : "—",
      chaosDelta,
      chaosLabel: this.formatUsageDie(chaosDelta),
      chaosAvailable: chaosDelta >= 4
    };
  }

  static parseDieFormula(formula) {
    const normalized = String(formula ?? "").trim();
    if (!normalized) return null;

    const dieMatch = normalized.match(/(?<count>\d*)d(?<sides>\d+)/i);
    if (dieMatch?.groups?.sides) {
      return {
        formula: normalized,
        count: Number(dieMatch.groups.count || 1),
        countRaw: dieMatch.groups.count ?? "",
        sides: Number(dieMatch.groups.sides),
        match: dieMatch[0],
        index: dieMatch.index ?? 0
      };
    }

    if (/^\d+$/.test(normalized)) {
      const sides = Number(normalized);
      if ([4, 6, 8, 10, 12, 20].includes(sides)) {
        return {
          formula: normalized,
          count: 1,
          countRaw: "",
          sides,
          match: normalized,
          index: 0
        };
      }
    }

    return null;
  }

  static getMartialDamageContext(actor, item) {
    const isUsageDie = Boolean(item?.system?.degatsEstUsageDe);
    const degatsDelta = Number(item?.system?.degatsDelta ?? 0);
    const baseFormula = isUsageDie
      ? (degatsDelta > 0 ? `1d${degatsDelta}` : "")
      : String(item?.system?.degats ?? "").trim();

    const baseContext = {
      baseFormula,
      effectiveFormula: baseFormula,
      capped: false,
      martialDvFormula: String(actor?.system?.sante?.dv ?? "").trim(),
      martialDvSides: 0,
      weaponSides: 0,
      isUsageDie
    };

    if (actor?.type !== "employe" || item?.type !== "arme" || !baseFormula) {
      return baseContext;
    }

    const damageDie = this.parseDieFormula(baseFormula);
    const martialDie = this.parseDieFormula(actor.system.sante?.dv);
    if (!damageDie || !martialDie?.sides) return baseContext;

    const cappedSides = Math.min(damageDie.sides, martialDie.sides);
    if (cappedSides >= damageDie.sides) {
      return {
        ...baseContext,
        martialDvSides: martialDie.sides,
        weaponSides: damageDie.sides
      };
    }

    const replacement = `${damageDie.countRaw || ""}d${cappedSides}`;
    const effectiveFormula = damageDie.match === damageDie.formula
      ? replacement
      : `${damageDie.formula.slice(0, damageDie.index)}${replacement}${damageDie.formula.slice(damageDie.index + damageDie.match.length)}`;

    return {
      ...baseContext,
      effectiveFormula,
      capped: true,
      martialDvSides: martialDie.sides,
      weaponSides: damageDie.sides
    };
  }

  static getFavorLabel(key) {
    return DONJON_ET_CIE.favorDepartments[key] ?? key;
  }

  static getFavorEntries(system) {
    const favors = system.faveurs ?? {};
    return Object.entries(DONJON_ET_CIE.favorDepartments).map(([key, label]) => {
      const delta = Number(favors[key]?.delta ?? 0);
      return {
        key,
        label,
        delta,
        deltaLabel: this.formatUsageDie(delta),
        hasFavor: delta > 0
      };
    });
  }

  static getAvailableFavorOptions(actor) {
    return this.getFavorEntries(actor.system)
      .filter((entry) => entry.hasFavor)
      .map((entry) => ({ value: entry.key, label: `${entry.label} (${entry.deltaLabel})` }));
  }

  static getChaosTableEntries() {
    return Object.entries(DONJON_ET_CIE.chaosTable)
      .map(([value, entry]) => ({ value: Number(value), ...entry }))
      .sort((a, b) => a.value - b.value);
  }

  static degradeUsageDie(value) {
    const sequence = [12, 10, 8, 6, 4];
    const index = sequence.indexOf(Number(value));
    if (index === -1) return 0;
    return sequence[index + 1] ?? 0;
  }

  static sortByName(documents) {
    return [...documents].sort((a, b) => a.name.localeCompare(b.name, "fr", { sensitivity: "base" }));
  }

  static getWeaponCharacteristicKey(category) {
    return category === "distance" ? "dexterite" : "force";
  }

  static getWeaponCharacteristicLabel(category) {
    const key = this.getWeaponCharacteristicKey(category);
    return DONJON_ET_CIE.characteristics[key]?.label ?? key;
  }

  static enrichItemForSheet(item) {
    const system = item.system;
    const delta = Number(system.delta ?? 0);
    const deltaMax = Number(system.deltaMax ?? delta ?? 0);
    const ammunitionDelta = Number(system.munitionsDelta ?? 0);
    const isUsageDie = Boolean(system.degatsEstUsageDe);
    const degatsDelta = Number(system.degatsDelta ?? 0);
    const usageLabel = item.type === "entrainement" && deltaMax > 0
      ? `${this.formatUsageDie(delta)} / ${this.formatUsageDie(deltaMax)}`
      : delta > 0
        ? this.formatUsageDie(delta)
        : null;

    const damageUsageLabel = isUsageDie
      ? (degatsDelta > 0 ? this.formatUsageDie(degatsDelta) : game.i18n.localize("DNC.UI.DamageExhausted"))
      : null;
    const damageLabel = isUsageDie ? damageUsageLabel : (system.degats || null);

    return {
      id: item.id,
      name: item.name,
      type: item.type,
      img: item.img,
      system,
      uuid: item.uuid,
      usageLabel,
      ammunitionUsageLabel: item.type === "arme" && ammunitionDelta > 0 ? this.formatUsageDie(ammunitionDelta) : null,
      protectionLabel: item.type === "armure" && Number(system.resultatProtection ?? 0) > 0 ? `Protection ${system.resultatProtection}` : null,
      weaponCharacteristicLabel: item.type === "arme" ? this.getWeaponCharacteristicLabel(system.categorie) : null,
      canRoll: ["arme", "sortilege"].includes(item.type),
      canUse: delta > 0,
      hasTrackedAmmunition: item.type === "arme" && ammunitionDelta > 0,
      damageUsageLabel,
      damageLabel,
      canRollDamage: item.type === "arme" && (isUsageDie ? degatsDelta > 0 : Boolean(system.degats)),
      rollAction: item.type === "sortilege" ? "rollSpell" : "rollWeapon",
      damageAction: "rollDamage",
      isEquipped: Boolean(system.equipee),
      canReset: item.type === "entrainement" && deltaMax > 0 && delta !== deltaMax
    };
  }

  static buildActorSections(actor) {
    return Object.entries(DONJON_ET_CIE.actorSections).map(([key, metadata]) => {
      const types = DONJON_ET_CIE.sectionTypes[key];
      const items = this.sortByName(actor.items.filter((item) => types.includes(item.type))).map((item) => this.enrichItemForSheet(item));
      return {
        key,
        label: metadata.label,
        createType: metadata.createType,
        items
      };
    });
  }
}
