import React from 'react';
import ReactDOM from 'react-dom';
import {Alert} from './popups/Alert';
import {Confirm} from './popups/Confirm';
import {PopupDispatcher} from './popups/PopupDispatcher';
import {NotificationDispatcher} from './NotificationDispatcher';
import {Notification} from './Notification';
import {isReactElement} from '../utils/functions';

export const popupContainerId = 'popup-container-' + Math.random();
let popupDispatcher;
let notificationDispatcher;

(function initializePopupContainer() {
    const popupContainer = document.createElement('div');
    popupContainer.id = popupContainerId;

    const wrapper = document.querySelector('.bootstrap-rc');

    if (wrapper) {
        wrapper.appendChild(popupContainer);
    } else {
        // FIXME [german.smirnov-30.01.19] HOTFIX for storybook. It initializes script before html. It should be throw new Error
        const bootstrapRC = document.createElement('div');
        bootstrapRC.className = 'bootstrap-rc';
        bootstrapRC.appendChild(popupContainer);
        document.body.appendChild(bootstrapRC);

        // eslint-disable-next-line no-console
        console.error('Initialize div.bootstrap-rc before use web-modules-core');
    }
})();

export function showPopup(componentOrElement, props = {}, popupContext = {}) {
    let {messageText, ...rest} = props || {};
    let element = isReactElement(componentOrElement)
        ? componentOrElement
        : React.createElement(componentOrElement, rest, messageText);

    // TODO: (UIA-70822) temporary solution caused by React 16 async rendering
    setTimeout(() => {
        if (!popupDispatcher || !element.props.stackable) {
            popupDispatcher = ReactDOM.render(
                React.createElement(PopupDispatcher, {popupContext}),
                document.getElementById(popupContainerId)
            );
        }

        popupDispatcher.showPopup(element);
    }, 0);
}

export function closePopup() {
    if (popupDispatcher) {
        popupDispatcher.closePopup();
    }
}

export function showAlert(params) {
    if (typeof params !== 'object') {
        params = {messageText: params};
    }
    showPopup(Alert, params);
}

export function showConfirmation(params) {
    showPopup(Confirm, params);
}

/**
 * Show "toast" notification at the top of the page
 *
 * @example Pre-rendered component
 * showNotification(
 *   <Notification success>
 *     {translate(langCommon.DONE)}
 *   </Notification>
 * );
 *
 * @example Props for Notification component
 * showNotification({
 *   messageText: translate(langCommon.DONE),
 *   success: true
 * });
 *
 * @example Custom component with props
 * showNotification(Panel, {
 *   messageText: translate(langCommon.DONE)
 * });
 *
 * @example Default notification
 * showNotification(translate(langCommon.DONE));
 *
 * @example Error notification
 * showNotification(translate(langCommon.DONE), 'error');
 *
 * @param {Object|string} componentOrElement - Component, Element, Notification properties or text message
 * @param {Object|string} props - Properties for Component or type of notification if componentOrElement is a text message
 */
export function showNotification(componentOrElement, props = {}) {
    let element = null;
    if (isReactElement(componentOrElement)) {
        element = componentOrElement;
    } else if (arguments.length === 1 && typeof componentOrElement === 'object') {
        let {messageText, ...rest} = componentOrElement;
        element = React.createElement(Notification, rest, messageText);
    } else if (typeof componentOrElement === 'string') {
        let type = typeof props === 'string' ? props : 'default';
        element = React.createElement(Notification, {[type]: true}, componentOrElement);
    } else {
        let {messageText, ...rest} = props;
        element = React.createElement(componentOrElement, rest, messageText);
    }

    if (!notificationDispatcher) {
        notificationDispatcher = ReactDOM.render(
            React.createElement(NotificationDispatcher),
            document.getElementById('notification-container')
        );
    }

    notificationDispatcher.showNotification(element);
}

export function getElementHeight(element, withMargin = false) {
    let height;
    let margin;
    if (document.all) {
        height = parseInt(element.currentStyle.height, 10);
        margin = withMargin
            ? parseInt(element.currentStyle.marginTop, 10) +
              parseInt(element.currentStyle.marginBottom, 10)
            : 0;
    } else {
        let styles = document.defaultView.getComputedStyle(element, '');
        height = parseInt(element.offsetHeight, 10);
        margin = withMargin
            ? parseInt(styles.getPropertyValue('margin-top')) +
              parseInt(styles.getPropertyValue('margin-bottom'))
            : 0;
    }
    return (height || 0) + (margin || 0);
}

export function getElementMargin(node) {
    let style = window.getComputedStyle(node);

    return {
        marginTop: extractCSSProperty(style.marginTop),
        marginBottom: extractCSSProperty(style.marginBottom),
        marginLeft: extractCSSProperty(style.marginLeft),
        marginRight: extractCSSProperty(style.marginRight)
    };
}

export function getElementPadding(node) {
    let style = window.getComputedStyle(node);

    return {
        paddingTop: extractCSSProperty(style.paddingTop),
        paddingBottom: extractCSSProperty(style.paddingBottom),
        paddingLeft: extractCSSProperty(style.paddingLeft),
        paddingRight: extractCSSProperty(style.paddingRight)
    };
}

export function getElementBorder(node) {
    let style = window.getComputedStyle(node);

    return {
        borderTop: extractCSSProperty(style.borderTop),
        borderBottom: extractCSSProperty(style.borderBottom),
        borderLeft: extractCSSProperty(style.borderLeft),
        borderRight: extractCSSProperty(style.borderRight)
    };
}

function extractCSSProperty(propertyValue) {
    return parseInt(propertyValue, 10) || 0;
}

export function scrollToTop() {
    document.body.scrollTop = document.documentElement.scrollTop = 0;
}

/**
 * Scrolls window to the elements node position
 * @param {React.Element} element || DOM node
 */
export function scrollToNode(element) {
    if (!element) {
        return;
    }

    let position;
    if (element.getBoundingClientRect) {
        position = element.getBoundingClientRect();
    } else {
        let node = ReactDOM.findDOMNode(element);
        position = node && node.getBoundingClientRect();
    }

    if (position) {
        window.scrollTo(position.x, position.y);
    }
}

export function getScrollbarWidth() {
    let outer = document.createElement('div');
    outer.style.visibility = 'hidden';
    outer.style.width = '100px';
    outer.style.msOverflowStyle = 'scrollbar';

    document.body.appendChild(outer);

    let widthNoScroll = outer.offsetWidth;
    outer.style.overflow = 'scroll';

    let inner = document.createElement('div');
    inner.style.width = '100%';
    outer.appendChild(inner);

    let widthWithScroll = inner.offsetWidth;

    outer.parentNode.removeChild(outer);

    return widthNoScroll - widthWithScroll;
}

export const restorePopupVisibility = ($container = document.getElementById(popupContainerId)) => {
    const $topPopup = $container.querySelector(
        'div[role="document"][aria-hidden="true"]:last-child'
    );

    if ($topPopup) {
        $topPopup.removeAttribute('aria-hidden');
    }
};
