import { Misc } from "./misc.js";
import { renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";


/**
 * Class providing helper methods around the Chat message
 */
export class ChatUtility {

  static async init() {
    Hooks.on("renderChatMessageHTML", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg))
    Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id))
  }

  /* -------------------------------------------- */
  static onSocketMessage(sockmsg) {
    switch (sockmsg.msg) {
      case "msg_gm_chat_message": return ChatUtility.handleGMChatMessage(sockmsg.data)
      case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data)
      case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data)
    }
  }

  /* -------------------------------------------- */
  static notifyUser(userId, level = 'info', message) {
    const socketData = {
      userId: userId, level: level, message: message
    };
    if (game.user.id == userId) {
      ChatUtility.onNotifyUser(socketData);
    }
    else {
      game.socket.emit(SYSTEM_SOCKET_ID, {
        msg: "msg_user_ui_notifications", data: socketData
      });
    }
  }

  static onNotifyUser(socketData) {
    if (game.user.id == socketData.userId) {
      switch (socketData.level) {
        case 'warn': ui.notifications.warn(socketData.message); break;
        case 'error': ui.notifications.error(socketData.message); break;
        default: ui.notifications.info(socketData.message); break;
      }
    }
  }

  /* -------------------------------------------- */
  static onRemoveMessages(socketData) {
    if (Misc.isFirstConnectedGM()) {
      if (socketData.part) {
        const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
        toDelete.forEach(it => it.delete());
      }
      if (socketData.messageId) {
        game.messages.get(socketData.messageId)?.delete();
      }
    }
  }

  /* -------------------------------------------- */
  static removeMessages(socketData) {
    if (Misc.isFirstConnectedGM()) {
      ChatUtility.onRemoveMessages(socketData);
    }
    else {
      game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_delete_chat_message", data: socketData });
    }
  }

  /* -------------------------------------------- */
  static removeChatMessageContaining(part) {
    ChatUtility.removeMessages({ part: part });
  }

  static remover(chatMessage) {
    const messageId = chatMessage.id;
    if (messageId) {
      return (..._) => ChatUtility.removeMessages({ messageId: messageId });

    }
    return (..._) => { }
  }

  static removeChatMessageId(messageId) {
    if (messageId) {
      ChatUtility.removeMessages({ messageId: messageId });
    }
  }

  /* -------------------------------------------- */
  static adaptVisibility( messageData, options = { actor: undefined, rollMode: undefined }) {
    foundry.utils.mergeObject(options, { rollMode: game.settings.get("core", "messageMode") }, { overwrite: false });
    switch (options.rollMode) {
      case "blind": // GM only
        if (!game.user.isGM) {
          ChatUtility.blindMessageToGM(messageData)
          messageData.whisper = [game.user]
          messageData.content = "Message envoyé en aveugle au Gardien";
        }
        else {
          messageData.whisper = ChatUtility.getGMs()
        }
        break;
      case "gm":
        messageData.whisper = options.actor ? ChatUtility.getOwners(options.actor) : ChatUtility.getUserAndGMs()
        break;
      case "self":
        messageData.whisper = [game.user]
        break
    }
    messageData.alias = messageData.alias ?? options.actor?.name ?? game.user.name
    return messageData
  }

  static async tellToUser(message) {
    await ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
  }

  static async tellToGM(message) {
    await ChatMessage.create({
      user: game.user.id,
      content: message,
      whisper: ChatUtility.getGMs()
    })
  }

  static async tellToUserAndGM(message) {
    await ChatMessage.create({
      user: game.user.id,
      content: message,
      whisper: ChatUtility.getUserAndGMs()
    })
  }

  static getOwners(document) {
    return document ? game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) : [game.user]
  }

  static getUserAndGMs() {
    return [game.user, ...ChatUtility.getGMs()]
  }

  /* -------------------------------------------- */
  static getMultipleActorsOwners(...actors) {
    return Misc.concat(actors.map(it => it == undefined ? [] : ChatUtility.getOwners(it)))
  }

  /* -------------------------------------------- */
  static getUsers(filter) {
    return game.users.filter(filter)
  }

  static getGMs() {
    return game.users.filter(user => user.isGM)
  }

  static applyRollMode(chatMessageData = {}, rollMode = game.settings.get("core", "messageMode")) {
    switch (rollMode) {
      case "blind":
        chatMessageData.blind = true
        chatMessageData.whisper = ChatUtility.getGMs()
        break
      case "gm":
        chatMessageData.whisper = ChatUtility.getGMs()
        chatMessageData.blind = false
        break
      case "public":
        chatMessageData.whisper = ChatUtility.getUsers(user => user.active)
        chatMessageData.blind = false
        break
      case "self":
        chatMessageData.whisper = [game.user]
        chatMessageData.blind = false
        break
    }
    return chatMessageData
  }

  /* -------------------------------------------- */
  static blindMessageToGM(chatOptions) {
    const chatGM = foundry.utils.deepClone(chatOptions)
    chatGM.content = "Message aveugle de " + game.user.name + "<br>" + chatOptions.content
    console.log("blindMessageToGM", chatGM)
    game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_gm_chat_message", data: chatGM })
  }

  /* -------------------------------------------- */
  static handleGMChatMessage(socketData) {
    console.log("blindMessageToGM", socketData);
    if (Misc.isFirstConnectedGM()) {
      ChatMessage.create({
        user: game.user.id,
        whisper: ChatUtility.getGMs(),
        content: socketData.content
      })
    }
  }

  static async setMessageData(chatMessage, key, flag) {
    if (flag && chatMessage.isAuthor) {
      await chatMessage.setFlag(SYSTEM_RDD, key, flag)
    }
  }

  static getMessageData(chatMessage, key) {
    return chatMessage.getFlag(SYSTEM_RDD, key);
  }

  static getChatMessage(event) {
    const chatMessageEl = $(event.currentTarget).closest('.chat-message');
    const chatMessageId = chatMessageEl.attr('data-document-id') ?? chatMessageEl.attr('data-message-id');
    return game.messages.get(chatMessageId);
  }

  static async onRenderChatMessage(chatMessage, html, data) {
    const rddTimestamp = chatMessage.getFlag(SYSTEM_RDD, 'rdd-timestamp')
    const heureRdD = $(html).find('header.message-header .heure-rdd')
    if (rddTimestamp && heureRdD.length == 0) {
      const messageTimestamp = $(html).find('header.message-header .message-timestamp');
      const timestamp = new RdDTimestamp(rddTimestamp);
      const timestampData = timestamp.toCalendrier();
      const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
      messageTimestamp.after(dateHeure)
    }
  }

  static async onCreateChatMessage(chatMessage, options, id) {
    if (chatMessage.isAuthor) {
      await ChatUtility.setTimestamp(chatMessage)
      await chatMessage.update({ content: await RdDTextEditor.enrichHTML(chatMessage.content, undefined, { showLink: false }) })
    }
  }

  static async setTimestamp(chatMessage) {
    await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
  }

  static getISODate(chatMessage) {
    const date = new Date(chatMessage.timestamp);
    return date?.toISOString().substring(0, 10)
  }
}
