import { VermineUtils } from './roll.mjs';

/**
 * Gestion de la Fiabilité du matériel (regles/regles_materiel.txt).
 *
 * Test de Fiabilité (Dommages matériels) : selon la gravité (Mineurs 1D,
 * Graves 3D, Critiques 5D), on lance les dés sans Relance ni Réserve ;
 * chaque dé >= Fiabilité inflige 1 Dommage (réduit la Fiabilité de 1).
 * À 0, l'équipement est détruit ou inutilisable.
 *
 * Réparations : jet de Précision + Compétence contre la Fiabilité d'origine
 * (classique) ou de Précision + Bricolage contre la Fiabilité actuelle
 * (de fortune). Chaque Réussite rend 1 point de Fiabilité (classique) ;
 * la réparation de fortune rend l'objet fonctionnel sans rendre de point.
 */
export class VermineReliability {
  /** Gravités et nombre de dés de Dommages (p. 37). */
  static SEVERITIES = { mineurs: 1, graves: 3, critiques: 5 };

  /** Handicap de réparation selon les Dommages subis (Mineure I, Grave II, Critique III). */
  static #PANNE_HANDICAP = { 1: 1, 2: 1, 3: 2, 4: 2, 5: 3 };

  /** Compétences de réparation classique (regles_materiel p. 39). */
  static REPAIR_SKILLS = ['crafting', 'mecanical', 'technology', 'armory'];

  /**
   * Handicap de la panne courante (0 si intact).
   * @param {Item} item
   * @returns {number}
   */
  static panneHandicap(item) {
    const value = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
    return this.#PANNE_HANDICAP[value] || 0;
  }

  /**
   * État courant dérivé des Dommages subis (tableau `damages.state`).
   * @param {Item} item
   * @returns {string}
   */
  static panneState(item) {
    const damages = item.system?.damages;
    const value = parseInt(damages?.value ?? 0, 10) || 0;
    if (value <= 0 || !damages?.state?.[value - 1]) return '';
    return game.i18n.localize(`VERMINE.damages_state_${value}`);
  }

  /**
   * Test de Fiabilité : jet de Dommages matériels.
   * @param {Item} item
   * @param {Object} opts
   * @param {string} opts.severity "mineurs" | "graves" | "critiques"
   * @param {boolean} opts.immersion si vrai, le Trait Étanche peut annuler les Dommages
   * @returns {Promise<Object|null>} Résultat (null si impossible)
   */
  static async test(item, { severity = 'graves', immersion = false } = {}) {
    if (!item) return null;
    const diceCount = this.SEVERITIES[severity] ?? 3;
    const current = parseInt(item.system?.reliability ?? 0, 10) || 0;
    const traits = item.system?.traits ?? {};
    const damagesValue = parseInt(item.system?.damages?.value ?? 0, 10) || 0;

    if (current <= 0) {
      ui.notifications.warn(game.i18n.localize('VERMINE.reliability_already_destroyed'));
      return { destroyed: true, dommages: 0 };
    }

    // Jet des dés de Dommages (sans Relance ni Réserve)
    const roll = new Roll(`${diceCount}d10`);
    await roll.evaluate();
    await VermineUtils.showDiceSoNice(roll);

    const results = (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => r.result ?? 0));
    const traitsNotes = [];

    // Trait Étanche : ignore les Dommages d'immersion tant que la Fiabilité
    // est à son maximum (objet sans Dommage cumulé).
    let dommages = results.filter((r) => r >= current).length;
    if (immersion && traits.etanche && damagesValue === 0) {
      dommages = 0;
      traitsNotes.push(game.i18n.localize('VERMINE.reliability_etanche'));
    }

    // Trait Coque (n) : ignore les (n) premiers Dommages puis s'épuise.
    // La coque épuisée est stockée { value: 0, max: n } : réparable (Phase 1).
    let coqueValue = parseInt(traits.coque?.value ?? 0, 10) || 0;
    const coqueInit = parseInt(traits.coque?.max ?? traits.coque?.value ?? 0, 10) || 0;
    const updates = {};
    if (coqueValue > 0 && dommages > 0) {
      const absorbed = Math.min(coqueValue, dommages);
      dommages -= absorbed;
      coqueValue -= absorbed;
      if (coqueValue > 0) {
        updates['system.traits.coque.value'] = coqueValue;
        traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_absorbed', { absorbed, remaining: coqueValue }));
      } else {
        updates['system.traits.coque'] = { value: 0, max: coqueInit };
        traitsNotes.push(game.i18n.format('VERMINE.reliability_coque_lost', { max: coqueInit }));
      }
    }

