import { open as openModal, close as closeModal } from '@vizality/modal';
import { getModuleByDisplayName, getModule } from '@vizality/webpack';
import { SettingsContextMenu } from '@vizality/components/vizality';
import { Content, Layout } from '@vizality/components/dashboard';
import { toPlural, toTitleCase } from '@vizality/util/string';
import { Confirm, ContextMenu } from '@vizality/components';
import { joinClassNames } from '@vizality/util/dom';
import { patch, unpatch } from '@vizality/patcher';
import { Builtin } from '@vizality/entities';
import { Messages } from '@vizality/i18n';
import React from 'react';

import SettingsPage from './components/Settings';

/**
 * The settings page categories.
 */
const categories = [
  'general',
  'account',
  'appearance',
  'notifications',
  'keybinds',
  'commands',
  'developer',
  'advanced'
];

const content = {
  header: 'Settings',
  icon: 'Gear'
};

export default class Settings extends Builtin {
  start () {
    this.pie = [];
    this.injectStyles('styles/main.scss');
    /**
     * Register a route for each of the settings categories.
     */
    categories.forEach(category =>
      vizality.api.routes.registerRoute({
        id: `settings/${category}`,
        path: `/settings/${category}`,
        render: props => (
          <Layout>
            <Content
              header={content.header}
              Separator={false}
              icon={content.icon}
              vz-builtin={this.addonId}
              vz-section={category}
            >
              <SettingsPage
                {...props}
                builtin={this}
                section={category}
              />
            </Content>
          </Layout>
        )
      })
    );

    vizality.api.settings._registerBuiltinSection({
      header: 'Settings',
      icon: content.icon,
      render: props => <SettingsPage {...props} builtin={this} section='general' />
    });

    vizality.api.actions.registerAction('CONFIRM_RESTART', () => this.confirmRestart());

    this.patchSettingsComponent();
    this.patchExperiments();
    this.patchSettingsContextMenu();
    this.patchSettingsContextMenuAddonItem();
    this.patchSettingsContextMenuAddonCheckboxItem();
  }

  stop () {
    categories.forEach(category => vizality.api.routes.unregisterRoute(`settings/${category}`));
    vizality.api.routes.unregisterRoute('settings');
    vizality.api.actions.unregisterAction('CONFIRM_RESTART');
    unpatch('vz-settings-items');
    unpatch('vz-settings-context-menu');
    unpatch('vz-settings-context-menu-addon-items');
    unpatch('vz-settings-context-menu-addon-checkbox-items');
  }

  confirmRestart () {
    const { colorStandard } = getModule('colorStandard');
    const { spacing } = getModule('spacing', 'message');
    const { size16 } = getModule('size16');

    openModal(() => <Confirm
      red
      header={Messages.ERRORS_RESTART_APP}
      confirmText={Messages.BUNDLE_READY_RESTART}
      cancelText={Messages.BUNDLE_READY_LATER}
      onConfirm={() => DiscordNative.app.relaunch()}
      onCancel={closeModal}
    >
      <div className={joinClassNames(colorStandard, spacing, size16)}>
        {Messages.VIZALITY_SETTINGS_RESTART}
      </div>
    </Confirm>);
  }

  patchExperiments () {
    try {
      const experimentsModule = getModule(r => r.isDeveloper !== void 0);
      Object.defineProperty(experimentsModule, 'isDeveloper', {
        get: () => vizality.settings.get('experiments', false)
      });

      // Ensure components do get the update
      experimentsModule._changeCallbacks.forEach(callback => callback());
    } catch (err) {
      // :eyes:
    }
  }

  patchSettingsComponent () {
    const SettingsView = getModuleByDisplayName('SettingsView');
    patch('vz-settings-items', SettingsView.prototype, 'getPredicateSections', (_, sections) => {
      const latestCommitHash = vizality.git.revision.substring(0, 7);
      const debugInfo = sections[sections.findIndex(c => c.section === 'CUSTOM') + 1];
      if (debugInfo) {
        debugInfo.element = ((_element) => function () {
          const res = _element();
          if (res.props.children && res.props.children.length === 3) {
            res.props.children.push(
              Object.assign({}, res.props.children[0], {
                props: Object.assign({}, res.props.children[0].props, {
                  children: [ 'Vizality', ' ',
                    <span className={res.props.children[0].props.children[4].props.className}>
                      {`${vizality.git.branch} (${latestCommitHash})`}
                    </span> ]
                })
              })
            );
          }
          return res;
        })(debugInfo.element);
      }
      return sections;
    });
  }

