import React from 'react';
import { AsyncSelect as FRCAsyncSelect, ProductDisplayType, SearchIcon } from '@orchard/frontend-react-components';
import RSAsyncSelect from 'react-select/async';
import { components, MenuListComponentProps, OptionProps, IndicatorProps, GroupTypeBase } from 'react-select';
import { useApolloClient } from '@apollo/client';
import { formatProductName } from 'src/utils/formatters';
import { renderConfigurationIcon } from 'src/utils/configuration-icon';
import { formatMessage } from '@orchard/frontend-localization';
import { debounce } from 'lodash';
import {
    DEBOUNCE_TIME,
    PRODUCT_SEARCH_RESULTS_LIMIT,
    SEGMENT_CATEGORIES,
    SEGMENT_EVENTS,
} from 'src/constants';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import Segment from 'src/utils/segment';
import { getViewProductUrl } from 'src/urls/collaborator';
import { PRODUCTS_SEARCH_QUERY } from 'src/queries/all-products-search';
import {
    AllProductsSearch,
    AllProductsSearchVariables,
    AllProductsSearch_allProductsSearch_products as Product
} from 'src/queries/__definitions__/AllProductsSearch';
import messages from './i18n';

const AsyncSelect = FRCAsyncSelect as unknown as typeof RSAsyncSelect;

const CLASS_NAME = 'SearchBar';

const MenuList: React.FC<MenuListComponentProps<Product, false>> = (props) => (
    <components.MenuList { ...props } className={ `${CLASS_NAME}-menu-list` } />
);

const Option: React.FC<OptionProps<Product, false>> = (props) => (
    <components.Option { ...props } className={ `${CLASS_NAME}-option` } />
);

const GroupHeading: React.FC = () => (
    <div className={ `${CLASS_NAME}-option ${CLASS_NAME}-option-header` }>
        { formatMessage(messages.products) }
    </div>
);

const DropdownIndicator: React.FC<IndicatorProps<Product, false>> = (props) => (
    <components.DropdownIndicator { ...props }>
        <SearchIcon />
    </components.DropdownIndicator>
);

const IndicatorSeparator = () => null;

const SearchBar: React.FC<RouteComponentProps> = ({ history }) => {
    const client = useApolloClient();

    /* eslint-disable @typescript-eslint/indent */
    const loadOptions = (
        term: string,
        callback: (options: GroupTypeBase<Product>[]) => void
    ) => {
        client
            .query<AllProductsSearch, AllProductsSearchVariables>({
                query: PRODUCTS_SEARCH_QUERY,
                variables: {
                    term,
                    limit: PRODUCT_SEARCH_RESULTS_LIMIT,
                },
            })
            .then((result => {
                callback([{
                    options: result.data.allProductsSearch?.products || [],
                }]);
            }));
    };
    /* eslint-enable */

    const loadOptionsDebounced = debounce(loadOptions, DEBOUNCE_TIME);

    // We want the initial empty call to fire immediately but later calls to be debounced
    const loadOptionsDebouncedWhenNotEmpty = (
        ...args: Parameters<typeof loadOptions>
    ) => {
        const [term] = args;
        return term ? loadOptionsDebounced(...args) : loadOptions(...args);
    };

    return (
        <AsyncSelect
            className={ CLASS_NAME }
            loadOptions={ loadOptionsDebouncedWhenNotEmpty }
            defaultOptions
            cacheOptions={ false }
            onChange={ (product, action) => {
                if (action.action === 'select-option' && product) {
                    Segment.trackEvent(SEGMENT_EVENTS.SELECT_PRODUCT_CLICK, SEGMENT_CATEGORIES.HOMEPAGE);
                    history.push(getViewProductUrl(product.productId));
                }
            } }
            value={ null }
            onFocus={ () => Segment.trackEvent(SEGMENT_EVENTS.SEARCH_PRODUCT_CLICK, SEGMENT_CATEGORIES.HOMEPAGE) }
            placeholder={ formatMessage(messages.productSearch) }
            noOptionsMessage={ () => formatMessage(messages.productSearchNoResults) }
            maxMenuHeight={ 800 }
            menuShouldScrollIntoView={ false }
            components={ {
                MenuList,
                Option,
                GroupHeading,
                DropdownIndicator,
                IndicatorSeparator,
            } }
            formatOptionLabel={ (product) => (
                <>
                    <div className={ `${CLASS_NAME}-option-image-container` }>
                        <img
                            className={ `${CLASS_NAME}-option-image` }
                            src={ product.imageLocation! }
                            alt=""
                        />
                    </div>
                    <div className={ `${CLASS_NAME}-option-info-container` }>
                        <div className={ `${CLASS_NAME}-option-title` }>
                            { formatProductName(product.productName!, product.deliveredVersion) }
                        </div>
                        <div className={ `${CLASS_NAME}-option-artist` }>
                            { product.primaryArtists![0]!.artistName }
                        </div>
                    </div>
                    <div className={ `${CLASS_NAME}-product-info-container` }>
                        <div className={ `${CLASS_NAME}-option-icon-container` }>
                            { renderConfigurationIcon(product.productConfiguration) }
                            <ProductDisplayType
                                productConfiguration={ product.productConfiguration }
                                configuration={ product.configuration }
                                format={ product.format }
                                typeOfVideo={ product.typeOfVideo }
                            />
                        </div>
                        <div className={ `${CLASS_NAME}-option-upc` }>
                            { product.displayUpc }
                        </div>
                    </div>
                </>
            ) }
        />
    );
};

export default withRouter(SearchBar);
