import React, { useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';

import { createNamespace } from '../../../../../tools/namingTools';
import { useResponsive } from '../../../../../tools/widths/useResponsive';
import { useContainerWidth } from '../../../../../tools/widths/useContainerWidth';
import { getNumberOfItemsThatFit } from '../../../../../tools/components/headerNavigation';
import { NAVIGATION_TEXT_ALIGN, NAVIGATION_TEXT_SIZE } from '../../../../../constants/components/header';
import { categoryShape, headerCategoriesMenuSettingsShape } from '../../../../../constants/components/prop-types/header';
import { headerCategoriesMenuDefaultSettings } from '../../../../../constants/components/schemas/header';
import { getPropValue } from '../../../../../tools/objectTools';
import { getListingUrlForProductLines } from '../../../../../tools/urlTools';
import ButtonIcon from '../../../../../atoms/button-icon/ButtonIcon';
import Link from '../../../../../atoms/link/Link';
import { MENUS } from '../../../../../constants/menus';

import HeaderCategoriesMenu from '../header-categories-menu/HeaderCategoriesMenu';

import './styles/HeaderNavigation.scss';
import { TARGET_BLANK } from '../../../../../constants/buttons';

const n = createNamespace('HeaderNavigation');

const CATEGORIES_OPEN_FLAG = 'categories_open';

const HeaderNavigation = ({
    items: navItems,
    align: navAlignment,
    i18n: i18nProp,
    textSize,
    ariaLabel,
    categories,
    categoriesMenuSettings,
    listingEndpoint,
    menuType,
    locale,
    toggleHeaderScrimFunction,
    ...otherProps
}) => {
    const [openSubmenus, setOpenSubmenus] = useState([]);
    const [containerRef, containerWidth] = useContainerWidth();
    const { mediumDown: isMobile } = useResponsive();

    const navigationRef = useRef(null);
    const menuItemsRefs = useRef([]);
    const moreMenuItemsRefs = useRef([]);
    const moreButtonRef = useRef();

    const isHomeBarMenu = menuType && menuType === MENUS.HOME_BAR_MENU;
    const isNavBarMenu = menuType && menuType === MENUS.NAVIGATION_BAR_MENU;
    const setMenuItemRef = (refsArray, node) => {
        if (node && !refsArray.includes(node)) {
            refsArray.push(node);
        }
    };

    const scrimToggle = {
        onFocus: () => toggleHeaderScrimFunction(true),
        onMouseOver: () => toggleHeaderScrimFunction(true),
        onMouseLeave: () => toggleHeaderScrimFunction(false)
    };

    const resetMenuItemsVisibility = () => {
        moreButtonRef.current.style.removeProperty('display');
        menuItemsRefs.current.forEach(elem => elem.style.removeProperty('display'));
        moreMenuItemsRefs.current.forEach(elem => elem.style.removeProperty('display'));
    };

    const updateMenuItemsVisibility = numberOfVisibleElements => {
        if (numberOfVisibleElements === menuItemsRefs.current.length) {
            moreButtonRef.current.style.setProperty('display', 'none');
            return;
        }
        menuItemsRefs.current.forEach((element, index) => {
            if (index >= numberOfVisibleElements) {
                element.style.setProperty('display', 'none');
            }
        });
        const nbrItemsInMoreToDisplay = (categories && categories.length)
            ? numberOfVisibleElements - 1
            : numberOfVisibleElements;
        moreMenuItemsRefs.current.forEach((element, index) => {
            if (index < nbrItemsInMoreToDisplay) {
                element.style.setProperty('display', 'none');
            }
        });
    };

    const updateNumberOfVisibleItems = () => {
        if (menuItemsRefs.current.length === 0 || moreMenuItemsRefs.current.length === 0 || containerWidth === 0) {
            return;
        }

        resetMenuItemsVisibility();

        // Find number of items that fit
        const numberOfItemsThatFit = getNumberOfItemsThatFit(menuItemsRefs, moreButtonRef, containerWidth);
        updateMenuItemsVisibility(numberOfItemsThatFit);
    };

    useEffect(updateNumberOfVisibleItems);

    if ((!navItems || !navItems.length) && (!categories || !categories.length)) {
        return null;
    }

    const i18n = { ...HeaderNavigation.defaultProps.i18n, ...i18nProp };

    const {
        className: extraClass,
        'data-testid': parentTestId,
        withMoreButton
    } = otherProps;

    const renderSubMenu = (subMenuItems, modifiers = [], setItemsArrayFunction = () => { }) => {
        if (!subMenuItems || !subMenuItems.length) {
            return null;
        }

        return (
            <ul
                {...n('subMenu', modifiers).withTestId('subMenu').props}
            >
                {subMenuItems.map(item => {
                    const { isVisible, openInNewTab, uuid, editorType, customData } = item;
                    const titleText = getPropValue(item, 'title.value');
                    let linkUrl = getPropValue(item, 'link.value');
                    const target = openInNewTab ? TARGET_BLANK : null;
                    if (!isVisible || !titleText) {
                        return null;
                    }
                    if (editorType === 'PRODUCT_LINES') {
                        linkUrl = getListingUrlForProductLines(locale.code, customData && customData.externalUUID);
                    }
                    return (
                        <li
                            {...n('subMenuItem').withTestId('subMenuItem').props}
                            key={uuid}
                            ref={setItemsArrayFunction}
                        >
                            <Link {...n('subMenuLink').props} href={linkUrl || ''} target={target}>{titleText}</Link>
                        </li>
                    );
                })}
            </ul>
        );
    };

    const caretIcon = isMobile ? 'caretRight' : 'angleDown';
    const renderNavItem = item => {
        const { isVisible, openInNewTab, children, uuid, editorType, customData } = item;
        const titleText = getPropValue(item, 'title.value');
        let linkUrl = getPropValue(item, 'link.value');

        if (!isVisible || !titleText) {
            return null;
        }

        if (editorType === 'PRODUCT_LINES') {
            linkUrl = getListingUrlForProductLines(locale.code, customData && customData.externalUUID);
        }
        const hasChildren = children && children.length > 0;
        const target = openInNewTab ? TARGET_BLANK : null;
        const openSubmenu = () => {
            setOpenSubmenus([...openSubmenus, item]);
        };
        const closeSubmenu = () => {
            const newState = [...openSubmenus];
            newState.pop();
            setOpenSubmenus(newState);
        };
        const submenuIsOpen = openSubmenus.includes(item);
        const itemModifiers = [];

        return (
            <li
                {...n('item', itemModifiers).withTestId('menuItem').props}
                ref={r => setMenuItemRef(menuItemsRefs.current, r)}
                key={uuid}
            >
                {hasChildren ? (
                    <>
                        <ButtonIcon
                            {...n('btnMenu').props}
                            icon={caretIcon}
                            iconPosition="right"
                            iconClass={n('btnMenuIcon').props.className}
                            textClass={n('btnMenuText').props.className}
                            onFocus={openSubmenu}
                            onBlur={closeSubmenu}
                            url={linkUrl || ''}
                            openUrlInNewTab={openInNewTab}
                        >
                            {titleText}
                        </ButtonIcon>
                        {renderSubMenu(children, [submenuIsOpen && 'visible'])}
                    </>
                ) : (
                    <Link
                        {...n('link').props}
                        href={linkUrl || ''}
                        target={target}
                    >
                        {titleText}
                    </Link>
                )}
            </li>
        );
    };

    const categoriesMenuProps = {
        ...n('subMenu', [
            'categories',
            openSubmenus.includes(CATEGORIES_OPEN_FLAG) && 'visible'
        ]).props,
        categories,
        i18n,
        listingEndpoint,
        toggleHeaderScrimFunction,
        settings: categoriesMenuSettings
    };
    const openCategories = () => {
        setOpenSubmenus([...openSubmenus, CATEGORIES_OPEN_FLAG]);
    };
    const closeCategories = () => {
        const newState = [...openSubmenus];
        newState.pop();
        setOpenSubmenus(newState);
    };

    return (
        <>
            <div {...n('widthReference', [isHomeBarMenu && 'hidden']).withTestId('widthReference').props} ref={containerRef} />
            <div {...n('wrapper', [textSize]).withTestId(parentTestId).withClass(extraClass).props}>
                <nav
                    {...n('nav').props}
                    aria-label={ariaLabel ?? i18n.title}
                    ref={navigationRef}
                >
                    <ul {...n('list', [navAlignment && `align-${navAlignment}`]).withTestId('navList').props}>
                        {isNavBarMenu && (
                            <li
                                ref={r => setMenuItemRef(menuItemsRefs.current, r)}
                                {...n('item').withTestId('menuItem').props}
                                {...scrimToggle}
                            >
                                <ButtonIcon
                                    {...n('btnMenu').props}
                                    icon={caretIcon}
                                    iconPosition="right"
                                    iconClass={n('btnMenuIcon').props.className}
                                    textClass={n('btnMenuText').props.className}
                                    onFocus={openCategories}
                                    onBlur={closeCategories}
                                >
                                    {i18nProp['all.products']}
                                </ButtonIcon>
                                <HeaderCategoriesMenu {...categoriesMenuProps} />
                            </li>
                        )}
                        {navItems.map(renderNavItem)}
                        {withMoreButton && (
                            <li
                                ref={moreButtonRef}
                                {...n('item').withTestId('menuItem').props}
                            >
                                <ButtonIcon
                                    {...n('btnMenu').props}
                                    icon={caretIcon}
                                    iconPosition="right"
                                    iconClass={n('btnMenuIcon').props.className}
                                    textClass={n('btnMenuText').props.className}
                                >
                                    {i18n.more}
                                </ButtonIcon>
                                {renderSubMenu(navItems, [], r => setMenuItemRef(moreMenuItemsRefs.current, r))}
                            </li>
                        )}
                    </ul>
                </nav>
            </div>
        </>
    );
};

HeaderNavigation.propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape()),
    align: PropTypes.oneOf(NAVIGATION_TEXT_ALIGN.map(({ value }) => value)),
    i18n: PropTypes.shape({
        title: PropTypes.string,
        more: PropTypes.string,
        'all.products': PropTypes.string
    }),
    listingEndpoint: PropTypes.string,
    textSize: PropTypes.oneOf(NAVIGATION_TEXT_SIZE.map(({ value }) => value)),
    ariaLabel: PropTypes.string,
    categories: PropTypes.arrayOf(PropTypes.shape(categoryShape)),
    categoriesMenuSettings: PropTypes.shape(headerCategoriesMenuSettingsShape),
    menuType: PropTypes.string,
    locale: PropTypes.object,
    toggleHeaderScrimFunction: PropTypes.func
};

HeaderNavigation.defaultProps = {
    items: [],
    align: null,
    listingEndpoint: '/',
    i18n: {
        title: 'Header Navigation Bar',
        more: 'More ...',
        'all.products': 'All Products'
    },
    textSize: undefined,
    ariaLabel: null,
    categories: [],
    categoriesMenuSettings: headerCategoriesMenuDefaultSettings,
    menuType: '',
    locale: {
        code: 'en-US'
    },
    toggleHeaderScrimFunction: () => {}
};

export default HeaderNavigation;
