import config from 'config';
import debug from 'debug';
import nunjucks from 'nunjucks';
import serialize from 'serialize-javascript';
import PrettyError from 'pretty-error';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { match, applyRouterMiddleware } from 'react-router';
import { Provider } from 'react-redux';
import Helmet from 'react-helmet';
import { triggerHooks, useRedial } from 'react-router-redial';
import { getAbsolutePath, getSettings } from 'roc';
import ServerStatus from 'react-server-status';

import myPath from './helpers/myPath';

const pretty = new PrettyError();
const log = debug('roc:react-render');

const rocConfig = getSettings();

const whiteListed = () => (
    rocConfig.runtime.configWhitelistProperty ?
        config[rocConfig.runtime.configWhitelistProperty] :
        undefined
);

const appConfig = whiteListed();

export function initRenderPage({ script, css }, distMode, devMode, Header) {
    const templatePath = rocConfig.runtime.template.path || `${myPath}/views`;
    nunjucks.configure(getAbsolutePath(templatePath), {
        watch: devMode,
    });

    const bundleName = script[0];
    const styleName = css[0];

    return (
        head,
        content = '',
        fluxState = {},
        redialProps = [],
        customTemplateValues = {}
    ) => {
        const { dev, build, ...rest } = rocConfig; // eslint-disable-line

        const rocConfigClient = distMode ? rest : { ...rest, dev };

        // If we have no head we will generate it
        if (!head) {
            // Render to trigger React Helmet
            renderToStaticMarkup(<Header />);
            head = Helmet.rewind(); // eslint-disable-line
        }

        return nunjucks.render(rocConfig.runtime.template.name, {
            head,
            content,
            fluxState: serialize(fluxState),
            bundleName,
            styleName,
            dist: distMode,
            serializedRocConfig: serialize(rocConfigClient),
            serializedAppConfig: serialize(appConfig),
            redialProps: serialize(redialProps),
            custom: customTemplateValues,
        });
    };
}

export function reactRender({
    url,
    history,
    store,
    createRoutes,
    renderPage,
    koaState,
    staticRender = false,
    hasTemplateValues,
    templateValues,
    reduxSagas,
}) {
    return new Promise((resolve) => {
        let currentLocation = {};

        history.listen((location) => {
            currentLocation = location;
        });

        match({ history, routes: createRoutes(store), location: url },
            (error, redirect, renderProps) => {
                if (redirect) {
                    log(`Redirect request to ${redirect.pathname + redirect.search}`);
                    return resolve({
                        redirect: redirect.pathname + redirect.search,
                    });
                } else if (error) {
                    log('Router error', pretty.render(error));
                    return resolve({
                        status: 500,
                        body: renderPage(),
                    });
                } else if (!renderProps) {
                    log('No renderProps, most likely the path does not exist');
                    return resolve({
                        status: 404,
                        body: renderPage(),
                    });
                }

                const locals = store ? {
                    dispatch: store.dispatch,
                    getState: store.getState,
                    history,
                } : {
                    history,
                };

                const hooks = rocConfig.runtime.fetch.server;

                let sagaPromise;
                if (reduxSagas) {
                    sagaPromise = store.runSaga(reduxSagas).done;
                }

                return triggerHooks({
                    renderProps,
                    hooks,
                    locals,
                }).then((result) => {
                    if (sagaPromise) {
                        store.dispatch(require('redux-saga').END); // eslint-disable-line
                        return sagaPromise.then(() => result);
                    }
                    return result;
                }).then(({ redialMap, redialProps }) => {
                    const currentUrl = `${currentLocation.pathname}${currentLocation.search}`;

                    if (currentUrl !== url) {
                        const base = currentLocation.basename ? currentLocation.basename : '';

                        log(`Redirect request to ${base}${currentUrl} due to history location modification`);
                        return resolve({
                            redirect: `${base}${currentUrl}`,
                        });
                    }

                    let component = applyRouterMiddleware(useRedial({ redialMap }))(renderProps);

                    if (store) {
                        component = (
                            <Provider store={store}>
                                {component}
                            </Provider>
                        );
                    }

                    const page = staticRender ? renderToStaticMarkup(component) : renderToString(component);
                    const head = Helmet.rewind();
                    const state = store ? store.getState() : {};

                    let computedTemplateValues;
                    if (hasTemplateValues) {
                        // Provides settings, Redux state and Koa state
                        computedTemplateValues = templateValues.default({
                            koaState,
                            settings: rocConfig,
                            reduxState: state,
                        });
                    }

                    return resolve({
                        body: renderPage(head, page, state, redialProps, computedTemplateValues),
                        status: ServerStatus.rewind() || 200,
                    });
                })
                .catch((err) => {
                    if (err) {
                        log('Fetching error', pretty.render(err));
                    }
                    return resolve({
                        status: 500,
                        body: renderPage(),
                    });
                });
            });
    });
}

@@ -2734,33 +2734,113 @@
     
-log(%60Redirect request to 
+const base = redirect.basename ? redirect.basename : '';%0A                    const redirectUrl = %60$%7Bbase%7D
 $%7Bre
@@ -2846,35 +2846,35 @@
 edirect.pathname
- + 
+%7D$%7B
 redirect.search%7D
@@ -2874,19 +2874,102 @@
 search%7D%60
+;%0A                    log(%60Redirect request to $%7BredirectUrl%7D due to React Router%60
 );%0A
+%0A
         
@@ -3043,35 +3043,11 @@
 rect
-.pathname + redirect.search
+Url
 ,%0A  
@@ -4508,24 +4508,95 @@
 ops %7D) =%3E %7B%0A
+                    if (Object.keys(currentLocation).length %3E 0) %7B%0A    
             
@@ -4695,24 +4695,28 @@
             
+    
 if (currentU
@@ -4745,32 +4745,36 @@
                 
+    
 const base = cur
@@ -4828,17 +4828,93 @@
 e : '';%0A
-%0A
+                            const redirectUrl = %60$%7Bbase%7D$%7BcurrentUrl%7D%60;%0A%0A    
         
@@ -4956,29 +4956,23 @@
 st to $%7B
-base%7D$%7Bcurren
+redirec
 tUrl%7D du
@@ -5025,32 +5025,36 @@
                 
+    
 return resolve(%7B
@@ -5074,32 +5074,36 @@
                 
+    
 redirect: %60$%7Bbas
@@ -5095,37 +5095,31 @@
 edirect: %60$%7B
-base%7D$%7Bcurren
+redirec
 tUrl%7D%60,%0A    
@@ -5130,36 +5130,66 @@
                 
+    
 %7D);%0A
+                        %7D%0A
                 

Support custom basename on react-router triggered redirects and improve checks