import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Alert, { alertTypes } from '../../atoms/alert/Alert';
import withListener from '../withListener';
import { getAlerts, clearAlerts } from '../../tools/alerts';
import { createNamespace } from '../../tools/namingTools';
import './styles/GlobalAlerts.scss';

const n = createNamespace('GlobalAlert');

// how much time we must wait before displaying alert (seconds)
const APPEAR_DELAY = 0.5;

// how much time the alert stays on (seconds)
const DISAPPEAR_DELAY = 5;

const getStoredAlert = (questionMessages, leadMessages, reviewMessages) => {
    const getAlertType = state => {
        switch (state) {
            case 'question.add.success':
                return {
                    message: questionMessages.successAlert,
                    type: 'questionAlert',
                    alertType: alertTypes.SUCCESS_ALERT
                };
            case 'question.add.error':
                return {
                    message: questionMessages.errorAlert,
                    type: 'questionAlert',
                    alertType: alertTypes.ERROR_ALERT
                };
            case 'review.add.success':
                return {
                    message: reviewMessages.success.postEntry,
                    type: 'reviewAlert',
                    alertType: alertTypes.SUCCESS_ALERT
                };
            case 'lead.add.success':
                return {
                    message: leadMessages.sucessMessage,
                    type: 'leadAlert',
                    alertType: alertTypes.SUCCESS_ALERT
                };
            default:
                return undefined;
        }
    };
    const alertState = getAlerts();
    clearAlerts();
    return (alertState || []).map(type => getAlertType(type));
};

export const GlobalAlerts = ({
    data: { questionMessages = {}, leadMessages = {}, reviewMessages = {} },
    isInline,
    persist
}) => {
    const [alertMessages, setAlertMessages] = useState(
        getStoredAlert(questionMessages, leadMessages, reviewMessages).filter(alert => alert)
    );

    // we need a way to access whatever current state of alerts is in a persistent way from
    // mount to unmount because timeouts act on state values
    const persistentAlertMessages = useRef();
    persistentAlertMessages.current = alertMessages;

    const deactivateElement = element => {
        if (element.active === false || persist) {
            return element;
        }
        const removedTimeout = setTimeout(() => {
            const newMessages = persistentAlertMessages.current.map(item => (
                item.type === element.type ? { ...item, killed: true } : item
            ));
            // when all alerts have disappeared, remove them altogether. We can not remove them one
            // at a time because it would affect the layout of the widget and mess the display
            // if remaining alerts
            const hasAlerts = newMessages.find(item => !item.killed);
            setAlertMessages(hasAlerts ? newMessages : []);
        }, DISAPPEAR_DELAY * 1000);
        return {
            ...element,
            removedTimeout,
            active: false
        };
    };

    // setup timeouts for appear/disappear. Intentional that inner-referenced state vars
    // are not referenced in the dependencies, as this effect should run only on mount and unmount
    useEffect(() => {
        const tIn = setTimeout(() => {
            const newMessages = persistentAlertMessages.current.map(item => (
                { ...item, active: true }
            ));
            setAlertMessages(newMessages);
        }, APPEAR_DELAY * 1000);

        const tOut = setTimeout(() => {
            const newMessages = persistentAlertMessages.current.map(item => (
                deactivateElement(item)
            ));
            setAlertMessages(newMessages);
        }, (APPEAR_DELAY + DISAPPEAR_DELAY) * 1000);

        return () => {
            clearTimeout(tIn);
            clearTimeout(tOut);
            persistentAlertMessages.current.forEach(item => {
                if (item.removedTimeout) {
                    clearTimeout(item.removedTimeout);
                }
                if (item.appearTimeout) {
                    clearTimeout(item.appearTimeout);
                }
            });
        };
    }, []);

    const alerts = alertMessages && alertMessages.map(alert => {
        const removeAlert = () => {
            const newMessages = alertMessages.map(item => (
                item === alert ? deactivateElement(item) : item
            ));
            setAlertMessages(newMessages);
        };
        return (
            <div {...n('alert', alert.active && 'active').withTestId().props} key={`global_alert_${alert.type}`}>
                <Alert
                    isGlobal
                    alertMessage={alert.message}
                    type={alert.alertType}
                    onClose={removeAlert}
                />
            </div>
        );
    });

    const [portal, setPortal] = useState(null);
    useEffect(() => {
        const portalDomNode = document.createElement('div');
        portalDomNode.classList.add(n('portal').props.className);
        document.body.append(portalDomNode);
        setPortal(portalDomNode);
        return () => portalDomNode.remove();
    }, []);

    if (isInline) {
        return alerts;
    }
    if (portal) {
        return ReactDOM.createPortal(
            alerts,
            portal
        );
    }
    return null;
};

GlobalAlerts.schema = () => ({
    name: 'GlobalAlerts',
    title: 'uieditor.sfbComponent.globalAlerts.title',
    iconName: '',
    form: {},
    marketplaceData: {
        questionMessages: 'application.questions.i18n.labels',
        leadMessages: 'application.leads.i18n',
        reviewMessages: 'application.reviews.i18n.messages'
    }
});

GlobalAlerts.propTypes = {
    isInline: PropTypes.bool,
    persist: PropTypes.bool,
    data: PropTypes.shape({
        questionMessages: PropTypes.shape({
            successAlert: PropTypes.string,
            errorAlert: PropTypes.string
        }),
        leadMessages: PropTypes.shape({
            sucessMessage: PropTypes.string
        }),
        reviewMessages: PropTypes.shape({
            success: PropTypes.shape({
                postEntry: PropTypes.string
            })
        })
    })
};

GlobalAlerts.defaultProps = {
    isInline: false,
    persist: false,
    data: {
        questionMessages: {
            successAlert: 'Thank you for your question! We will post it shortly.',
            errorAlert: 'An error has occurred.'
        },
        leadMessages: {
            sucessMessage:
                'Thank you for submitting your information. A representative will follow up with you shortly!'
        },
        reviewMessages: {
            success: {
                postEntry: 'Thank you for the review, it will be posted shortly.'
            }
        }
    }
};

export default withListener(GlobalAlerts);
