import { VermineUtils } from "../roll.mjs";
import { VermineExperience } from "../experience.mjs";

const { HandlebarsApplicationMixin } = foundry.applications.api;

/**
 * Dialogue d'apprentissage (phase d'Expérience).
 * Permet de dépenser des Dés d'Expérience pour tenter de développer une
 * Compétence, une Spécialité, une Caractéristique, une Réserve ou un
 * Dé d'Évolution. La progression est appliquée automatiquement en cas de
 * réussite.
 */
export default class LearningDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {

  #actor;

  static DEFAULT_OPTIONS = {
    classes: ["vermine-roll"],
    tag: "form",
    window: {
      icon: "fas fa-graduation-cap",
      resizable: false
    },
    position: {
      width: 560,
      height: 640
    },
    actions: {
      roll: LearningDialog.#onRoll,
      cancel: LearningDialog.#onCancel
    }
  };

  static PARTS = {
    main: { template: "systems/vermine2047/templates/dialogs/learning-dialog.hbs", scrollable: [""] }
  };

  static async create({ actorId }) {
    const actor = await game.actors.get(actorId);
    if (!actor) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected"));
      return null;
    }
    return new LearningDialog({ actor });
  }

  constructor(options = {}) {
    super(options);
    this.#actor = options.actor;
  }

  get title() {
    return game.i18n.localize("VERMINE.open_learning");
  }

  async _prepareContext() {
    const actor = this.#actor;
    const skills = Object.entries(CONFIG.VERMINE.skills).map(([key, cfg]) => ({
      key,
      name: game.i18n.localize("SKILLS." + key + ".name"),
      category: cfg.category,
      value: actor.system?.skills?.[key]?.value || 0,
      target: Math.min(5, (actor.system?.skills?.[key]?.value || 0) + 1),
      rarity: actor.system?.skills?.[key]?.rarity || 0,
      isDomain: VermineExperience.isPreferredDomain(actor, key)
    }));
    const totem = actor.system?.identity?.totem || "";
    const gameMode = this.#getGameMode();
    const groupLevel = this.#getGroupLevel();
    const totemAbilities = (actor.itemTypes?.ability || [])
      .filter(i =>
        i.system?.type === "character"
        && !i.system?.learned
        && i.system?.totem === totem
        && (i.system?.level?.value || 0) <= gameMode
        && groupLevel >= (i.system?.level?.value || 0)
      )
      .map(a => ({
        id: a.id,
        name: a.name,
        level: a.system.level.value,
        threshold: a.system.learn.threshold,
        hindrance: a.system.learn.hindrance
      }));
    const backgrounds = (actor.itemTypes?.background || [])
      .filter(i => !i.system?.learned)
      .map(b => ({ id: b.id, name: b.name, asterisk: b.system?.asterisk || false }));
    const human = actor.system?.adaptation?.totems?.human?.value || 0;
    const adapted = actor.system?.adaptation?.totems?.adapted?.value || 0;
    return {
      actor,
      system: actor.system,
      config: CONFIG.VERMINE,
      dice: actor.system?.experience?.dice || 0,
      ageType: actor.system?.identity?.ageType || 2,
      abilities: Object.entries(CONFIG.VERMINE.abilities).map(([key, label]) => ({
        key,
        label: game.i18n.localize(label),
        value: actor.system?.abilities?.[key]?.value || 0
      })),
      reserves: [
        { key: "self_control", label: game.i18n.localize("VERMINE.self_control"), value: actor.system?.attributes?.self_control?.value || 0, max: actor.system?.attributes?.self_control?.max || 0 },
        { key: "effort", label: game.i18n.localize("VERMINE.effort"), value: actor.system?.attributes?.effort?.value || 0, max: actor.system?.attributes?.effort?.max || 0 }
      ],
      specialties: actor.itemTypes?.specialty || [],
      skills,
      specialtyCount: (actor.itemTypes?.specialty || []).length,
      totem,
      gameMode,
      groupLevel,
      totemAbilities,
      backgrounds,
      reputation: actor.system?.attributes?.reputation?.value ?? 0,
      human,
      adapted,
      canUseAdaptedDie: VermineExperience.canUseAdaptedDie(human, adapted)
    };
  }

  #getGameMode() {
    return parseInt(game.settings.get("vermine2047", "game-mode") || "1", 10) || 1;
  }

  #getGroupLevel() {
    const groups = game.actors.filter(a => a.type === "group" && (a.system?.members || []).includes(this.#actor.id));
    return Math.max(0, ...groups.map(g => g.system?.level?.value || 0));
  }

  #canUseAdaptedDie() {
    const a = this.#actor.system?.adaptation?.totems;
    return VermineExperience.canUseAdaptedDie(a?.human?.value || 0, a?.adapted?.value || 0);
  }

  #totemCapacityCounts() {
    // Ne comptabilise que les Capacités du Totem courant du personnage
    // (les Capacités liées à un autre Totem ne bloquent pas les limites).
    const totem = this.#actor.system?.identity?.totem;
    const items = (this.#actor.itemTypes?.ability || []).filter(i =>
      i.system?.type === "character" && i.system?.learned && i.system?.totem === totem);
    const byLevel = { 1: 0, 2: 0, 3: 0 };
    for (const i of items) {
      const level = i.system?.level?.value || 0;
      byLevel[level] = (byLevel[level] || 0) + 1;
    }
    return { total: items.length, byLevel };
  }

  /**
   * Bonus de l'atout "Socle de compétences" (règles p. 112).
   * Rend le niveau (1-3) de l'atout possédé par le personnage, ou 0.
   * @returns {number}
   */
  #socleCompetencesBonus() {
    const item = (this.#actor.itemTypes?.ability || []).find(i =>
      i.system?.type === "character"
      && (i.name || "").trim().toLowerCase() === "socle de compétences".toLowerCase()
    );
    if (!item) return 0;
    return Math.min(3, Math.max(1, parseInt(item.system?.level?.value, 10) || 1));
  }

  async _onRender() {
    this.element.dataset.actorId = this.#actor.id;
    for (const inp of this.element.querySelectorAll("[data-roll]")) {
      inp.addEventListener("change", () => this.#updateUI());
    }
    this.element.querySelector("#learning-dice")?.addEventListener("input", () => this.#updateUI());
    this.#updateUI();
  }

  get #el() { return this.element; }

  #getType() {
    return this.#el.querySelector('input[name="learning-type"]:checked')?.value ?? "skill";
  }

  #getTargetKey() {
    const type = this.#getType();
    if (type === "characteristic") return this.#el.querySelector("#learning-ability")?.value ?? "";
    if (type === "reserve") return this.#el.querySelector("#learning-reserve")?.value ?? "";
    if (type === "totem") return this.#el.querySelector("#learning-totem")?.value ?? "";
    if (type === "background") return this.#el.querySelector("#learning-background")?.value ?? "";
    return this.#el.querySelector("#learning-target")?.value ?? "";
  }

  #getReputationDirection() {
    return this.#el.querySelector("#learning-reputation-direction")?.value ?? "reduce";
  }

  #isAdaptedDieChecked() {
    return Boolean(this.#el.querySelector("#learning-adapted-die")?.checked);
  }

  #getDiceSpent() {
    return Math.max(1, parseInt(this.#el.querySelector("#learning-dice")?.value, 10) || 1);
  }

  #getSpecialtyName() {
    // Le dialogue peut avoir été fermé pendant le jet (this.#el null).
    const el = this.#el?.querySelector("#specialty-name");
    return el?.value?.trim() || game.i18n.localize("ITEMS.new_specialty");
  }

  #compute() {
    const actor = this.#actor;
    const type = this.#getType();
    const key = this.#getTargetKey();
    let label = "";
    let targetLevel = null;

    if (type === "skill") {
      const current = key ? actor.system?.skills?.[key]?.value || 0 : 0;
      targetLevel = Math.min(5, current + 1);
      const rarity = key ? actor.system?.skills?.[key]?.rarity || 0 : 0;
      label = game.i18n.localize(VermineExperience.skillLevelLabelKey(targetLevel));
      const diff = VermineExperience.skillLearningDifficulty(targetLevel);
      const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
      // Atout "Socle de compétences" : 1D gratuit si la Compétence est déjà possédée.
      const socleBonus = current >= 1 ? this.#socleCompetencesBonus() : 0;
      return {
        type,
        key,
        targetLevel,
        difficulty: domain ? Math.max(3, diff - 2) : diff,
        handicap: VermineExperience.skillLearningHandicap(targetLevel, rarity),
        poolModifier: socleBonus,
        socleBonus,
        label,
        targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
      };
    }
    if (type === "specialty") {
      const current = key ? actor.system?.skills?.[key]?.value || 0 : 0;
      const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
      label = game.i18n.localize("VERMINE.specialty");
      return {
        type,
        key,
        targetLevel: current,
        difficulty: VermineExperience.specialtyLearningDifficulty(current, domain),
        handicap: VermineExperience.specialtyLearningHandicap(actor.itemTypes?.specialty?.length || 0),
        poolModifier: 0,
        label,
        targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
      };
    }
    if (type === "characteristic") {
      const current = key ? actor.system?.abilities?.[key]?.value || 0 : 0;
      label = game.i18n.localize("VERMINE.characteristic");
      return {
        type,
        key,
        targetLevel: current,
        difficulty: VermineExperience.characteristicLearningDifficulty(),
        handicap: current,
        poolModifier: 0,
        label,
        targetLabel: key ? game.i18n.localize(CONFIG.VERMINE.abilities[key]) : ""
      };
    }
    if (type === "reserve") {
      const current = key ? actor.system?.attributes?.[key]?.value || 0 : 0;
      label = game.i18n.localize("VERMINE.reserve");
      return {
        type,
        key,
        targetLevel: current,
        difficulty: VermineExperience.reserveLearningDifficulty(current),
        handicap: VermineExperience.ageHandicap(actor.system?.identity?.ageType || 2),
        poolModifier: 0,
        adaptedDie: false,
        label,
        targetLabel: key ? game.i18n.localize(key === "self_control" ? "VERMINE.self_control" : "VERMINE.effort") : ""
      };
    }
    if (type === "totem") {
      const item = actor.itemTypes?.ability?.find(i => i.id === key);
      const data = item?.system;
      label = game.i18n.localize("VERMINE.learn_totem_ability");
      return {
        type,
        key,
        targetLevel: data?.level?.value || 0,
        difficulty: VermineExperience.totemAbilityLearningDifficulty(data),
        handicap: VermineExperience.totemAbilityLearningHandicap(data),
        poolModifier: 0,
        adaptedDie: false,
        label,
        targetLabel: item?.name || ""
      };
    }
    if (type === "background") {
      const item = actor.itemTypes?.background?.find(i => i.id === key);
      const data = item?.system;
      label = game.i18n.localize("VERMINE.learn_background");
      return {
        type,
        key,
        targetLevel: 0,
        difficulty: VermineExperience.backgroundLearningDifficulty(),
        handicap: VermineExperience.backgroundLearningHandicap(data?.asterisk),
        poolModifier: 0,
        adaptedDie: false,
        label,
        targetLabel: item?.name || ""
      };
    }
    if (type === "reputation") {
      const current = actor.system?.attributes?.reputation?.value ?? 0;
      const direction = this.#getReputationDirection();
      if (direction === "increase") {
        return {
          type,
          key: "increase",
          targetLevel: current,
          difficulty: VermineExperience.reputationIncreaseDifficulty(current),
          handicap: 0,
          poolModifier: 0,
          adaptedDie: false,
          label: game.i18n.localize("VERMINE.learn_reputation"),
          targetLabel: game.i18n.localize("VERMINE.reputation_increase")
        };
      }
      return {
        type,
        key: "reduce",
        targetLevel: Math.max(0, current - 1),
        difficulty: VermineExperience.reputationReduceDifficulty(current),
        handicap: 0,
        poolModifier: 0,
        adaptedDie: false,
        label: game.i18n.localize("VERMINE.learn_reputation"),
        targetLabel: game.i18n.localize("VERMINE.reputation_reduce")
      };
    }
    // evolution
    const human = actor.system?.adaptation?.totems?.human?.value || 0;
    const adapted = actor.system?.adaptation?.totems?.adapted?.value || 0;
    label = game.i18n.localize("VERMINE.evolution_dice");
    return {
      type,
      key,
      targetLevel: actor.system?.experience?.evolution || 0,
      difficulty: VermineExperience.evolutionDifficulty(actor.system?.identity?.ageType || 2),
      handicap: actor.system?.experience?.evolution || 0,
      poolModifier: VermineExperience.evolutionTotemModifier(human, adapted),
      adaptedDie: this.#canUseAdaptedDie() && this.#isAdaptedDieChecked(),
      label,
      targetLabel: ""
    };
  }

  #updateUI() {
    const type = this.#getType();
    const show = (id, visible) => {
      const el = this.#el.querySelector(id);
      if (el) el.style.display = visible ? "" : "none";
    };
    show("#learning-skill-section", type === "skill" || type === "specialty");
    show("#learning-specialty-section", type === "specialty");
    show("#learning-ability-section", type === "characteristic");
    show("#learning-reserve-section", type === "reserve");
    show("#learning-evolution-section", type === "evolution");
    show("#learning-totem-section", type === "totem");
    show("#learning-background-section", type === "background");
    show("#learning-reputation-section", type === "reputation");
    show("#learning-adapted-die-row", type === "evolution" && this.#canUseAdaptedDie());

    // Pour une Spécialité, seule une compétence au moins Confirmée (≥2) est valable.
    const skillSelect = this.#el.querySelector("#learning-target");
    if (skillSelect) {
      for (const opt of skillSelect.options) {
        const value = parseInt(opt.dataset.value, 10) || 0;
        opt.hidden = (type === "specialty") ? value < 2 : false;
      }
    }

    const c = this.#compute();
    const set = (sel, txt) => { const el = this.#el.querySelector(sel); if (el) el.textContent = txt; };
    set("#diff-value", c.difficulty);
    set("#handicap-value", c.handicap);
    set("#required-value", 1 + c.handicap);
    const socleHint = this.#el.querySelector("#socle-bonus-hint");
    if (socleHint) {
      if (type === "skill" && c.socleBonus > 0) {
        socleHint.style.display = "";
        socleHint.textContent = game.i18n.format("VERMINE.socle_competences_hint", { level: c.socleBonus });
      } else {
        socleHint.style.display = "none";
      }
    }
    const pool = Math.max(0, this.#getDiceSpent() + c.poolModifier);
    let poolText = `${pool}D${c.poolModifier ? ` (${c.poolModifier >= 0 ? "+" : ""}${c.poolModifier}D)` : ""}`;
    if (c.adaptedDie) poolText += " — " + game.i18n.localize("VERMINE.adapted_die_x2");
    set("#pool-value", poolText);
    if (type === "reputation") {
      const current = this.#actor.system?.attributes?.reputation?.value ?? 0;
      const direction = this.#getReputationDirection();
      set("#reputation-target", direction === "increase"
        ? `${current} → ${Math.min(10, current + 1)}+`
        : `${current} → ${Math.max(0, current - 1)}`);
    }
    const poolInput = this.#el.querySelector("#learning-dice");
    if (poolInput) poolInput.max = this.#actor.system?.experience?.dice || 0;
  }

  static async #onCancel() {
    this.close();
  }

  static async #onRoll() {
    const actor = this.#actor;
    const ex = actor.system?.experience;
    const dice = ex?.dice || 0;
    const spent = Math.min(this.#getDiceSpent(), dice);
    if (spent < 1) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_no_experience_dice"));
      return;
    }

    const c = this.#compute();

    // Limite : une seule compétence, spécialité, réserve, caractéristique,
    // un seul Dé d'Évolution, Capacité de Totem, Historique ou jet de
    // Réputation par phase.
    if (ex?.learned?.phase === ex?.phase) {
      const flagMap = { skill: "skill", specialty: "specialty", reserve: "reserve", characteristic: "characteristic", evolution: "evolution", totem: "ability", background: "background", reputation: "reputation" };
      if (ex.learned[flagMap[c.type]]) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_learning_limit"));
        return;
      }
    }
    if (c.type === "skill" && c.targetLevel !== null && (actor.system?.skills?.[c.key]?.value || 0) >= 5) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_skill_max"));
      return;
    }
    if (c.type === "characteristic" && c.targetLevel !== null && (actor.system?.abilities?.[c.key]?.value || 0) >= 3) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_characteristic_max"));
      return;
    }
    if (c.type === "specialty" && c.targetLevel !== null && c.targetLevel < 2) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_specialty_requirement"));
      return;
    }
    if (c.type === "reserve" && c.targetLevel !== null && (actor.system?.attributes?.[c.key]?.max || 0) >= 10) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_reserve_max"));
      return;
    }
    if (c.type === "totem") {
      const item = actor.itemTypes?.ability?.find(i => i.id === c.key);
      if (!item || item.system?.learned) {
        ui.notifications.warn(game.i18n.localize("VERMINE.no_totem_ability"));
        return;
      }
      const level = item.system?.level?.value || 0;
      if (level > this.#getGameMode()) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_mode"));
        return;
      }
      if (item.system?.totem !== actor.system?.identity?.totem) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_totem"));
        return;
      }
      if (this.#getGroupLevel() < level) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_group_level"));
        return;
      }
      const counts = this.#totemCapacityCounts();
      if (level === 1 && (counts.byLevel[1] || 0) >= 3) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
        return;
      }
      if (level >= 2 && (counts.byLevel[level] || 0) >= 1) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
        return;
      }
      if (counts.total >= 5) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
        return;
      }
    }
    if (c.type === "background") {
      const item = actor.itemTypes?.background?.find(i => i.id === c.key);
      if (!item || item.system?.learned) {
        ui.notifications.warn(game.i18n.localize("VERMINE.no_background"));
        return;
      }
    }
    if (c.type === "reputation") {
      const current = actor.system?.attributes?.reputation?.value ?? 0;
      if (c.key === "reduce" && current <= 0) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_reputation_min"));
        return;
      }
      if (c.key === "increase" && current >= 10) {
        ui.notifications.warn(game.i18n.localize("VERMINE.error_reputation_max"));
        return;
      }
    }
    if (c.type === "evolution" && c.adaptedDie && !this.#canUseAdaptedDie()) {
      ui.notifications.warn(game.i18n.localize("VERMINE.error_evolution_no_adapted"));
      return;
    }

    const pool = Math.max(0, spent + c.poolModifier);
    const rollLabel = `${game.i18n.localize("VERMINE.open_learning")} — ${c.label}${c.targetLabel ? " (" + c.targetLabel + ")" : ""}`;

    const roll = await VermineUtils.roll({
      actor,
      NoD: pool,
      Reroll: 0,
      difficulty: c.difficulty,
      handicap: c.handicap,
      totems: c.adaptedDie ? { adapted: true } : { human: false, adapted: false },
      rollLabel,
      poolBreakdown: { ability: 0, skill: 0, selfControl: 0, specialty: 0, help: 0, tooling: 0, group: 0, total: pool },
      messageFlags: { "vermine-learning": { type: c.type, key: c.key, spent, success: false } }
    });

    const required = 1 + c.handicap;
    const specialtyName = this.#getSpecialtyName();
    if ((roll._total ?? 0) >= required) {
      await this.#applySuccess(c, specialtyName, roll._total ?? 0);
    }
    // Atout "Socle de compétences" : rappeler le bonus conféré aux camarades
    // du Groupe qui apprennent une Compétence possédée par le personnage.
    if (c.socleBonus > 0) {
      await ChatMessage.create({
        content: game.i18n.format("VERMINE.socle_competences_message", { level: c.socleBonus }),
        speaker: ChatMessage.getSpeaker({ actor })
      });
    }
    // Les Dés d'Expérience sont perdus que le jet soit réussi ou raté.
    await actor.update({ "system.experience.dice": Math.max(0, (actor.system.experience.dice || 0) - spent) });
    this.close();
  }

  async #applySuccess(c, specialtyName, successes = 0) {
    const actor = this.#actor;
    const updates = {};
    if (c.type === "skill") {
      const current = actor.system.skills[c.key].value || 0;
      updates["system.skills." + c.key + ".value"] = Math.min(5, current + 1);
      updates["system.experience.learned.skill"] = true;
    } else if (c.type === "specialty") {
      await actor.createEmbeddedDocuments("Item", [{ name: specialtyName, type: "specialty", system: { skill: c.key } }]);
      updates["system.experience.learned.specialty"] = true;
    } else if (c.type === "characteristic") {
      const current = actor.system.abilities[c.key].value || 0;
      updates["system.abilities." + c.key + ".value"] = Math.min(3, current + 1);
      updates["system.experience.learned.characteristic"] = true;
    } else if (c.type === "reserve") {
      const bonus = actor.system.experience.reserveBonus?.[c.key] || 0;
      updates["system.experience.reserveBonus." + c.key] = bonus + 1;
      updates["system.experience.learned.reserve"] = true;
    } else if (c.type === "evolution") {
      updates["system.experience.evolution"] = (actor.system.experience.evolution || 0) + 1;
      updates["system.experience.learned.evolution"] = true;
    } else if (c.type === "totem") {
      updates["system.experience.learned.ability"] = true;
      await actor.updateEmbeddedDocuments("Item", [{ _id: c.key, "system.learned": true }]);
    } else if (c.type === "background") {
      updates["system.experience.learned.background"] = true;
      await actor.updateEmbeddedDocuments("Item", [{ _id: c.key, "system.learned": true }]);
    } else if (c.type === "reputation") {
      const current = actor.system?.attributes?.reputation?.value ?? 0;
      if (c.key === "increase") {
        updates["system.attributes.reputation.value"] = Math.min(10, current + successes);
      } else {
        updates["system.attributes.reputation.value"] = Math.max(0, current - 1);
      }
      updates["system.experience.learned.reputation"] = true;
    }
    updates["system.experience.learned.phase"] = actor.system.experience.phase;
    await actor.update(updates);
    ui.notifications.info(game.i18n.localize("VERMINE.learning_success"));
  }
}
