import React from 'react';

import { config } from 'config/general';
import { renderToString } from 'react-dom/server';

// Bootup saga
import { bootup } from 'store/bootup';
import configServerStore from 'store/setup/server';
import ServerApp from 'appComponents/ServerApp';
import auth from 'store/auth';
import UniversalCookies from 'universal-cookie';
import Cookies from 'cookies';

import get from 'lodash/get';

import cors from 'cors';
import bodyParser from 'body-parser';
import cookiesMiddleware from 'universal-cookie-express';
import mobile from 'is-mobile';
import path from 'path';
import orderBy from 'lodash/orderBy';
import appActions from 'store/app/actions';
import { ChunkExtractor } from '@loadable/server';
import StaticData from './staticData';

const refreshData = (req, res) => {
  StaticData.getInstance().clearAll();

  return res.send('Static data cleared');
};

const handleRedirection = (matches, to) => {
  if (matches && matches.length) {
    let redirect = to;

    if (redirect && redirect.indexOf('$') >= 0) {
      matches.forEach((m, index) => {
        const key = `$${index}`;

        if (redirect.indexOf(key) >= 0) {
          redirect = redirect.replace(key, m);
        }
      });
    }

    return redirect;
  }
  return '';
};

const handleRedirections = (req, res, storeState) => {
  const serverContent = get(storeState, config.serverRedirectionContentPath || 'content.raw.server.redirections');

  const redirections = orderBy(serverContent, 'position');

  if (redirections) {
    const list = Object.values(redirections);

    for (let i = 0; i < list.length; i++) {
      const redirection = list[i];

      const origin = redirection.origin.value;
      const to = redirection.to.value;
      const status = redirection.type.value ? parseInt(redirection.type.value, 10) : 301;
      // deprecated
      const hostname = redirection.hostname && redirection.hostname.value === 'yes';
      const disable = redirection.disable && redirection.disable.value === 'yes';

      if (disable) continue;

      const regex = new RegExp(origin, 'i');

      // deprecated
      if (hostname) {
        const matches = req.hostname.match(regex);

        const redirect = handleRedirection(matches, to);

        if (redirect) {
          res.redirect(status, redirect + req.url);
          return true;
        }
      }

      const matches = req.url.match(regex);

      if (matches && matches.length) {
        const redirect = handleRedirection(matches, to);

        if (redirect) {
          res.redirect(status, redirect);
          return true;
        }
      }
    }
  }
  return false;
};

const handleRender = async (req, res, sls) => {
  const start = Date.now();

  // This is the stats file generated by webpack loadable plugin
  const webStats = sls
    ? path.resolve(config.locale ? './sls/client/loadable-stats.json' : 'loadable-stats-client.json')
    : path.resolve(`./ssr/client/loadable-stats.json`);

  // We create an extractor from the statsFile
  const webExtractor = new ChunkExtractor({ statsFile: webStats, entrypoints: ['bundle'] });

  let isMobile = mobile({ ua: req });

  let isTablet = false;
  if (req.headers['cloudfront-is-mobile-viewer'] === 'true') isMobile = true;
  if (req.headers['cloudFront-is-tablet-viewer'] === 'true') isTablet = true;

  // allows debug for cloudfront headers
  if (req.headers['cloudfront-is-mobile-viewer']) {
    console.log('HEADERS', req.headers);
    console.log('isMobile', isMobile);
    console.log('isTablet', isTablet);
  }

  const storeSetup = await configServerStore({
    req,
    res,
  });

  const store = storeSetup.store;

  // Dispatch login success session
  if (!config.serverPersistStorage) {
    const cookies = new UniversalCookies(req.headers.cookie);

    const session = cookies.get(config.sessionCookie);

    // dispatch session and other cookie store infos
    if (session) store.dispatch(auth.actions.login.result.action(session));
  }

  if (isMobile) store.dispatch(appActions.mobile.action(true));
  if (isTablet) store.dispatch(appActions.tablet.action(true));

  // Launch bootup
  await storeSetup.sagaRun(bootup, req.url, req.query || null).done;

  console.log('bootup done', Date.now() - start, 'ms');

  const storeState = store.getState();

  const redirect = handleRedirections(req, res, storeState);

  if (redirect) return null;

  // Wrap your application using "collectChunks"

  const jsx = webExtractor.collectChunks(<ServerApp location={req.url} context={req} store={store} />);

  // Render your application
  const content = renderToString(jsx);

  const state = JSON.stringify(storeState);

  // You can now collect your script tags
  const scriptElements = webExtractor.getScriptElements(); // or webExtractor.getScriptElements();
  // You can also collect your "preload/prefetch" links
  const linkElements = webExtractor.getLinkElements(); // or webExtractor.getLinkElements();
  // And you can even collect your style tags (if you use "mini-css-extract-plugin")
  const styleElements = webExtractor.getStyleElements(); // or webExtractor.getStyleElements();

  return { state, content, scriptElements, linkElements, styleElements };
};

const handleServer = (app, robotsTxt) => {
  // If usage of redux-persist-cookie-storage
  // see https://github.com/abersager/redux-persist-cookie-storage
  if (config.serverPersistStorage) {
    app.use(Cookies.express());
  } else {
    app.use(cookiesMiddleware());
  }

  app.use(cors());

  // Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
  app.use(bodyParser.json());

  if (robotsTxt) {
    app.use('/robots.txt', (req, res) => {
      return res.send(robotsTxt);
    });
  }

  app.use('/refresh-static', refreshData);

  // eslint-disable-next-line no-unused-vars
  app.use((err, req, res, next): void =>
    res.status(404).json({
      status: 'error',
      message: err.message,
      stack:
        // print a nicer stack trace by splitting line breaks and making them array items (can be improved)
        process.env.NODE_ENV === 'development' &&
        (err.stack || '')
          .split('\n')
          .map((line): string => line.trim())
          .map((line): string => line.split(path.sep).join('/'))
          .map((line): string =>
            line.replace(
              process
                .cwd()
                .split(path.sep)
                .join('/'),
              '.',
            ),
          ),
    }),
  );

  return app;
};

export { handleRedirections, handleRender, handleServer };

export default handleRedirections;
