import React, { useEffect, useState, useMemo, useCallback } from 'react';
import PropTypes from 'prop-types';
import merge from 'deepmerge';
import { createNamespace } from '../../tools/namingTools';
import { GRID_VIEW, LIST_VIEW } from '../../constants/views';
import './styles/ProductListingControls.scss';

import { compareProductsStore } from '../../store/compareProducts';
import { productListControlsStore } from '../../store/productListControls';
import { productListDataStore } from '../../store/productListData';

import Select from '../../atoms/select/Select';
import Button from '../../atoms/button/Button';
import withListener from '../withListener';
import ToggleSwitch from '../../atoms/toggle-switch/ToggleSwitch';
import SlidingPanel from '../../atoms/sliding-panel/SlidingPanel';
import RadioGroup from '../../atoms/radio-group/RadioGroup';
import Icon from '../../atoms/icon/Icon';
import BreadCrumbs from '../../atoms/bread-crumbs/BreadCrumbs';

import { getUrlParams, navigateTo, buildCompareUrl } from '../../tools/urlTools';
import { useResponsive } from '../../tools/widths/useResponsive';
import { useBodyFixed } from '../../tools/useBodyFixed';
import { selectedSortByOptions } from '../../constants/sortingOptions';
import { RATING_QUERY_KEY, PLATFORM_QUERY_KEY, FILTER_QUERY_KEY, CAT_QUERY_KEY, ATTR_QUERY_KEY, PRODUCT_LINE_QUERY_KEY } from '../../constants/urlParams';

const NAMESPACE = 'ProductListingControls';
const EVENT_SHOW = 'show';
const EVENT_HIDE = 'hide';

const n = createNamespace(NAMESPACE);

const MAX_NUMBER_CATEGORY_TITLES = 2;

