import React from 'react';
import cx from 'classnames';
import { groupBy, flatMap } from 'lodash';
import OptionGroupFooter from './option-group-footer';
import OptionGroupHeader from './option-group-header';
import type { SearchOption } from '../search-result-option';

type SearchResultItem = SearchOption & {
    disabled?: boolean;
};

interface OptionComponentProps {
    option: SearchResultItem;
    optionIndex?: number;
    isFocused?: boolean;
    isSelected?: boolean;
    isDisabled?: boolean;
    className?: string;
    children?: React.ReactNode;
    [key: string]: unknown;
}

interface SearchResultsProps {
    focusedOption?: SearchResultItem;
    footerType?: string;
    optionClassName?: string;
    optionComponent: React.ComponentType<OptionComponentProps>;
    optionRenderer: (
        option: SearchResultItem,
        index: number
    ) => React.ReactNode;
    valueArray?: SearchResultItem[];
    valueKey: string;
    onOptionRef?: (ref: unknown, isFocused: boolean) => void;
    onFooterClick?: (value: { type: string; term?: string }) => void;
    options?: SearchResultItem[];
    idPrefix?: string;
    formatMessage?: (key: string) => string;
}

class SearchResults extends React.Component<SearchResultsProps> {
    static defaultProps = {
        options: [] as SearchResultItem[],
        formatMessage: (msg: string) => msg,
    };

    renderGroup = (group: SearchResultItem[]): JSX.Element[] => {
        const {
            focusedOption,
            optionClassName,
            optionComponent: Option,
            optionRenderer,
            valueArray,
            valueKey,
            onOptionRef,
            ...props
        } = this.props;

        return group.map((option, i) => {
            const isSelected = valueArray && valueArray.includes(option);
            const isFocused = option === focusedOption;
            const optionClass = cx(optionClassName, {
                'Select-option': true,
                'is-selected': isSelected,
                'is-focused': isFocused,
                'is-disabled': option.disabled,
            });

            return (
                <Option
                    {...props}
                    className={optionClass}
                    isDisabled={option.disabled}
                    isFocused={isFocused}
                    isSelected={isSelected}
                    key={`option-${option.type}-${String(
                        (option as Record<string, unknown>)[valueKey]
                    )}`}
                    option={option}
                    optionIndex={i}
                    ref={(ref: unknown) =>
                        onOptionRef && onOptionRef(ref, isFocused)
                    }
                >
                    {optionRenderer(option, i)}
                </Option>
            );
        });
    };

    render(): React.ReactNode {
        const {
            footerType,
            options,
            idPrefix,
            onFooterClick,
            formatMessage = (msg: string) => msg,
        } = this.props;

        return flatMap(
            groupBy(options, ({ type }) => type),
            (group, type) => {
                const [{ count = 0, term } = {} as SearchResultItem] = group;

                const headerMessage = formatMessage(`${type}Plural`);

                return [
                    <OptionGroupHeader
                        key={`header-${type}`}
                        message={headerMessage}
                    />,
                    ...this.renderGroup(group),
                    <OptionGroupFooter
                        groupCount={group.length}
                        totalCount={count}
                        footerType={footerType}
                        formatMessage={formatMessage}
                        key={`footer-${type}`}
                        type={type}
                        idPrefix={idPrefix}
                        onClick={() =>
                            onFooterClick && onFooterClick({ type, term })
                        }
                    />,
                ];
            }
        );
    }
}

export default SearchResults;
