/* eslint-disable no-unused-vars */
import parseHTML, { attributesToProps, domToReact } from 'html-react-parser';
import * as FontAwesomeIcons from '@fortawesome/free-solid-svg-icons';
import { toKebabCase, toTitleCase } from '@vizality/util/string';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { Events, Directories } from '@vizality/constants';
import { excludeProperties } from '@vizality/util/object';
import { joinClassNames } from '@vizality/util/dom';
import { getModules } from '@vizality/webpack';
import { readdirSync, readFileSync } from 'fs';
import { error } from '@vizality/util/logger';
import React, { memo, useState } from 'react';
import { sleep } from '@vizality/util/time';
import { Messages } from '@vizality/i18n';
import { join, parse } from 'path';

import { Clickable, Flex, Tooltip as TooltipContainer } from '.';

const iconList = Object
  .keys(FontAwesomeIcons)
  .filter(key => key !== 'fas' && key !== 'prefix')
  .map(icon => FontAwesomeIcons[icon]);

library.add(...iconList);

/**
 * @private
 */
const _labels = [ 'Component', 'Icon' ];
const _error = (...message) => error({ labels: _labels, message });

export const Icons = {};

/*
 * We're going to process our assets folder SVGs now and turn them into React components.
 */
(async () => {
  try {
    const dirs = [ 'svgs', 'logos' ];
    for (const dirName of dirs) {
      const icons = readdirSync(join(Directories.ASSETS, dirName)).map(item => parse(item).name);
      for (const name of icons) {
        const icon = readFileSync(join(Directories.ASSETS, dirName, `${name}.svg`), { encoding: 'utf8' });
        Icons[toKebabCase(name)] = memo(props => parseHTML(icon, {
          replace: domNode => {
            if (domNode?.attribs && domNode?.name === 'svg') {
              const attrs = attributesToProps(domNode.attribs);
              return (
                <svg {...attrs} {...props}>
                  {domToReact(domNode?.children)}
                </svg>
              );
            }
          }
        }));
      }
    }

    /**
     * Add the Font Awesome icons to our collection. These will override any of the existing
     * icons with the same name.
     */
    Object.keys(FontAwesomeIcons)
      .filter(key => key !== 'fas' && key !== 'prefix')
      .forEach(icon => Icons[FontAwesomeIcons[icon].iconName] = memo(props => (
        <FontAwesomeIcon icon={FontAwesomeIcons[icon].iconName} {...props} />
      )));

    /*
     * @note The following is a sort of automated warning system to let us know when Discord
     * has added an icon to their batch, so we can be made aware of and add it. This will
     * initiate after Vizality's settings are ready so that only Vizality developers get
     * alerted about this.
     */
    vizality.once(Events.VIZALITY_SETTINGS_READY, async () => {
      if (vizality.settings.get('developer', false)) {
        /*
         * These are Discord's icons that will crash the appl if attempted to render as a normal icon.
         */
        const blacklist = [
          'ApplicationPlaceholder',
          'DiscordNitro',
          'DiscordWordmark',
          'Nitro',
          'NitroClassic',
          'NitroStackedIcon',
          'NitroClassicHorizontal',
          'NowPlayingMemberMenuItem',
          'PremiumGuildSubscriptionLogoCentered',
          'Arrow',
          'PremiumGuildTier1Simple',
          'PremiumGuildTier2Simple',
          'PremiumGuildTier3Simple'
        ];

        /*
         * These are Discord's inherent icons I have purposely altered or removed for whatever reason.
         */
        const knownAlterations = [
          'ChannelTextNSFW',
          'CopyID',
          'EarlyAccess',
          'EmojiActivityCategory',
          'ExpandIcon',
          'FlowerStarIcon',
          'Grid',
          'GridSmall',
          'HelpButton',
          'InvertedGIFLabel',
          'LeftCaret',
          'MegaphoneNSFW',
          'MultipleChoice',
          'NitroWheel2',
          'NitroStackedLeftAlignedIcon',
          'NSFWAnnouncementThreadIcon',
          'NSFWThreadIcon',
          'PlatformSpotify',
          'PlatformSteam',
          'PlatformTwitch',
          'PlatformXbox',
          'PlatformBlizzard',
          'PlayIcon',
          'RightCaret',
          'StarBadge',
          'Synced',
          'TemplateIcon',
          'TitleBarClose',
          'TitleBarCloseMac',
          'TitleBarMaximize',
          'TitleBarMaximizeMac',
          'TitleBarMinimize',
          'TitleBarMinimizeMac',
          'TrendingArrow',
          'Unsynced',
          'UpdateAvailable',
          'Upload2',
          'SwitchAccountsIcon',
          'WalletIcon',
          'ThickArrowUp',
          'ImagePlaceholderWithPlusIcon',
          'HandPointIcon',
          'PrivacyAndSafetyShield',
          'Home',
          'PremiumChannelIcon',
          'BoostedGuildTier1Simple',
          'BoostedGuildTier2Simple',
          'BoostedGuildTier3Simple',
          'BoostedGuildTier1',
          'HubBadge',
          'GuildBoostingLogoCentered',
          'DiscordLogoLockup',
          'ForumPostLockedIcon',
          'ForumPostIcon',
          'AllChannelsIcon',
          'DoubleStarIcon',
          'ForumPostNSFWIcon'
        ];

        const registry = getModules(m => typeof m === 'function' && m.toString()?.indexOf('"currentColor"') !== -1);
        const Names = Object.keys(Icons).map(name => toTitleCase(name).replaceAll(' ', ''));
        const DiscordIcons = registry?.map(m => m?.displayName);
        const missing = DiscordIcons?.filter(icon => !Names?.includes(icon) && !blacklist?.includes(icon) && !knownAlterations?.includes(icon));
        const SVG = memo(({ icon }) => {
          const Icon = registry.find(m => m?.displayName === icon);
          const [ tooltipColor, setTooltipColor ] = useState('black');
          const [ tooltipText, setTooltipText ] = useState(icon);
          const [ tooltipShow, setTooltipShow ] = useState(false);
          const handleCodeCopy = () => {
            try {
              // Prevent clicking when it's still showing copied
              if (tooltipText === Messages?.COPIED) return;
              setTooltipText(Messages?.COPIED);
              setTooltipColor('green');
              setTooltipShow(true);
              setTimeout(() => {
                setTooltipText(icon);
                setTooltipColor('black');
                setTooltipShow(false);
              }, 1500);
              // Make it easy to copy the markup for the icon with just a click
              const copy = document.querySelector(`.vz-missing-icon-${icon.toLowerCase()}`)?.outerHTML;
              DiscordNative?.clipboard?.copy(copy);
            } catch (err) {
              return _error(err);
            }
          };
          return (
            <div style={{ margin: 5 }}>
              <TooltipContainer
                className='vz-icon-wrapper'
                text={tooltipText}
                color={tooltipColor}
                forceOpen={tooltipShow}
              >
                <Icon
                  className={joinClassNames('vz-icon', `vz-missing-icon-${icon.toLowerCase()}`)}
                  onClick={handleCodeCopy}
                />
              </TooltipContainer>
            </div>
          );
        });
        if (missing?.length) {
          while (!vizality.manager.builtins.get('notifications')) await sleep(100);
          vizality.manager.builtins.get('notifications') && vizality.api.notifications.sendToast({
            header: `Found ${missing.length} Missing Icon ${missing.length === 1 ? 'Asset' : 'Assets '}`,
            icon: 'uwu',
            timeout: false,
            content:
              <Flex wrap={Flex.Wrap.WRAP} style={ { gap: 5 } }>
                {missing.map(icon => <SVG icon={icon} />)}
              </Flex>
          });
        }
      }
    });
  } catch (err) {
    return _error(err);
  }
})();

