/**
 * Applies attributes to chat messages.
 * @module Message
 * @memberof Builtin.Attributes.Chat
 */

import { toHash, isString, toKebabCase } from '@vizality/util/string';
import { patch, unpatch } from '@vizality/patcher';
import { findInTree } from '@vizality/util/react';
import { getModule } from '@vizality/webpack';
import { Regexes } from '@vizality/constants';

/**
 * Discord message types for "special" messages, such as when a message is pinned.
 * @see {@link https://discord.com/developers/docs/resources/channel#message-object-message-types|Discord}
 */
const MESSAGE_TYPES = {};
for (const type of Object.entries($discord.constants.MessageTypes)) {
  if (type[0] !== '0' && isString(type[1])) {
    [ , MESSAGE_TYPES[Number(type[0])] ] = type;
  }
}

const attributes = {
  /**
   * Message author related attributes.
   */
  'vz-bot-vizality': message => Boolean(message.author?.phone === toHash('VIZALITY') && message.author?.avatar !== 'clyde') && '',
  'vz-bot-plugin': message => Boolean(message.author?.phone === toHash('PLUGIN') && message.author?.avatar !== 'clyde') && '',
  'vz-user-id': message => new RegExp(Regexes.USER_ID).test(message.author?.id) && message.author?.id,
  'vz-local': message => Boolean(message.author?.isLocalBot?.()) && '',
  'vz-mentioned': message => Boolean(message.mentioned) && '',
  'vz-self': message => Boolean(message.author?.email) && '',
  'vz-webhook': message => Boolean(message.webhookId) && '',
  'vz-bot': message => Boolean(message.author?.bot) && '',
  'vz-member': (message, channel, _, memberModule) => Boolean(
    channel?.guild_id &&
    memberModule.getMember(channel?.guild_id, message.author.id) &&
    message.type === 0
  ) && '',
  'vz-owner': (message, channel, guildModule) => Boolean(
    channel?.guild_id &&
    message.author?.id === guildModule.getGuild(channel?.guild_id) &&
    message.type === 0
  ) && '',
  /**
   * Message related attributes.
   */
  'vz-attachment': message => Boolean(message.attachments?.length) && '',
  'vz-embed': message => Boolean(message.embeds?.length) && '',
  'vz-blocked': message => Boolean(message.blocked) && '',
  'vz-message-id': message => message.id,
  /**
   * Set an attribute for special types of messages.
   */
  'vz-message-type': message => message.type !== 0 && toKebabCase(MESSAGE_TYPES[message.type?.toString()])
};

export const labels = [ 'Chat', 'Message' ];

export default builtin => {
  const Message = getModule(m => m.default?.toString()?.search('childrenRepliedMessage') > -1);
  const memberModule = getModule('getMember');
  const guildModule = getModule('getGuild');
  patch('vz-attributes-messages', Message, 'default', (_, res) => {
    try {
      const props = findInTree(res, n => n?.message && n.channel);
      /**
       * Blocked messages have no props.
       */
      if (!props?.message) return;

      const { message, channel } = props;
      if (!res.props?.children?.props) return;
      for (const attribute in attributes) {
        try {
          res.props.children.props[attribute] = attributes[attribute](message, channel, guildModule, memberModule);
        } catch (error) {
          return builtin.error(builtin._labels.concat(labels), `Failed to add attribute "${attribute}"!\n`, error);
        }
      }
    } catch (err) {
      return builtin.error(builtin._labels.concat(labels), err);
    }
  });
  return () => unpatch('vz-attributes-messages');
};