    // Trait Cassant : toutes les pertes de Fiabilité sont majorées d'1 point.
    const cassant = !!traits.cassant;
    if (cassant && dommages > 0) {
      dommages += 1;
      traitsNotes.push(game.i18n.localize('VERMINE.reliability_cassant'));
    }

    const newReliability = Math.max(0, current - dommages);
    const newDamagesValue = Math.min(5, damagesValue + dommages);
    const destroyed = newReliability === 0;

    updates['system.reliability'] = newReliability;
    updates['system.damages.value'] = newDamagesValue;
    await item.update(updates);

    // État final affiché sur la carte.
    let state = '';
    if (destroyed) {
      state = game.i18n.localize('VERMINE.reliability_destroyed');
    } else if (newDamagesValue > 0) {
      state = game.i18n.localize(`VERMINE.damages_state_${newDamagesValue}`);
    } else {
      state = '';
    }

    // Ne marque comme Dommage que les dés réellement comptés (après
    // absorption Étanche/Coque) : les dés absorbés restent affichés mais
    // sans le style "damage".
    const candidates = results.map((v, i) => ({ v, i })).filter((o) => o.v >= current);
    const damageIdx = new Set(candidates.slice(0, Math.max(0, dommages)).map((o) => o.i));

    const content = await foundry.applications.handlebars.renderTemplate(
      'systems/vermine2047/templates/reliability-test.hbs',
      {
        item: { name: item.name, img: item.img },
        severityLabel: game.i18n.localize(`VERMINE.reliability_severity_${severity}`),
        diceCount,
        oldReliability: current,
        newReliability,
        dommages,
        results: results.map((v, i) => ({ value: v, isDamage: damageIdx.has(i) })),
        state,
        destroyed,
        traitsNotes,
      },
    );

    await ChatMessage.create({
      user: game.user?._id,
      speaker: this.#getSpeaker(item),
      content,
    });

