import { getOwnerInstance, forceUpdateElement, findInReactTree } from '@vizality/util/react';
import { getModuleByDisplayName, getModule } from '@vizality/webpack';
import { waitForElement } from '@vizality/util/dom';
import { patch, unpatch } from '@vizality/patcher';
import { Directories } from '@vizality/constants';
import { Builtin } from '@vizality/entities';
import { promises, existsSync } from 'fs';
import { join } from 'path';
import React from 'react';

const { unlink } = promises;

import NoticeContainer from './components/NoticeContainer';
import ToastContainer from './components/ToastContainer';

export default class Notifications extends Builtin {
  async start () {
    document.querySelector('.Toastify')?.remove();
    this.injectStyles('styles/main.scss');
    const injectedFile = join(Directories.SRC, '__injected.txt');
    await this._patchNotices();
    await this._patchToasts();
    if (existsSync(injectedFile)) {
      this._welcomeNewUser();
      unlink(injectedFile);
    }
  }

  stop () {
    unpatch('vz-notices-notices');
    unpatch('vz-notices-toast');
    document.querySelector('.Toastify')?.remove();
  }

  /**
   * 
   * @todo Figure out how to forceUpdate the app initially to render startup notices.
   */
  async _patchNotices () {
    const { base } = getModule('base', 'container');
    const instance = getOwnerInstance(await waitForElement(`.${base.split(' ')[0]}`));
    patch('vz-notices-notices', instance?.props?.children, 'type', (_, res) => {
      try {
        const { children } = findInReactTree(res, ({ className }) => className === base);
        children.unshift(<NoticeContainer />);
      } catch (err) {
        return this.error(this._labels.concat('_patchNotices'), err);
      }
    });
  }

  /**
   * 
   */
  async _patchToasts () {
    const { app } = getModule('app', 'layers');
    const Shakeable = getModuleByDisplayName('Shakeable');
    patch('vz-notices-toast', Shakeable?.prototype, 'render', (_, res) => {
      try {
        if (!res.props?.children?.find(child => child.type?.name === 'ToastContainer')) {
          res.props?.children?.push(
            <ToastContainer settings={this.settings} />
          );
        }
      } catch (err) {
        return this.error(this._labels.concat('_patchToasts'), err);
      }
    });
    forceUpdateElement(`.${app}`);
  }

  /**
   * 
   */
  _welcomeNewUser () {
    try {
      return vizality.api.routes.navigateTo('dashboard');
    } catch (err) {
      return this.error(this._labels.concat('_welcomeNewUser'), err);
    }
  }
}
