import React from 'react';
import ReactDOM from 'react-dom';
import { loadableReady } from '@loadable/component';
import './polyfills';

// styling
import './styles/seedMain.scss';

import config from 'config/general';
import * as serviceWorker from './serviceWorker';

import configBrowserStore from './store/setup/client';

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

const rootElement = document.getElementById('root');

const preloadedState = window.__PRELOADED_STATE__;
// remove _persist from preloaded state from server to avoid problem with persist rehydratation if the front has to rehydrate too.
if (preloadedState?._persist) delete preloadedState._persist;

// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__;

const storeSetup = configBrowserStore({
  initialState: preloadedState,
});

// Tell react-snap how to save Redux state
window.snapSaveState = () => {
  return {
    __PRELOADED_STATE__: storeSetup.store.getState(),
  };
};

// Render app
const renderApp = (dev = false): JSX => {
  // VERY IMPORTANT FOR HOT RELOADING WITH ROUTER V4+
  const ClientApp = require('./appComponents/ClientApp').default;

  if (!rootElement) throw new Error('rootElement not found!');

  const persistor = storeSetup.persistor;

  const isSSR = __SSR__ && rootElement.hasChildNodes();
  const renderMethod = !dev && rootElement.hasChildNodes() ? ReactDOM.hydrate : ReactDOM.render;

  // render or hydrate app
  const renderRoot = () =>
    renderMethod(<ClientApp store={storeSetup.store} persistor={storeSetup.persistor} history={storeSetup.history} />, rootElement);

  // allows to not use the PersistGate
  const waitPersistor = () => {
    const { bootstrapped } = persistor.getState();
    if (bootstrapped) renderRoot();
    else {
      persistor.subscribe(waitPersistor);
    }
  };

  // loadableReady must only be called in SSR
  if (isSSR) {
    loadableReady(waitPersistor);
  } else waitPersistor();
};

// Render error
const renderError = (error): JSX => {
  const RedBox = require('redbox-react').default;

  if (!rootElement) throw new Error('rootElement not found!');

  ReactDOM.render(<RedBox error={error} />, rootElement);
};

// Development Tools
// ------------------------------------
if (config.locale) {
  if (module.hot) {
    const render = (): JSX => {
      try {
        renderApp(true);
      } catch (e) {
        console.error(e);
        renderError(e);
      }
    };

    // Setup hot module replacement
    module.hot.accept(['./appComponents/ClientApp'], (): void =>
      setImmediate((): void => {
        ReactDOM.unmountComponentAtNode(rootElement);
        render(true);
      }),
    );

    // Setup hot module replacement
    module.hot.accept(['./styles/seedMain.scss'], (): void =>
      setImmediate((): void => {
        ReactDOM.unmountComponentAtNode(rootElement);
        render(true);
      }),
    );
  }
}

// Let's Go!
// ------------------------------------
renderApp();
