import {
  onManageActiveEffect,
  prepareActiveEffectCategories,
} from '../helpers/effects.mjs';

/**
 * Extend the basic ActorSheet with some very simple modifications
 * @extends {foundry.appv1.sheets.ActorSheet}
 */
export class MyriadSystemActorSheet extends foundry.appv1.sheets.ActorSheet {
  /** @override */
  static get defaultOptions() {
    return foundry.utils.mergeObject(super.defaultOptions, {
      classes: ['myriad-system', 'sheet', 'actor'],
      width: 600,
      height: 600,
      tabs: [
        {
          navSelector: '.sheet-tabs',
          contentSelector: '.sheet-body',
          initial: 'features',
        },
      ],
    });
  }

  /** @override */
  get template() {
    return `systems/myriad-system/templates/actor/actor-${this.actor.type}-sheet.hbs`;
  }

  /* -------------------------------------------- */

  /** @override */
  async getData() {
    // Retrieve the data structure from the base sheet.
    const context = await super.getData();

    // Add the actor's data to context.actor for easier access
    context.actor = this.actor;
    context.system = this.actor.system;
    context.flags = this.actor.flags;

    // Adding a pointer to CONFIG.MYRIAD_SYSTEM
    context.config = CONFIG.MYRIAD_SYSTEM;

    // Prepare character data and items.
    if (this.actor.type === 'character') {
      this._prepareItems(context);
      this._prepareCharacterData(context);
  context.actionPoints = this.actor.system.actionPoints;
    }

    // Prepare NPC data and items.
    if (this.actor.type === 'npc') {
      this._prepareItems(context);
    }

    // Enrich biography info for display
    // Enrichment turns text like `[[/r 1d20]]` into buttons
    context.enrichedBiography = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
      this.actor.system.biography,
      {
        // Whether to show secret blocks in the finished html
        secrets: this.document.isOwner,
        async: true,
        // Data to fill in for inline rolls
        rollData: this.actor.getRollData(),
        // Relative UUID resolution
        relativeTo: this.actor,
      }
    );

    // Prepare active effects
    context.effects = prepareActiveEffectCategories(
      // A generator that returns all effects stored on the actor
      // as well as any items
      this.actor.allApplicableEffects()
    );

  // Determine if this actor is currently in combat (any combatant references it)
  context.inCombat = !!game.combat?.combatants.find(c => c.actor?.id === this.actor.id);