  patchSettingsContextMenu () {
    const DiscordSettingsContextMenu = getModule(m => m.default?.displayName === 'UserSettingsCogContextMenu');
    patch('vz-settings-context-menu', DiscordSettingsContextMenu, 'default', (_, res) => {
      const items = res.props.children.find(child => Array.isArray(child));
      items.push(
        <>
          <ContextMenu.Separator />
          <ContextMenu.Item
            id='vizality-dashboard'
            label='Vizality'
            action={() => vizality.api.routes.navigateTo('dashboard')}
          >
            {SettingsContextMenu.type().props.children}
          </ContextMenu.Item>
        </>
      );
    });
  }

  patchSettingsContextMenuAddonCheckboxItem () {
    const MenuCheckboxItem = getModule(m => m.default?.displayName === 'MenuCheckboxItem');
    patch('vz-settings-context-menu-addon-checkbox-items', MenuCheckboxItem, 'default', ([ props ], res) => {
      if (!res.props?.id) {
        return;
      }

      /**
       * If the ID doesn't start with one of the items we're targetting, don't patch it.
       */
      if ((res.props.id.indexOf('vizality-dashboard--plugins--') > 0 &&
           res.props.id.indexOf('vizality-dashboard--themes--') > 0) ||
           res.props['vz-addon-icon']
      ) return;

      /**
       *
       */
      const type =
        new RegExp(/(vizality-dashboard-?-plugins)/).test(res.props.id)
          ? 'plugin'
          : new RegExp(/(vizality-dashboard-?-themes)/).test(res.props.id)
            ? 'theme'
            : null;

      /**
       *
       */
      if (type !== 'plugin' && type !== 'theme') {
        return;
      }

      /**
       *
       */
      const addonId = res.props.id.replace(new RegExp(`(user-settings-cog-)?(vizality-dashboard-?-${toPlural(type)}--)`), '');

      /**
       *
       */
      if (!vizality.manager[toPlural(type)].isInstalled(addonId)) {
        return;
      }

      const addonIconUrl = props['vz-addon-icon'];
      res.props['vz-addon-icon'] = '';
      res.props['vz-addon-id'] = props.id;
      if (addonIconUrl) {
        res.props.style = { ...res.props.style, '--vz-addon-icon': `url('${addonIconUrl}')` };
      }
    });
  }

  patchSettingsContextMenuAddonItem () {
    const MenuItem = getModule(m => m.default?.displayName === 'MenuItem');
    patch('vz-settings-context-menu-addon-items', MenuItem, 'default', ([ props ], res) => {
      if (!res.props?.id) {
        return;
      }

      /**
       * If the ID doesn't start with one of the items we're targetting, don't patch it.
       */
      if ((res.props.id.indexOf('vizality-dashboard--plugins--') > 0 &&
           res.props.id.indexOf('vizality-dashboard--themes--') > 0) ||
           res.props['vz-addon-icon']
      ) return;

      /**
       *
       */
      const type =
        new RegExp(/(vizality-dashboard-?-plugins)/).test(res.props.id)
          ? 'plugin'
          : new RegExp(/(vizality-dashboard-?-themes)/).test(res.props.id)
            ? 'theme'
            : null;

      /**
       *
       */
      if (type !== 'plugin' && type !== 'theme') {
        return;
      }

      /**
       *
       */
      const addonId = res.props.id.replace(new RegExp(`(user-settings-cog-)?(vizality-dashboard-?-${toPlural(type)}--)`), '');

      /**
       *
       */
      if (!vizality.manager[toPlural(type)].isInstalled(addonId)) {
        return;
      }

      const addonIconUrl = props['vz-addon-icon'];
      res.props['vz-addon-icon'] = '';
      res.props['vz-addon-id'] = props.id;
      if (addonIconUrl) {
        res.props.style = { ...res.props.style, '--vz-addon-icon': `url('${addonIconUrl}')` };
      }
    });
  }
}