export const ProductListingControlsComponent = ({
    data: {
        sorting: {
            options: sortingOptions
        },
        compareBaseUrl,
        homeUrl,
        i18n
    }
}) => {
    const i18nWithDefaults = merge(ProductListingControlsComponent.defaultProps.data.i18n, i18n);

    const { list: compareList } = compareProductsStore();
    const [compareUrl, setCompareUrl] = useState('');

    // Build the compare url when the component loads and when compareList is updated
    useEffect(() => {
        const updatedCompareUrl = buildCompareUrl(compareBaseUrl, compareList);
        setCompareUrl(updatedCompareUrl);
    }, [compareList, compareBaseUrl]);

    const [isSortOptionsVisible, setSortOptionsVisible] = useState(false);

    const { setListingPage, sortBy, setSortBy, filtersDetails,
        listingCategories, nAppliedFilters, searchResultCount } = productListDataStore();

    // set state from prop values when component loads
    useEffect(() => {
        const getDefaultSortBy = () => {
            const queryParams = getUrlParams();
            const { order } = queryParams;
            if (!order && sortingOptions) {
                return sortingOptions.find(o => o.selected);
            }
            return selectedSortByOptions[order] || selectedSortByOptions.RELEVANCE;
        };
        const initialSortby = getDefaultSortBy();
        setSortBy(initialSortby.value);
    }, []);

    const { mediumUp, mediumDown } = useResponsive();

    const { viewType, setViewType,
        isFiltersVisible, setFiltersVisible,
        setListingSidebarTitle, listingSidebarTitle } = productListControlsStore();

    const setFiltersVisibleCallback = useCallback(
        newVal => {
            // trigger an event for the theme to respond to
            const eventType = newVal ? EVENT_SHOW : EVENT_HIDE;
            const eventName = `${NAMESPACE}:${eventType}`;
            const updateSidebarEvent = new CustomEvent(eventName, {
                bubbles: true
            });
            window.dispatchEvent(updateSidebarEvent);
            setFiltersVisible(newVal);
        },
        [setFiltersVisible]
    );

    useEffect(() => {
        const getTitle = () => {
            const isBetween0andMax = listingCategories
                && listingCategories.length > 0
                && listingCategories.length <= MAX_NUMBER_CATEGORY_TITLES;

            const isGreaterThanMax = listingCategories && listingCategories.length > MAX_NUMBER_CATEGORY_TITLES;

            if (isBetween0andMax) {
                return listingCategories.join(', ');
            }
            if (isGreaterThanMax) {
                return `${i18nWithDefaults.resultsFrom} ${listingCategories.length} ${i18nWithDefaults.categories}`;
            }
            return i18nWithDefaults.allApplications;
        };
        const sideBarTitle = getTitle();
        setListingSidebarTitle(sideBarTitle);
    }, [listingCategories]);

    // default behavior for state of filters is shown on desktop but hidden on mobile.
    // Run effect on mount or when breakpoint changes
    useBodyFixed(mediumDown && isFiltersVisible);
    useEffect(() => {
        if (mediumUp) {
            setFiltersVisibleCallback(true);
            setSortOptionsVisible(false);
        }

        if (mediumDown) {
            setFiltersVisibleCallback(false);
            setViewType(GRID_VIEW);
        }
    }, [mediumUp, mediumDown, setFiltersVisibleCallback, setViewType]);

    // if view type is set in store, use that value, otherwise use default value (list)
    const shouldShowAsGrid = viewType === GRID_VIEW;

    const viewModeOptions = [{
        id: LIST_VIEW,
        value: LIST_VIEW,
        label: i18nWithDefaults.list,
        selected: !shouldShowAsGrid,
        iconType: 'list'
    }, {
        id: GRID_VIEW,
        value: GRID_VIEW,
        label: i18nWithDefaults.grid,
        selected: shouldShowAsGrid,
        iconType: 'grid'
    }];

    const toggleViewMode = e => {
        setViewType(e.currentTarget.value);
    };

    const toggleFilters = () => {
        setFiltersVisibleCallback(!isFiltersVisible);
    };
    const toggleFiltersCheckbox = e => {
        setFiltersVisibleCallback(e.target.checked);
    };
    const toggleSortOptions = () => {
        setSortOptionsVisible(!isSortOptionsVisible);
    };

    const onSortChange = e => {
        const newSortValue = typeof e.value !== 'undefined' ? e.value : e.currentTarget && e.currentTarget.value;
        setSortBy(newSortValue);
        setListingPage(null);
    };

    // if more than one product selected, we can click compare button
    const canCompare = compareList.length > 1;

    const radioGroupName = 'order';

    const sortingOptionsSidebar = (
        <SlidingPanel
            onClose={toggleSortOptions}
            isVisible={isSortOptionsVisible}
            i18n={{ closePanel: i18nWithDefaults.closeSortingOptions }}
        >
            <div {...n('sortOptionsWrapper').props}>
                <div {...n('sortOptionsHeader').props}>
                    <span {...n('sortOptionsTitle').props}>{i18nWithDefaults.sortBy}</span>
                </div>
                <div {...n('sortOptionsContainer').props}>
                    <RadioGroup
                        name={radioGroupName}
                        label={i18nWithDefaults.sortBy}
                        id="order"
                        size="small"
                        value={sortBy}
                        onChange={onSortChange}
                        options={sortingOptions}
                    />
                </div>
                <div {...n('sortOptionsFooter').props}>
                    <Button type="primary" size="default" onClick={toggleSortOptions}>{i18nWithDefaults.applySortOptions}</Button>
                </div>
            </div>
        </SlidingPanel>
    );

    const toggleFiltersBtnClasses = [
        'button_item',
        'button_item--small',
        'button_item--white',
        !isFiltersVisible ? 'button_item--active' : undefined
    ].join(' ');
    const desktopActionsContainer = (
        <div {...n('desktop-actions').props}>
            <div {...n('hgroup').props}>
                {listingSidebarTitle && (
                    <span {...n('title').withE2EId().props}>{listingSidebarTitle}</span>
                )}
                {searchResultCount !== null ? (
                    <span {...n('results').withE2EId().props}>{`${searchResultCount} ${i18nWithDefaults.results}`}</span>
                ) : null}
            </div>
            <div {...n('filter').withE2EId().props}>
                {canCompare ? (
                    <div {...n('filterCompare').withE2EId().props}>
                        <Button
                            type="secondary"
                            size="small"
                            onClick={() => { navigateTo(compareUrl); }}
                        >
                            {i18nWithDefaults.compare}
                        </Button>
                    </div>
                ) : ''}
                <div {...n('filterSidebar').withE2EId().props}>
                    <input
                        {...n('toggleFiltersCheckbox').props}
                        type="checkbox"
                        id="toggleFiltersCheckbox"
                        checked={isFiltersVisible}
                        onChange={toggleFiltersCheckbox}
                    />
                    <label {...n('toggleFiltersBtn', [isFiltersVisible && 'active']).withClass(toggleFiltersBtnClasses).props} htmlFor="toggleFiltersCheckbox">
                        <div {...n('toggleFiltersLabel').withClass('button_text').withTestId().props}>
                            {isFiltersVisible ? i18nWithDefaults.hideFilters : i18nWithDefaults.showFilters}
                        </div>
                        <span className="button_icon">
                            <Icon type="filter" />
                        </span>
                    </label>
                </div>
                <div {...n('filterSort').withE2EId().props}>
                    <Select
                        name="order"
                        id="order"
                        size="small"
                        type="button"
                        value={sortBy}
                        label={i18nWithDefaults.sortBy}
                        onChange={onSortChange}
                        options={sortingOptions}
                        inline
                    />
                </div>
                <div {...n('filterView').withE2EId().props}>
                    <ToggleSwitch
                        name="viewMode"
                        size="small"
                        legend={i18nWithDefaults.viewMode}
                        options={viewModeOptions}
                        onChange={toggleViewMode}
                    />
                </div>
            </div>
        </div>
    );

    const appliedFiltersBadge = useMemo(() => (nAppliedFilters ? <span {...n('number-badge').props}>{nAppliedFilters}</span> : null), [nAppliedFilters]);

    const mobileActionsContainer = (
        <div {...n('mobile-actions').props}>
            <Button type="secondary" size="default" onClick={toggleFilters}>
                <div className="button_text">
                    {i18nWithDefaults.filters}
                    {appliedFiltersBadge}
                </div>
            </Button>
            <Button type="secondary" size="default" onClick={toggleSortOptions}>{i18nWithDefaults.sort}</Button>
            {canCompare ? (
                <Button
                    type="secondary"
                    size="default"
                    onClick={() => { navigateTo(compareUrl); }}
                >
                    {i18nWithDefaults.compare}
                </Button>
            ) : null}
        </div>
    );

    const breadcrumbs = useMemo(() => {
        if (!filtersDetails) {
            return null;
        }

        // attributes are a merge of different query keys
        const attributesCrumbs = [
            ...(filtersDetails[FILTER_QUERY_KEY] || []),
            ...(filtersDetails[ATTR_QUERY_KEY] || []),
            ...(filtersDetails[PLATFORM_QUERY_KEY] || []),
            ...(filtersDetails[RATING_QUERY_KEY] || [])
        ];

        const crumbGroups = [];

        if (filtersDetails[PRODUCT_LINE_QUERY_KEY] && filtersDetails[PRODUCT_LINE_QUERY_KEY].length) {
            crumbGroups.push({
                label: i18nWithDefaults.breadcrumbs.productLine.label,
                items: filtersDetails[PRODUCT_LINE_QUERY_KEY]
            });
        }
        if (filtersDetails[CAT_QUERY_KEY] && filtersDetails[CAT_QUERY_KEY].length) {
            crumbGroups.push({
                label: i18nWithDefaults.breadcrumbs.categories.label,
                items: filtersDetails[CAT_QUERY_KEY]
            });
        }
        if (attributesCrumbs.length) {
            // if only attributes are selected, the separator must be the default (caret) because the attributes will be
            // right next to the HOME link
            crumbGroups.push({
                label: i18nWithDefaults.breadcrumbs.attributes.label,
                separator: crumbGroups.length ? '|' : '',
                isDisplayAsString: true,
                items: attributesCrumbs
            });
        }

        return crumbGroups;
    }, [filtersDetails]);

    const containerProps = n('container').withTestId().props;

    return (
        <div {...containerProps}>
            <div {...n('breadCrumbs').props}>
                <BreadCrumbs
                    crumbs={breadcrumbs}
                    baseLinkUrl={homeUrl}
                    baseIconName="homeNav"
                    baseLinkText={i18nWithDefaults.home}
                    i18n={{
                        label: i18nWithDefaults.breadcrumbs.label
                    }}
                />
            </div>
            {desktopActionsContainer}
            {mobileActionsContainer}
            {sortingOptionsSidebar}
        </div>
    );
};

