import { ChatUtility } from "../chat-utility.js";
import { HIDE_DICE, SYSTEM_RDD } from "../constants.js";
import { Grammar } from "../grammar.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js";

const COMPENDIUM_SETTING_PREFIX = 'compendium-';

const CONFIGURABLE_COMPENDIUMS = {
  'tables-diverses': { label: "Tables aléatoires", type: "RollTable" },
  'competences': { label: "Compétences", type: "Item" },
  'extrait-poetique': { label: "Extraits poetiques", type: "Item" },
  'queues-de-dragon': { label: "Queues de dragon", type: "Item" },
  'ombres-de-thanatos': { label: "Ombres de Thanatos", type: "Item" },
  'souffles-de-dragon': { label: "Souffles de Dragon", type: "Item" },
  'tarot-draconique': { label: "Tarots draconiques", type: "Item" },
  'rencontres': { label: "Rencontres dans les TMR", type: "Item" },
  'tetes-de-dragon-pour-haut-revants': { label: "Têtes de dragons (haut-rêvant)", type: "Item" },
  'tetes-de-dragon-pour-tous-personnages': { label: "Têtes de dragons (tous)", type: "Item" },
  'faune-flore-mineraux': { label: "Herbes & plantes", type: "Item" },
  'equipement': { label: "Equipements", type: "Item" },
}

/**
 * ======= Gestion des accès aux compendiums systèmes (ou surchargés) =======
 */
export class SystemCompendiums extends FormApplication {
  static initSettings() {
    Object.keys(CONFIGURABLE_COMPENDIUMS).forEach(compendium => {
      const definition = CONFIGURABLE_COMPENDIUMS[compendium];
      foundry.utils.mergeObject(definition, {
        compendium: compendium,
        default: SystemCompendiums._getDefaultCompendium(compendium),
        setting: SystemCompendiums._getSettingCompendium(compendium)
      })

      game.settings.register(SYSTEM_RDD, definition.setting, {
        name: definition.label,
        default: definition.default,
        scope: "world",
        config: false,
        type: String
      })
    })

    game.settings.registerMenu(SYSTEM_RDD, "compendium-settings", {
      name: "Choisir les compendiums système",
      label: "Compendiums système",
      hint: "Ouvre la fenêtre de sélection des compendiums système",
      icon: "fas fa-bars",
      restricted: true,
      type: SystemCompendiums
    })
  }

  static getPack(compendium) {
    const pack = game.packs.get(compendium);
    if (pack) {
      return pack;
    }
    return game.packs.get(SystemCompendiums.getCompendium(compendium)) ?? game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
  }

  static async getPackContent(compendium, docType) {
    const pack = SystemCompendiums.getPack(compendium);
    if (pack?.metadata.type == docType) {
      return await pack.getDocuments();
    }
    return [];
  }

  static async getCompetences(actorType) {
    switch (actorType ?? 'personnage') {
      case 'personnage':
        return await SystemCompendiums.getWorldOrCompendiumItems('competence', 'competences')
      case 'entite':
      case 'creature':
        return await SystemCompendiums.getWorldOrCompendiumItems('competencecreature', 'competences-creatures')
      case 'vehicule': return [];
    }
  }

  /* -------------------------------------------- */
  static async getWorldOrCompendiumItems(itemType, compendium) {
    let items = game.items.filter(it => it.type == itemType)
    if (compendium) {
      const ids = items.map(it => it.id)
      const names = items.map(it => Grammar.toLowerCaseNoAccent(it.name))
      const compendiumItems = await SystemCompendiums.getItems(compendium, itemType)
      return items.concat(compendiumItems
        .filter(it => !ids.includes(it.id))
        .filter(it => !names.includes(Grammar.equalsInsensitive(it.name))))
    }
    return items
  }

  static async loadDocument(document) {
    const pack = game.packs.get(document.pack);
    return await pack.getDocument(document.id ?? document._id);
  }

  static async getItems(compendium, itemType = undefined) {
    const items = await SystemCompendiums.getPackContent(compendium, 'Item');
    return (itemType ? items.filter(it => it.type == itemType) : items);
  }

  static async getContent(compendium, type, filter, itemFrequence, sorting) {
    let elements = await SystemCompendiums.getPackContent(compendium, type);
    elements = elements.filter(filter).filter(it => itemFrequence(it) > 0);
    if (sorting) {
      elements = elements.sort(sorting);
    }
    return elements;
  }

  /* -------------------------------------------- */
  static async loadCompendiumData(compendium) {
    const pack = game.packs.get(compendium);
    return await pack?.getDocuments() ?? [];
  }

  /* -------------------------------------------- */
  static async loadCompendium(compendium, filter = item => true) {
    let compendiumData = await SystemCompendiums.loadCompendiumData(compendium);
    return compendiumData.filter(filter);
  }


  static async getDefaultItems(compendium) {
    const pack = game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
    if (pack.metadata.type == 'Item') {
      return await pack.getDocuments();
    }
    return [];
  }

  static getCompendium(compendium) {
    const setting = CONFIGURABLE_COMPENDIUMS[compendium]?.setting;
    return setting ? game.settings.get(SYSTEM_RDD, setting) : SystemCompendiums._getDefaultCompendium(compendium);
  }

  static _getSettingCompendium(compendium) {
    return COMPENDIUM_SETTING_PREFIX + compendium;
  }

  static _getDefaultCompendium(compendium) {
    return `${SYSTEM_RDD}.${compendium}`;
  }

  constructor(...args) {
    super(...args);
  }

