/*
 * notion-enhancer
 * (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 * under the MIT license
 */

'use strict';

const url = require('url'),
  path = require('path'),
  { __notion } = require('../../pkg/helpers.js'),
  config = require(`${__notion}/app/config.js`),
  constants = require(`${__notion}/app/shared/constants.js`),
  notion_intl = require(`${__notion}/app/shared/notion-intl/index.js`),
  notionIpc = require(`${__notion}/app/helpers/notionIpc.js`),
  localizationHelper = require(`${__notion}/app/helpers/localizationHelper.js`),
  koMessages = require(`${__notion}/app/i18n/ko_KR/messages.json`),
  schemeHelpers = require(`${__notion}/app/shared/schemeHelpers.js`),
  React = require(`${__notion}/app/node_modules/react/index.js`),
  ReactDOM = require(`${__notion}/app/node_modules/react-dom/index.js`);

const insertCSP = `
  const csp = document.createElement('meta');
  csp.httpEquiv = 'Content-Security-Policy';
  csp.content = "script-src 'self' 'unsafe-inline' 'unsafe-eval' enhancement: https://gist.github.com https://apis.google.com https://api.amplitude.com https://widget.intercom.io https://js.intercomcdn.com https://logs-01.loggly.com https://cdn.segment.com https://analytics.pgncs.notion.so https://checkout.stripe.com https://embed.typeform.com https://admin.typeform.com https://platform.twitter.com https://cdn.syndication.twimg.com; connect-src 'self' https://msgstore.www.notion.so wss://msgstore.www.notion.so https://notion-emojis.s3-us-west-2.amazonaws.com https://s3-us-west-2.amazonaws.com https://s3.us-west-2.amazonaws.com https://notion-production-snapshots-2.s3.us-west-2.amazonaws.com https: http: https://api.amplitude.com https://api.embed.ly https://js.intercomcdn.com https://api-iam.intercom.io wss://nexus-websocket-a.intercom.io https://logs-01.loggly.com https://api.segment.io https://api.pgncs.notion.so https://checkout.stripe.com https://cdn.contentful.com https://preview.contentful.com https://images.ctfassets.net https://api.unsplash.com https://boards-api.greenhouse.io; font-src 'self' data: https://cdnjs.cloudflare.com https://js.intercomcdn.com; img-src 'self' data: blob: https: https://platform.twitter.com https://syndication.twitter.com https://pbs.twimg.com https://ton.twimg.com; style-src 'self' 'unsafe-inline' enhancement: https://cdnjs.cloudflare.com https://github.githubassets.com https://platform.twitter.com https://ton.twimg.com; frame-src https: http:; media-src https: http:";
  document.head.appendChild(csp);
`;