ProductListingControlsComponent.schema = () => ({
    name: 'ProductListingControls',
    title: 'uieditor.sfbComponent.productListingControls.title',
    iconName: 'reorder',
    editable: false,
    form: {},
    marketplaceData: {
        sorting: 'listingProducts.sorting',
        i18n: 'listingProducts.i18n',
        compareBaseUrl: 'listingProducts.compare.baseUrl',
        homeUrl: 'meta.baseUrl'
    }
});

const sortingOptionShape = PropTypes.shape({
    value: PropTypes.string,
    label: PropTypes.string,
    selected: PropTypes.bool,
    url: PropTypes.string
});

ProductListingControlsComponent.propTypes = {
    data: PropTypes.shape({
        sorting: PropTypes.shape({
            options: PropTypes.arrayOf(
                sortingOptionShape
            ),
            selected: sortingOptionShape
        }),
        compareBaseUrl: PropTypes.string,
        homeUrl: PropTypes.string,
        i18n: PropTypes.shape({
            allApplications: PropTypes.string,
            applySortOptions: PropTypes.string,
            backToTop: PropTypes.string,
            categories: PropTypes.string,
            compare: PropTypes.string,
            filters: PropTypes.string,
            grid: PropTypes.string,
            hideFilters: PropTypes.string,
            home: PropTypes.string,
            list: PropTypes.string,
            results: PropTypes.string,
            resultsFrom: PropTypes.string,
            showFilters: PropTypes.string,
            sort: PropTypes.string,
            sortBy: PropTypes.string,
            viewMode: PropTypes.string,
            viewMore: PropTypes.string,
            categoriesAriaLabel: PropTypes.string,
            attributesAriaLabel: PropTypes.string,
            productLineAriaLabel: PropTypes.string,
            breadcrumbs: PropTypes.shape({
                label: PropTypes.string,
                categories: PropTypes.shape({
                    label: PropTypes.string
                }),
                attributes: PropTypes.shape({
                    label: PropTypes.string
                }),
                productLine: PropTypes.shape({
                    label: PropTypes.string
                })
            }),
            closeSortingOptions: PropTypes.string
        })
    }),
    settings: PropTypes.shape({
        sortBy: PropTypes.string
    })
};

ProductListingControlsComponent.defaultProps = {
    data: {
        sorting: {},
        compareBaseUrl: '',
        homeUrl: '',
        i18n: {
            allApplications: 'All Applications',
            applySortOptions: 'Done',
            backToTop: 'Back to Top',
            categories: 'categories',
            compare: 'Compare',
            filters: 'Filters',
            grid: 'Grid',
            hideFilters: 'Hide Filters',
            home: 'Home',
            list: 'List',
            results: 'Results',
            resultsFrom: 'Results from',
            showFilters: 'Show Filters',
            sort: 'Sort',
            sortBy: 'Sorted By',
            viewMode: 'View Mode',
            viewMore: 'View More',
            breadcrumbs: {
                label: 'Breadcrumbs',
                categories: {
                    label: 'Categories'
                },
                attributes: {
                    label: 'Attributes'
                },
                productLine: {
                    label: 'Product Line'
                }
            },
            closeSortingOptions: 'Close sorting options'
        }
    },
    settings: {}
};

export default withListener(ProductListingControlsComponent);