export default memo(props => {
  let {
    name,
    icon,
    width = '24',
    height = '24',
    size,
    className,
    iconClassName,
    color = 'currentColor',
    tooltip,
    tooltipColor = 'primary',
    tooltipPosition = 'top',
    onClick,
    onContextMenu,
    svgOnly = false
  } = props;

  try {
    if (!name) {
      throw new Error('You must specify a valid name property!');
    }

    const SVG = icon ? icon : Icons[name] ? Icons[name] : null;

    if (!SVG && !icon) {
      throw new Error(`"${name}" is not a valid name property.`);
    }

    if (size) {
      width = size;
      height = size;
    }

    const isClickable = Boolean(onClick || onContextMenu);
    const exposeProps = excludeProperties(props, 'name', 'icon', 'size', 'width', 'height', 'className', 'iconClassName', 'color', 'tooltip', 'tooltipColor', 'tooltipPosition', 'onClick', 'onContextMenu', 'svgOnly');

    const renderIcon = () => {
      // !svgOnly
      if (!svgOnly) {
        // !svgOnly and tooltip
        if (tooltip) {
          // !svgOnly and tooltip and clickable
          if (isClickable) {
            return (
              <TooltipContainer
                text={tooltip}
                color={tooltipColor}
                position={tooltipPosition}
              >
                <Clickable
                  className={joinClassNames(className, 'vz-icon-wrapper')}
                  onClick={onClick}
                  onContextMenu={onContextMenu}
                >
                  <SVG
                    vz-icon={name}
                    className={joinClassNames(iconClassName, 'vz-icon')}
                    fill={color}
                    color={color}
                    width={width}
                    height={height}
                    {...exposeProps}
                  />
                </Clickable>
              </TooltipContainer>
            );
          }
          // !svgOnly and tooltip and !clickable
          return (
            <TooltipContainer
              className={joinClassNames(className, 'vz-icon-wrapper')}
              text={tooltip}
              color={tooltipColor}
              position={tooltipPosition}
            >
              <SVG
                vz-icon={name}
                className={joinClassNames(iconClassName, 'vz-icon')}
                fill={color}
                color={color}
                width={width}
                height={height}
                {...exposeProps}
              />
            </TooltipContainer>
          );
        }
        // !svgOnly and !tooltip and clickable
        if (isClickable) {
          return (
            <Clickable
              className={joinClassNames(className, 'vz-icon-wrapper')}
              onClick={onClick}
              onContextMenu={onContextMenu}
            >
              <SVG
                vz-icon={name}
                className={joinClassNames(iconClassName, 'vz-icon')}
                fill={color}
                color={color}
                width={width}
                height={height}
                {...exposeProps}
              />
            </Clickable>
          );
        }
        // !svgOnly and !tooltip and !clickable
        return (
          <div className={joinClassNames(className, 'vz-icon-wrapper')}>
            <SVG
              vz-icon={name}
              className={joinClassNames(iconClassName, 'vz-icon')}
              fill={color}
              color={color}
              width={width}
              height={height}
              {...exposeProps}
            />
          </div>
        );
      }
      // svgOnly
      return (
        <SVG
          vz-icon={name}
          className={joinClassNames(className, 'vz-icon')}
          fill={color}
          color={color}
          width={width}
          height={height}
          {...exposeProps}
        />
      );
    };
    return renderIcon();
  } catch (err) {
    return _error(err);
  }
});
