import { ChatUtility } from "../chat-utility.js";
import { HIDE_DICE, renderTemplate, 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";
import { ReglesOptionnelles } from "./regles-optionnelles.js";

const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;

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" },
  'races': { label: "Races", 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" },
}

export class SystemCompendiums extends HandlebarsApplicationMixin(ApplicationV2) {
  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')
          .then(list => list.sort((a, b) => a.name.localeCompare(b.name)))
      case 'entite':
      case 'creature':
        return await SystemCompendiums.getWorldOrCompendiumItems('competencecreature', 'competences-creatures')
          .then(list => list.sort((a, b) => a.name.localeCompare(b.name)))
    }
    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}`;
  }

  static DEFAULT_OPTIONS = {
    tag: "form",
    id: "system-compendiums",
    classes: ["system-compendiums"],
    form: { submitOnChange: false, closeOnSubmit: true },
    position: { width: 600, height: "auto" },
    window: { title: "Compendiums système", icon: "fa-solid fa-bars" }
  }

  static PARTS = {
    form: { template: "systems/foundryvtt-reve-de-dragon/templates/settings/system-compendiums.hbs" }
  }

  async _prepareContext() {
    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 { systemCompendiums, availableCompendiums }
  }

  async _onRender(context, options) {
    this.element.querySelectorAll("select.system-compendium-setting").forEach(el => {
      el.addEventListener("change", async event => {
        const compendium = event.currentTarget.dataset.compendium
        const value = event.currentTarget.value;
        const systemCompendium = CONFIGURABLE_COMPENDIUMS[compendium];
        await game.settings.set(SYSTEM_RDD, systemCompendium.setting, value);
      });
    });
  }
}

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;
  }
}

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 = {}) {
    if (options.showSource == undefined){
      options.showSource = ReglesOptionnelles.isUsing('afficher-table-source')
    }
    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: { showSource: options.showSource }
    })
    await ChatMessage.create(ChatUtility.adaptVisibility(
      {
        user: game.user.id,
        rolls: [row.roll],
        sound: CONFIG.sounds.dice,
        content: flavorContent
      },
      { rollMode: options.rollMode }))
  }

  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,
    })
    await ChatMessage.create(ChatUtility.adaptVisibility(
      {
        user: game.user.id,
        whisper: [game.user],
        content: flavorContent
      }
    ))
  }
}