    return { dommages, newReliability, destroyed };
  }

  /**
   * Réparation classique, de fortune ou de coque.
   * @param {Item} item
   * @param {Object} opts
   * @param {string} opts.mode "classique" | "fortune" | "coque"
   * @param {string} opts.skill Compétence de réparation classique (clé de skills)
   * @returns {Promise<Object|null>}
   */
  static async repair(item, { mode = 'classique', skill = 'technology' } = {}) {
    const actor = item?.actor;
    if (!item || !actor) {
      ui.notifications.warn(game.i18n.localize('VERMINE.reliability_need_actor'));
      return null;
    }

    const traits = item.system?.traits ?? {};
    const current = parseInt(item.system?.reliability ?? 0, 10) || 0;
    const damagesValue = parseInt(item.system?.damages?.value ?? 0, 10) || 0;
    const handicap = this.panneHandicap(item);

    // Trait Pratique : +1 Handicap sur toutes les réparations.
    const pratique = !!traits.pratique;
    const pratiqueHandicap = pratique ? 1 : 0;
    const pratiqueNote = pratique ? game.i18n.localize('VERMINE.reliability_pratique') : '';

    const isCoque = mode === 'coque';
    const coqueMax = isCoque ? parseInt(traits.coque?.max ?? 0, 10) || 0 : 0;

    // Difficulté : Fiabilité d'origine (≈ actuelle + Dommages subis) pour la
    // réparation classique ; Fiabilité actuelle pour la réparation de fortune ;
    // valeur maximale du Trait pour la réparation de coque.
    let difficulty = current;
    if (isCoque) difficulty = coqueMax;
    else if (mode === 'classique') difficulty = Math.min(10, current + damagesValue);

    const precision = parseInt(actor.system?.abilities?.precision?.value ?? 0, 10) || 0;
    const skillValue = mode === 'fortune'
      ? parseInt(actor.system?.skills?.diy?.value ?? 0, 10) || 0
      : parseInt(actor.system?.skills?.[skill]?.value ?? 0, 10) || 0;
    const NoD = Math.max(0, precision + skillValue);

    // Réparation de fortune (p. 39) : si le personnage possède la Compétence
    // requise, +1D sur le jet ; sinon, Handicap de Rareté de cette Compétence
    // (le Bonus de Bricolage s'applique dans les deux cas).
    const skillRarity = parseInt(actor.system?.skills?.[skill]?.rarity ?? 0, 10) || 0;
    const hasSkill = (parseInt(actor.system?.skills?.[skill]?.value ?? 0, 10) || 0) > 0;
    const fortuneBonus = mode === 'fortune' && hasSkill ? 1 : 0;
    const fortuneHandicap = mode === 'fortune' && !hasSkill ? skillRarity : 0;
    let fortuneNote = '';
    if (mode === 'fortune') {
      if (hasSkill) {
        fortuneNote = game.i18n.format('VERMINE.reliability_fortune_skill_bonus', { skill: game.i18n.localize(`SKILLS.${skill}.name`) });
      } else if (skillRarity > 0) {
        fortuneNote = game.i18n.format('VERMINE.reliability_fortune_skill_missing', { skill: game.i18n.localize(`SKILLS.${skill}.name`), rarity: skillRarity });
      }
    }

    // Trait Bien conçu : Réussite automatique sur les Réparations classiques.
    const bienConcu = !!traits.bien_conçu;
    const autoSuccess = bienConcu && mode === 'classique';
    let bienConcuNote = '';

    // Trait Bien conçu (fortune) : la 1ʳᵉ réparation de fortune réussie rend
    // 1 point de Fiabilité (compteur traits.bien_conçu.value).
    const bienConcuUsed = bienConcu && mode === 'fortune' && (parseInt(traits.bien_conçu?.value ?? 0, 10) || 0) > 0;
    const bienConcuBonus = bienConcu && mode === 'fortune' && !bienConcuUsed;

    // Réparation de coque : pas de panne à réparer, Difficulté = max du Trait.
    const required = 1 + (isCoque ? 0 : handicap) + (isCoque ? 0 : fortuneHandicap) + pratiqueHandicap;

    const roll = autoSuccess ? null : new Roll(`${NoD + fortuneBonus}d10cs>=${difficulty}`);
    if (roll) {
      await roll.evaluate();
      await VermineUtils.showDiceSoNice(roll);
    }

    const results = roll
      ? (roll.dice ?? []).flatMap((d) => (d.results ?? []).map((r) => ({
        value: r.result ?? 0,
        isSuccess: r.success,
      })))
      : [];
    const successes = roll?._total ?? required;
    const success = autoSuccess || successes >= required;

    let gained = 0;
    let fortuneFixed = false;
    let coqueRestored = false;
    let newReliability = null;
    let resultLabel = '';
    if (success) {
      if (isCoque) {
        coqueRestored = true;
        resultLabel = game.i18n.format('VERMINE.reliability_coque_restored', { max: coqueMax });
      } else if (mode === 'classique') {
        gained = autoSuccess ? damagesValue : successes;
        newReliability = Math.min(10, current + gained);
        resultLabel = game.i18n.format('VERMINE.reliability_gained', { gained });
        if (autoSuccess) {
          bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu');
        }
      } else {
        fortuneFixed = true;
        if (bienConcuBonus) {
          gained = 1;
          newReliability = Math.min(10, current + 1);
          bienConcuNote = game.i18n.localize('VERMINE.reliability_bien_concu_fortune');
        }
        resultLabel = game.i18n.localize('VERMINE.reliability_fixed');
      }
    } else {
      resultLabel = game.i18n.localize('VERMINE.reliability_failed');
    }

    const updates = {};
    if (isCoque && coqueRestored) {
      updates['system.traits.coque'] = { value: coqueMax, max: coqueMax };
    } else if (mode === 'classique' && gained > 0) {
      updates['system.reliability'] = newReliability;
      updates['system.damages.value'] = Math.max(0, damagesValue - gained);
    } else if (mode === 'fortune' && fortuneFixed) {
      updates['system.damages.value'] = 0;
      if (bienConcuBonus) {
        updates['system.reliability'] = newReliability;
        updates['system.traits.bien_conçu'] = { value: 1 };
      }
    }
    if (Object.keys(updates).length) await item.update(updates);

    const skillLabel = mode === 'fortune'
      ? game.i18n.localize('SKILLS.diy.name')
      : game.i18n.localize(`SKILLS.${skill}.name`);
    const modeLabel = isCoque
      ? game.i18n.localize('VERMINE.reliability_repair_coque_label')
      : game.i18n.localize(mode === 'classique' ? 'VERMINE.reliability_repair_classic' : 'VERMINE.reliability_repair_fortune');

    const content = await foundry.applications.handlebars.renderTemplate(
      'systems/vermine2047/templates/reliability-repair.hbs',
      {
        item: { name: item.name, img: item.img },
        modeLabel,
        skillLabel,
        difficulty,
        handicap: isCoque ? 0 : handicap,
        pratiqueHandicap,
        results,
        successes,
        required,
        resultLabel,
        autoSuccess,
        fortuneNote,
        bienConcuNote,
        pratiqueNote,
      },
    );

    await ChatMessage.create({
      user: game.user?._id,
      speaker: this.#getSpeaker(item),
      content,
    });

    return { success, gained, successes, required };
  }

  static #getSpeaker(item) {
    return ChatMessage.getSpeaker({ actor: item?.actor ?? null });
  }
}