module.exports = (store, __exports) => {
  if (!store().tabs) {
    class Index extends React.PureComponent {
      constructor() {
        super(...arguments);
        this.state = {
          error: false,
          searching: false,
          searchingPeekView: false,
          zoomFactor: 1,
          tabs: [],
        };
        this.$currentTab;
        this.tabCache = {
          react: {},
          active: [],
          loading: [],
        };
        this.notionElm = [];
        this.loadedElms = [];
        this.reactTabs = {};
        this.handleNotionRef = ($notion) => {
          this.tabCache.loading.push($notion);
        };
        this.$search = null;
        this.handleSearchRef = (searchElm) => {
          this.$search = searchElm;
        };
        this.handleReload = () => {
          this.setState({ error: false });
          setTimeout(() => {
            this.tabCache.loading.forEach(($notion) => {
              if ($notion.isWaitingForResponse()) $notion.reload();
            });
          }, 50);
        };
        this.startSearch = this.startSearch.bind(this);
        this.stopSearch = this.stopSearch.bind(this);
        this.nextSearch = this.nextSearch.bind(this);
        this.prevSearch = this.prevSearch.bind(this);
        this.clearSearch = this.clearSearch.bind(this);
        this.doneSearch = this.doneSearch.bind(this);
        window['tab'] = (id) => {
          if (!id && id !== 0) return;
          this.setState({ tabs: [...new Set([...this.state.tabs, id])] });
          setTimeout(() => {
            this.loadListeners();
            this.blurListeners();
            if (document.querySelector(`#tab-${id}`)) {
              this.tabCache.active.forEach(($notion) => {
                $notion.style.display = 'none';
              });
              this.$currentTab = document.querySelector(`#tab-${id}`);
              this.$currentTab.style.display = 'flex';
              this.focusListeners();
            }
          }, 1000);
        };
      }
      componentDidMount() {
        this.loadListeners();

        try {
          electron.remote.getCurrentWindow().on('focus', (e) => {
            this.$currentTab.focus();
          });
        } catch {}
      }
      startSearch(isPeekView) {
        this.setState({
          searching: true,
          searchingPeekView: isPeekView,
        });
        if (document.activeElement instanceof HTMLElement)
          document.activeElement.blur();
        this.$search.focus();
        notionIpc.sendIndexToSearch(this.$search, 'search:start');
        notionIpc.sendIndexToNotion(this.$search, 'search:started');
      }
      stopSearch() {
        notionIpc.sendIndexToSearch(this.$search, 'search:reset');
        this.setState({
          searching: false,
        });
        this.lastSearchQuery = undefined;
        this.$currentTab.getWebContents().stopFindInPage('clearSelection');
        notionIpc.sendIndexToNotion(this.$currentTab, 'search:stopped');
      }
      nextSearch(query) {
        this.$currentTab.getWebContents().findInPage(query, {
          forward: true,
          findNext: query === this.lastSearchQuery,
        });
        this.lastSearchQuery = query;
      }
      prevSearch(query) {
        this.$currentTab.getWebContents().findInPage(query, {
          forward: false,
          findNext: query === this.lastSearchQuery,
        });
        this.lastSearchQuery = query;
      }
      clearSearch() {
        this.lastSearchQuery = undefined;
        this.$currentTab.getWebContents().stopFindInPage('clearSelection');
      }
      doneSearch() {
        this.lastSearchQuery = undefined;
        this.$currentTab.getWebContents().stopFindInPage('clearSelection');
        this.setState({ searching: false });
        if (document.activeElement instanceof HTMLElement) {
          document.activeElement.blur();
        }
        this.$currentTab.focus();
        notionIpc.sendIndexToNotion(this.$currentTab, 'search:stopped');
      }
      blurListeners() {
        if (!this.$currentTab) return;
        if (this.state.searching) this.stopSearch();
        notionIpc.receiveIndexFromNotion.removeListener(
          this.$currentTab,
          'search:start',
          this.startSearch
        );
        notionIpc.receiveIndexFromNotion.removeListener(
          this.$currentTab,
          'search:stop',
          this.stopSearch
        );
        notionIpc.receiveIndexFromSearch.removeListener(
          this.$search,
          'search:next',
          this.nextSearch
        );
        notionIpc.receiveIndexFromSearch.removeListener(
          this.$search,
          'search:prev',
          this.prevSearch
        );
        notionIpc.receiveIndexFromSearch.removeListener(
          this.$search,
          'search:clear',
          this.clearSearch
        );
        notionIpc.receiveIndexFromSearch.removeListener(
          this.$search,
          'search:stop',
          this.doneSearch
        );
      }
      focusListeners() {
        notionIpc.receiveIndexFromNotion.addListener(
          this.$currentTab,
          'search:start',
          this.startSearch
        );
        notionIpc.receiveIndexFromNotion.addListener(
          this.$currentTab,
          'search:stop',
          this.stopSearch
        );
        notionIpc.receiveIndexFromSearch.addListener(
          this.$search,
          'search:next',
          this.nextSearch
        );
        notionIpc.receiveIndexFromSearch.addListener(
          this.$search,
          'search:prev',
          this.prevSearch
        );
        notionIpc.receiveIndexFromSearch.addListener(
          this.$search,
          'search:clear',
          this.clearSearch
        );
        notionIpc.receiveIndexFromSearch.addListener(
          this.$search,
          'search:stop',
          this.doneSearch
        );
      }
      loadListeners() {
        if (!this.$search) return;
        this.tabCache.loading
          .filter(($notion) => !this.tabCache.active.includes($notion))
          .forEach(($notion) => {
            this.tabCache.active.push($notion);
            $notion.addEventListener('did-fail-load', (error) => {
              // logger.info('Failed to load:', error);
              if (
                error.errorCode === -3 ||
                !error.validatedURL.startsWith(
                  schemeHelpers.getSchemeUrl({
                    httpUrl: config.default.baseURL,
                    protocol: config.default.protocol,
                  })
                )
              ) {
                return;
              }
              this.setState({ error: true });
            });
            $notion.addEventListener('dom-ready', () => {
              $notion.getWebContents().executeJavaScript(insertCSP);
              $notion
                .getWebContents()
                .addListener('found-in-page', (event, result) => {
                  const matches = result
                    ? {
                        count: result.matches,
                        index: result.activeMatchOrdinal,
                      }
                    : { count: 0, index: 0 };
                  notionIpc.sendIndexToSearch(
                    this.$search,
                    'search:result',
                    matches
                  );
                });
              notionIpc.proxyAllMainToNotion($notion);
            });
            notionIpc.receiveIndexFromNotion.addListener(
              $notion,
              'search:set-theme',
              (theme) => {
                notionIpc.sendIndexToSearch(
                  this.$search,
                  'search:set-theme',
                  theme
                );
              }
            );
            notionIpc.receiveIndexFromNotion.addListener(
              $notion,
              'zoom',
              (zoomFactor) => {
                $notion.getWebContents().setZoomFactor(zoomFactor);
                this.$search.getWebContents().setZoomFactor(zoomFactor);
                this.setState({ zoomFactor });
              }
            );
            let electronWindow;
            try {
              electronWindow = electron.remote.getCurrentWindow();
            } catch (error) {
              notionIpc.sendToMain('notion:log-error', {
                level: 'error',
                from: 'index',
                type: 'GetCurrentWindowError',
                error: error.message,
              });
            }
            if (!electronWindow) {
              this.setState({ error: true });
              this.handleReload();
              return;
            }
            electronWindow.addListener('app-command', (e, cmd) => {
              const webContents = $notion.getWebContents();
              if (cmd === 'browser-backward' && webContents.canGoBack()) {
                webContents.goBack();
              } else if (
                cmd === 'browser-forward' &&
                webContents.canGoForward()
              ) {
                webContents.goForward();
              }
            });
            electronWindow.addListener('swipe', (e, dir) => {
              const webContents = $notion.getWebContents();
              if (dir === 'left' && webContents.canGoBack()) {
                webContents.goBack();
              } else if (dir === 'right' && webContents.canGoForward()) {
                webContents.goForward();
              }
            });
            const sendFullScreenChangeEvent = () => {
              notionIpc.sendIndexToNotion(
                $notion,
                'notion:full-screen-changed'
              );
            };
            electronWindow.addListener(
              'enter-full-screen',
              sendFullScreenChangeEvent
            );
            electronWindow.addListener(
              'leave-full-screen',
              sendFullScreenChangeEvent
            );
            electronWindow.addListener(
              'enter-html-full-screen',
              sendFullScreenChangeEvent
            );
            electronWindow.addListener(
              'leave-html-full-screen',
              sendFullScreenChangeEvent
            );
          });
        this.tabCache.loading = [];
      }
      renderSearchContainer() {
        return React.createElement(
          'div',
          { style: this.getSearchContainerStyle() },
          React.createElement('webview', {
            id: 'search',
            style: this.getSearchWebviewStyle(),
            ref: this.handleSearchRef,
            partition: constants.electronSessionPartition,
            preload: `file://${path.resolve(
              `${__notion}/app/renderer/search.js`
            )}`,
            src: `file://${path.resolve(
              `${__notion}/app/renderer/search.html`
            )}`,
          })
        );
      }
      renderNotionContainer() {
        this.reactTabs = Object.fromEntries(
          this.state.tabs.map((id) => {
            return [
              id,
              this.reactTabs[id] ||
                React.createElement('webview', {
                  className: 'notion',
                  id: `tab-${id}`,
                  style: Index.notionWebviewStyle,
                  ref: this.handleNotionRef,
                  partition: constants.electronSessionPartition,
                  preload: path.resolve(`${__notion}/app/renderer/preload.js`),
                  src: this.props.notionUrl,
                }),
            ];
          })
        );
        return React.createElement(
          'div',
          { style: this.getNotionContainerStyle() },
          ...Object.values(this.reactTabs)
        );
      }
      renderErrorContainer() {
        return React.createElement(
          'div',
          { style: this.getErrorContainerStyle() },
          React.createElement('img', {
            style: Index.frontImageStyle,
            src: './onboarding-offline.png',
          }),
          React.createElement(
            'div',
            { style: Index.frontMessageStyle },
            React.createElement(
              'div',
              null,
              React.createElement(notion_intl.FormattedMessage, {
                id: 'desktopLogin.offline.title',
                defaultMessage: 'Welcome to <strong>Notion</strong>!',
                values: {
                  strong: (...chunks) =>
                    React.createElement('strong', null, chunks),
                },
              })
            ),
            React.createElement(
              'div',
              null,
              React.createElement(notion_intl.FormattedMessage, {
                id: 'desktopLogin.offline.message',
                defaultMessage: 'Connect to the internet to get started.',
              })
            )
          ),
          React.createElement(
            'div',
            null,
            React.createElement(
              'button',
              { style: Index.reloadButtonStyle, onClick: this.handleReload },
              React.createElement(notion_intl.FormattedMessage, {
                id:
                  'desktopLogin.offline.retryConnectingToInternetButton.label',
                defaultMessage: 'Try again',
              })
            )
          )
        );
      }
      renderDragRegion() {
        return React.createElement('div', { style: Index.dragRegionStyle });
      }
      render() {
        const notionLocale = localizationHelper.getNotionLocaleFromElectronLocale(
            window.navigator.language
          ),
          result = React.createElement(
            notion_intl.IntlProvider,
            {
              locale: notionLocale,
              messages: notionLocale === 'ko-KR' ? koMessages : {},
            },
            this.renderDragRegion(),
            this.renderNotionContainer(),
            this.renderSearchContainer(),
            this.renderErrorContainer()
          );
        this.loadListeners();
        return result;
      }
      getNotionContainerStyle() {
        const style = {
          position: 'fixed',
          top: 0,
          left: 0,
          right: 0,
          bottom: 0,
          display: this.state.error ? 'none' : 'flex',
        };
        return style;
      }
      getErrorContainerStyle() {
        const style = {
          position: 'fixed',
          top: 0,
          left: 0,
          right: 0,
          bottom: 0,
          display: this.state.error ? 'flex' : 'none',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          padding: 24,
          paddingBottom: '8vh',
        };
        return style;
      }
      getSearchWebviewStyle() {
        const style = {
          width: '100%',
          height: '100%',
          transition: 'transform 70ms ease-in',
          transform: 'translateY(-100%)',
          pointerEvents: 'none',
        };
        if (this.state.searching) {
          style.transition = 'transform 70ms ease-out';
          style.transform = 'translateY(0%)';
          style.pointerEvents = 'auto';
        }
        return style;
      }
      getSearchContainerStyle() {
        const style = {
          position: 'fixed',
          overflow: 'hidden',
          pointerEvents: 'none',
          padding: '0 20px',
          top:
            (this.state.searchingPeekView
              ? 0
              : process.platform === 'darwin'
              ? 37
              : 45) * this.state.zoomFactor,
          right: (48 - 24) * this.state.zoomFactor,
          width: 440 * this.state.zoomFactor,
          height: 72 * this.state.zoomFactor,
        };
        return style;
      }
    }
    Index.frontMessageStyle = {
      paddingTop: 16,
      paddingBottom: 16,
      textAlign: 'center',
      lineHeight: 1.4,
      fontSize: 17,
      letterSpacing: '-0.01em',
      color: '#424241',
      fontWeight: 500,
    };
    Index.reloadButtonStyle = {
      background: '#fefaf8',
      border: '1px solid #f1cbca',
      boxSizing: 'border-box',
      boxShadow: '0px 1px 2px rgba(0, 0, 0, 0.1)',
      borderRadius: 3,
      lineHeight: 'normal',
      fontSize: 14,
      fontWeight: 600,
      letterSpacing: '-0.03em',
      color: '#d8615c',
      paddingLeft: 12,
      paddingRight: 12,
      height: 36,
    };
    Index.frontImageStyle = {
      width: 300,
      maxWidth: '100%',
    };
    Index.notionWebviewStyle = {
      width: '100%',
      height: '100%',
      display: 'none',
    };
    Index.dragRegionStyle = {
      position: 'absolute',
      zIndex: 9999,
      top: 0,
      left: 0,
      right: 0,
      height: 2,
      pointerEvents: 'none',
      WebkitAppRegion: 'drag',
    };

    window['__start'] = () => {
      const parsed = url.parse(window.location.href, true),
        notionUrl =
          parsed.query.path ||
          schemeHelpers.getSchemeUrl({
            httpUrl: config.default.baseURL,
            protocol: config.default.protocol,
          });
      delete parsed.search;
      delete parsed.query;
      const plainUrl = url.format(parsed);
      window.history.replaceState(undefined, undefined, plainUrl);
      document.title = localizationHelper
        .createIntlShape(
          localizationHelper.getNotionLocaleFromElectronLocale(
            window.navigator.language
          )
        )
        .formatMessage(
          notion_intl.defineMessages({
            documentTitle: {
              id: 'desktop.documentTitle',
              defaultMessage: 'Notion Desktop',
            },
          }).documentTitle
        );
      const rootElm = document.getElementById('root');
      ReactDOM.render(
        React.createElement(Index, { notionUrl: notionUrl }),
        rootElm
      );
      tab(0);
    };
  } else {
    const __start = window['__start'];
    window['__start'] = () => {
      __start();
      const dragarea = document.querySelector(
          '#root [style*="-webkit-app-region: drag"]'
        ),
        default_styles = dragarea.getAttribute('style');
      if (store().tiling_mode) {
        dragarea.style.display = 'none';
      } else {
        document
          .getElementById('notion')
          .addEventListener('ipc-message', (event) => {
            if (event.channel !== 'enhancer:sidebar-width') return;
            dragarea.setAttribute(
              'style',
              `${default_styles} top: 2px; height: ${
                store().dragarea_height
              }px; left: ${event.args[0]};`
            );
          });

        document.getElementById('notion').addEventListener('dom-ready', () => {
          document
            .getElementById('notion')
            .getWebContents()
            .executeJavaScript(insertCSP);
        });
      }
    };
  }
};