  static get defaultOptions() {
    const options = super.defaultOptions;
    foundry.utils.mergeObject(options, {
      id: "system-compendiums",
      template: "systems/foundryvtt-reve-de-dragon/templates/settings/system-compendiums.html",
      height: 'fit-content',
      width: 600,
      minimizable: false,
      closeOnSubmit: true,
      title: "Compendiums système"
    });
    return options;
  }

  getData() {
    const systemCompendiums = Object.values(CONFIGURABLE_COMPENDIUMS)
      .map(it => foundry.utils.mergeObject(it, { value: SystemCompendiums.getCompendium(it.compendium) }, { inplace: false }))
    const availableCompendiums = game.packs.map(pack => {
      return {
        name: pack.collection,
        path: pack.collection.replace('.', " / "),
        type: pack.metadata.type
      }
    });
    return foundry.utils.mergeObject(super.getData(), {
      systemCompendiums: systemCompendiums,
      availableCompendiums: availableCompendiums
    }, { inplace: false })
  }

  activateListeners(html) {
    html.find("select.system-compendium-setting").change((event) => {
      const compendium = $(event.currentTarget).data('compendium')
      const value = $(event.currentTarget).val();
      const systemCompendium = CONFIGURABLE_COMPENDIUMS[compendium];

      game.settings.set(SYSTEM_RDD, systemCompendium.setting, value);
    });
  }
}

/**
 * ======= Gestion de jets dans une table correspondant à un compendium =======
 */
export class CompendiumTable {

  constructor(compendium, type, subTypes = undefined, sorting = undefined) {
    this.compendium = compendium;
    this.type = type;
    this.subTypes = subTypes;
    this.sorting = sorting ?? Misc.ascending(it => it.name);
  }

  async getContent(itemFrequence = it => it.system.frequence, filter = it => true) {
    return await SystemCompendiums.getContent(this.compendium,
      this.type,
      it => (!this.subTypes || this.subTypes.includes(it.type)) && itemFrequence(it) > 0 && filter(it),
      itemFrequence,
      this.sorting);
  }

  async buildTable(itemFrequence = it => it.system.frequence, filter = it => true) {
    const elements = await this.getContent(itemFrequence, filter);
    return CompendiumTableHelpers.buildTable(elements, itemFrequence);
  }

  async getRandom(itemFrequence = it => it.system.frequence, filter = it => true, forcedRoll = undefined) {
    const table = await this.buildTable(itemFrequence, filter);
    return await CompendiumTableHelpers.getRandom(table, this.type, this.subTypes, forcedRoll, SystemCompendiums.getCompendium(compendium));
  }

  async toChatMessage(itemFrequence = it => it.system.frequence, filter = it => true, typeName = undefined) {
    const table = await this.buildTable(itemFrequence, filter);
    await CompendiumTableHelpers.tableToChatMessage(table, this.type, this.subTypes, typeName);
    return true;
  }
}

/**
 * ======= Gestion de tables correspondant à un compendium =======
 */
export class CompendiumTableHelpers {

  static buildTable(elements, itemFrequence) {
    let max = 0;
    const total = elements.map(it => itemFrequence(it)).reduce(Misc.sum(), 0);
    return elements.map(it => {
      const frequence = itemFrequence(it);
      let row = { document: it, frequence: frequence, min: max + 1, max: max + frequence, total: total };
      max += frequence;
      return row;
    });
  }

  static concatTables(...tables) {
    const rows = tables.reduce((a, b) => a.concat(b));
    let max = 0;
    const total = rows.map(it => it.frequence).reduce(Misc.sum(), 0);
    return rows.map(row => {
      const frequence = row.frequence
      row.min = max + 1
      row.max = max + frequence
      row.total = total
      max += frequence
      return row
    })
  }
  static async getRandom(table, type, subTypes = ['objet'], forcedRoll = undefined, localisation = undefined) {
    if (table.length == 0) {
      const typeName = Misc.typeName(type, subTypes[0]);
      ui.notifications.warn(`Aucun ${typeName} trouvé dans ${localisation ?? ' les compendiums'}`);
      return undefined;
    }
    return await CompendiumTableHelpers.selectRow(table, forcedRoll);
  }

  /* -------------------------------------------- */
  static async selectRow(table, forcedRoll = undefined) {
    if (table.length == 0) {
      return undefined
    }
    const total = table[0].total;
    const formula = `1d${total}`;
    if (forcedRoll != undefined && (forcedRoll > total || forcedRoll <= 0)) {
      ui.notifications.warn(`Jet forcé ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`);
      forcedRoll = undefined;
    }
    const roll = forcedRoll ? { total: forcedRoll, formula } : await RdDDice.roll(formula, { showDice: HIDE_DICE });
    const row = table.find(it => it.min <= roll.total && roll.total <= it.max);
    row.roll = roll;
    return row;
  }

  /* -------------------------------------------- */
  static async tableRowToChatMessage(row, type, options = { showSource: true }) {
    if (!row) {
      return;
    }
    const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.hbs', {
      roll: row.roll,
      document: row.document,
      typeName: Misc.typeName(type, row.document?.type ?? 'objet'),
      isGM: game.user.isGM,
      options
    });
    const messageData = {
      user: game.user.id,
      rolls: [row.roll],
      sound: CONFIG.sounds.dice,
      content: flavorContent
    };
    await ChatUtility.createChatWithRollMode(messageData)
  }

  /* -------------------------------------------- */
  static async tableToChatMessage(table, type, subTypes, typeName = undefined) {
    const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.hbs', {
      img: RdDItem.getDefaultImg(subTypes[0]),
      typeName: typeName ?? Misc.typeName(type, subTypes[0]),
      table,
      isGM: game.user.isGM,
    });
    const messageData = {
      user: game.user.id,
      whisper: [game.user],
      content: flavorContent
    };
    await ChatUtility.createChatWithRollMode(messageData)
  }

}