    return context;
  }

  /**
   * Character-specific context modifications
   *
   * @param {object} context The context object to mutate
   */
  _prepareCharacterData(context) {
    try {
      // Determine if magic points should be displayed (character has arts and domains)
      const hasArts = Array.isArray(context.arts) && context.arts.length > 0;
      const hasDomains = Array.isArray(context.domains) && context.domains.length > 0;
      context.hasMagic = hasArts && hasDomains;
      
    } catch (error) {
      console.error("Error in _prepareCharacterData:", error);
    }
  }

  /**
   * Organize and classify Items for Actor sheets.
   *
   * @param {object} context The context object to mutate
   */
  _prepareItems(context) {
    // Initialize containers.
    const gear = [];
    const features = [];
    const paths = [];
    const arts = [];
    const domains = [];
    const overflowItems = [];
    const spells = { 1: [], 2: [], 3: [], 4: [], 5: [] };

    const roman = (n) => ({1: 'I', 2: 'II', 3: 'III', 4: 'IV', 5: 'V'}[Number(n)] || 'I');

    // Iterate through items, allocating to containers
    for (let i of context.items) {
      i.img = i.img || Item.DEFAULT_ICON;
      // Ensure a displayGrade field for list rendering
      const g = Number(i.system?.grade ?? 0);
      i.displayGrade = roman(g);

      if (i.type === 'item') gear.push(i);
      else if (i.type === 'feature') features.push(i);
      else if (i.type === 'path') paths.push(i);
      else if (i.type === 'art') arts.push(i);
      else if (i.type === 'domain') domains.push(i);
      else if (i.type === 'spell') {
        if (g >= 1 && g <= 5) spells[g].push(i);
        else overflowItems.push(i);
      } else overflowItems.push(i);
    }

    // Assign and return
    context.gear = gear;
    context.features = features;
    context.paths = paths;
    context.arts = arts;
    context.domains = domains;
    context.overflowItems = overflowItems;
    context.spells = spells;
  }

  /* -------------------------------------------- */

  /** @override */
  activateListeners(html) {
    super.activateListeners(html);

    // Render the item sheet for viewing/editing prior to the editable check.
    html.on('click', '.item-edit', (ev) => {
      const li = $(ev.currentTarget).parents('.item');
      const item = this.actor.items.get(li.data('itemId'));
      item.sheet.render(true);
    });

    // -------------------------------------------------------------
    // Everything below here is only needed if the sheet is editable
    if (!this.isEditable) return;

    // Add Inventory Item
    html.on('click', '.item-create', this._onItemCreate.bind(this));

    // Delete Inventory Item
    html.on('click', '.item-delete', (ev) => {
      const li = $(ev.currentTarget).parents('.item');
      const item = this.actor.items.get(li.data('itemId'));
      item.delete();
      li.slideUp(200, () => this.render(false));
    });

    // Active Effect management
    html.on('click', '.effect-control', (ev) => {
      const row = ev.currentTarget.closest('li');
      const document =
        row.dataset.parentId === this.actor.id
          ? this.actor
          : this.actor.items.get(row.dataset.parentId);
      onManageActiveEffect(ev, document);
    });

    // Rollable abilities.
    html.on('click', '.rollable', this._onRoll.bind(this));

    // Characteristic rolls
    html.on('click', '.characteristic-roll', this._onCharacteristicRoll.bind(this));
    
    // Characteristic temporary damage
    html.on('click', '.characteristic-damage', this._onCharacteristicDamage.bind(this));
    
    // Characteristic temporary bonus
    html.on('click', '.characteristic-bonus', this._onCharacteristicBonus.bind(this));

    // XP controls
    html.on('click', '.xp-control', this._onXPControl.bind(this));

    // Drag events for macros.
    if (this.actor.isOwner) {
      let handler = (ev) => this._onDragStart(ev);
      html.find('li.item').each((i, li) => {
        if (li.classList.contains('inventory-header')) return;
        li.setAttribute('draggable', true);
        li.addEventListener('dragstart', handler, false);
      });
    }
  }

  /**
   * Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
   * @param {Event} event   The originating click event
   * @private
   */
  async _onItemCreate(event) {
    event.preventDefault();
    const header = event.currentTarget;
    // Get the type of item to create.
    const type = header.dataset.type;
    // Grab any data associated with this control.
    const data = foundry.utils.duplicate(header.dataset);
    // Initialize a default name.
    const name = `New ${type.capitalize()}`;
    // Prepare the item object.
    const itemData = {
      name: name,
      type: type,
      system: data,
    };
    // Remove the type from the dataset since it's in the itemData.type prop.
    delete itemData.system['type'];

    // Finally, create the item!
    return await Item.create(itemData, { parent: this.actor });
  }
  
  /**
   * Handle dropping of an item reference or item data onto an Actor Sheet
   * @param {DragEvent} event     The concluding DragEvent which contains drop data
   * @param {Object} data         The data transfer extracted from the event
   * @override
   */
  async _onDropItem(event, data) {
    if (!this.actor.isOwner) return false;
    // Defer to base implementation; legacy 'voie' special handling removed
    return super._onDropItem(event, data);
  }
  
  /**
   * Handle XP control buttons
   * @param {Event} event   The originating click event
   * @private
   */
  async _onXPControl(event) {
    event.preventDefault();
    const element = event.currentTarget;
    const action = element.dataset.action;
    
    if (!this.actor.system.xp) return;

    const currentEarned = this.actor.system.xp.earned || 0;
    let newEarned = currentEarned;

    if (action === 'increase-xp') {
      newEarned = currentEarned + 1;
    } else if (action === 'decrease-xp') {
      newEarned = Math.max(0, currentEarned - 1);
    }

    if (newEarned !== currentEarned) {
      await this.actor.update({ 'system.xp.earned': newEarned });
    }
  }
  
  /**
   * Handle characteristic damage button click
   * @param {Event} event The originating click event
   * @private 
   */
  _onCharacteristicDamage(event) {
    event.preventDefault();
    const element = event.currentTarget;
    const characteristic = element.dataset.characteristic;
    
    if (!characteristic || !this.actor.isOwner) return;
    
    // Afficher un dialogue pour configurer les dégâts
    const dialog = new Dialog({
      title: `Appliquer des Dégâts à ${game.i18n.localize(`MYRIAD_SYSTEM.Characteristic.${characteristic.capitalize()}`)}`,
      content: `
        <form>
          <div class="form-group">
            <label>Montant des dégâts:</label>
            <input type="number" name="damage" value="5" min="1">
          </div>
          <div class="form-group">
            <label>Durée (rounds):</label>
            <input type="number" name="duration" value="10" min="1">
          </div>
          <div class="form-group">
            <label>Raison:</label>
            <input type="text" name="reason" value="">
          </div>
        </form>
      `,
      buttons: {
        apply: {
          icon: '<i class="fas fa-check"></i>',
          label: "Appliquer",
          callback: html => {
            const damage = Math.max(1, parseInt(html.find('[name=damage]').val()) || 5);
            const duration = Math.max(1, parseInt(html.find('[name=duration]').val()) || 10);
            const reason = html.find('[name=reason]').val();
            
            // Si le système d'effets est disponible, utiliser applyCharacteristicDamage
            if (game.myriad?.effects?.applyCharacteristicDamage) {
              game.myriad.effects.applyCharacteristicDamage(this.actor, characteristic, damage, {
                duration: duration,
                reason: reason
              });
            } else {
              // Fallback si le système d'effets n'est pas disponible
              ui.notifications.error("Le système d'effets n'est pas disponible.");
            }
          }
        },
        remove: {
          icon: '<i class="fas fa-trash"></i>',
          label: "Supprimer Existant",
          callback: () => {
            if (game.myriad?.effects?.removeTemporaryDamageEffect) {
              game.myriad.effects.removeTemporaryDamageEffect(this.actor, characteristic);
            } else {
              ui.notifications.error("Le système d'effets n'est pas disponible.");
            }
          }
        },
        cancel: {
          icon: '<i class="fas fa-times"></i>',
          label: "Annuler"
        }
      },
      default: "apply"
    });
    
    dialog.render(true);
  }

  /**
   * Handle characteristic bonus button click
   * @param {Event} event The originating click event
   * @private 
   */
  _onCharacteristicBonus(event) {
    event.preventDefault();
    const element = event.currentTarget;
    const characteristic = element.dataset.characteristic;
    
    if (!characteristic || !this.actor.isOwner) return;
    
    // Afficher un dialogue pour configurer le bonus
    const dialog = new Dialog({
      title: `Appliquer un Bonus à ${game.i18n.localize(`MYRIAD_SYSTEM.Characteristic.${characteristic.capitalize()}`)}`,
      content: `
        <form>
          <div class="form-group">
            <label>Montant du bonus:</label>
            <input type="number" name="bonus" value="5" min="1">
          </div>
          <div class="form-group">
            <label>Durée (rounds):</label>
            <input type="number" name="duration" value="10" min="1">
          </div>
          <div class="form-group">
            <label>Raison:</label>
            <input type="text" name="reason" value="">
          </div>
        </form>
      `,
      buttons: {
        apply: {
          icon: '<i class="fas fa-check"></i>',
          label: "Appliquer",
          callback: html => {
            const bonus = Math.max(1, parseInt(html.find('[name=bonus]').val()) || 5);
            const duration = Math.max(1, parseInt(html.find('[name=duration]').val()) || 10);
            const reason = html.find('[name=reason]').val;
            
            // Si le système d'effets est disponible, utiliser applyCharacteristicBonus
            if (game.myriad?.effects?.applyCharacteristicBonus) {
              game.myriad.effects.applyCharacteristicBonus(this.actor, characteristic, bonus, {
                duration: duration,
                reason: reason
              });
            } else {
              // Fallback si le système d'effets n'est pas disponible
              ui.notifications.error("Le système d'effets n'est pas disponible.");
            }
          }
        },
        cancel: {
          icon: '<i class="fas fa-times"></i>',
          label: "Annuler"
        }
      },
      default: "apply"
    });
    
    dialog.render(true);
  }

  /**
   * Handle characteristic roll button click
   * @param {Event} event The originating click event
   * @private 
   */
  async _onCharacteristicRoll(event) {
    event.preventDefault();
    const element = event.currentTarget;
    const characteristic = element.dataset.characteristic;
    
    if (!characteristic || !this.actor.isOwner) return;
    
    // Récupérer la valeur effective de la caractéristique
    const charData = this.actor.system.characteristics[characteristic];
    if (!charData) {
      return ui.notifications.warn(`Caractéristique non trouvée: ${characteristic}`);
    }
    
    const charValue = charData.value || 0;
    const bonus = charData.bonus || 0;
    const malus = charData.malus || 0;
    const total = charValue + bonus - malus;
    
    // Créer le jet de dés (d100 <= charValue)
    const roll = new Roll("1d100");
    await roll.evaluate();
    const d100Result = roll.total;
    
    // Déterminer le résultat
    let success = d100Result <= total;
    let critical = false;
    let fumble = false;
    
    // Règles pour réussites et échecs critiques
    if (d100Result === 1) {
      success = true;
      critical = true;
    } else if (d100Result === 100) {
      success = false;
      fumble = true;
    } else {
      // Doubles réussis (11, 22, etc.) et valeur inférieure à la caractéristique
      const tens = Math.floor(d100Result / 10);
      const units = d100Result % 10;
      if (tens === units && tens <= 5 && success) {
        critical = true;
      }
      // Doubles ratés (66, 77, etc.) et valeur supérieure à la caractéristique
      else if (tens === units && tens >= 6 && !success) {
        fumble = true;
      }
    }
    
    // Calculer la marge de réussite/échec
    const margin = success ? total - d100Result : d100Result - total;
    
    // Construire le message de chat
    const rollResult = success ? "Réussite" : "Échec";
    const resultClass = success ? "success" : "failure";
    const criticalText = critical ? " (Critique!)" : (fumble ? " (Échec critique!)" : "");
    const charName = game.i18n.localize(`MYRIAD_SYSTEM.Characteristic.${characteristic.capitalize()}`);
    
    const messageContent = `
      <div class="dice-roll">
        <div class="dice-result">
          <div class="dice-formula">${charName} (${total})</div>
          <div class="dice-tooltip">
            <section class="tooltip-part">
              <div class="dice">
                <header class="part-header flexrow">
                  <span class="part-formula">1d100</span>
                  <span class="part-total">${d100Result}</span>
                </header>
              </div>
            </section>
          </div>
          <h4 class="dice-total ${resultClass}">${rollResult}${criticalText} (Marge: ${margin})</h4>
        </div>
      </div>
    `;
    
    await ChatMessage.create({
      speaker: ChatMessage.getSpeaker({ actor: this.actor }),
      content: messageContent,
      sound: CONFIG.sounds.dice,
      type: CONST.CHAT_MESSAGE_TYPES.ROLL,
      roll: roll
    });
  }

  /**
   * Handle clickable rolls.
   * @param {Event} event   The originating click event
   * @private
   */
  _onRoll(event) {
    event.preventDefault();
    const element = event.currentTarget;
    const dataset = element.dataset;

    // Handle item rolls.
    if (dataset.rollType) {
      if (dataset.rollType == 'item') {
        const itemId = element.closest('.item').dataset.itemId;
        const item = this.actor.items.get(itemId);
        if (item) return item.roll();
      }
    }

    // Handle rolls that supply the formula directly.
    if (dataset.roll) {
      let label = dataset.label ? `[ability] ${dataset.label}` : '';
      let roll = new Roll(dataset.roll, this.actor.getRollData());
      roll.toMessage({
        speaker: ChatMessage.getSpeaker({ actor: this.actor }),
        flavor: label,
        rollMode: game.settings.get('core', 'rollMode'),
      });
      return roll;
    }
  }
}
