import * as React from 'react';
import * as _ from 'lodash';
import { InputDropDownDrawer } from '../InputDropDownDrawer';

export namespace InputSuggest {
    export interface Props {
        /**
         * Custom input
         */
        customInput?: any;

        customInputProps?: any;
        /**
         * Custom component to show when no options
         */
        customEmptyStateMessage?: JSX.Element;
        /**
         * Message to show when no options
         */
        emptyStateMessage?: string;
        /**
         * List of options to be displayed initially (the state will manage them)
         */
        initialSuggestOptions: { toString: () => string }[];
        /**
         * Value to display inside the input
         */
        inputValue: string;
        /**
         * Function called when the user hits Enter and the dropdown is closed
         * This function is not executed when the Enter is used to select an option
         */
        onEnterKeyPressed?: () => void;
        /**
         * Function called when an option is selected, passing it
         */
        onSelectOption?: (event, option: { toString: () => string }) => void;
        /**
         * Function called when the value of the input changes
         */
        onValueChange: (value: string) => void;
        /**
         * Function called when rendering the options, being responsible of saying whether the value matches the option or not
         * By default function includes is used (case insensitive)
         */
        optionMatches?: (option: { toString: () => string }, currentValue: string) => boolean;
        /**
         * Placeholder of the input
         */
        placeholder?: string;
        /**
         * Indicates if the input is read only
         */
        readOnly?: boolean;
        /**
         * Indicates if the dropdown is displayed when there are no options matching the value
         * It must be true to display emptyStateMessage or customEmptyStateMessage
         */
        showDropDownOnEmpty?: boolean;
        /**
         * Prop to customize styles
         */
        styles?: Styles;

        id?: string;

        name?: string;
    }

    export interface State {
        /**
         * Indicates the current options to be displayed
         */
        suggestOptions: { toString: () => string }[];
        /**
         * Indicates whether the dropdown is shown or not
         */
        showDropDown: boolean;
    }

    export interface Styles {
        /**
         * Styles for the component that renders the input and the dropdown
         */
        InputDropDownDrawer?: InputDropDownDrawer.Styles;
    }
}

/**
 * Component that displays an input that drops down a list of suggestions that match with what the user is typing
 * 
 * @version 1.0.0
 * 
 */
export default class InputSuggest extends React.Component<InputSuggest.Props, InputSuggest.State> {
    constructor(props: InputSuggest.Props) {
        super(props);

        this.state = {
            showDropDown: false,
            suggestOptions: this.filterOptions(props.initialSuggestOptions, props.inputValue.toString())
        };
    }

    /**
     * Function called when rendering the options, being responsible of saying whether the value matches the option or not
     * It calls prop optionsMatches is passed
     * By default function includes is used (case insensitive)
     */
    filterOptions = (options: { toString: () => string }[], value: string) => (
        options.filter((option) => (
            this.props.optionMatches ?
                this.props.optionMatches(option, value)
                :
                option.toString().toUpperCase().includes(value.toUpperCase())
        ))
    );

    handleChangeValue = (event) => {
        const newValue = event.target.value;
        this.props.onValueChange(newValue);
        const newOptions = this.filterOptions(this.props.initialSuggestOptions, newValue.toString());
        this.setState((prevState: InputSuggest.State, props: InputSuggest.Props) => ({
            showDropDown: newOptions.length > 0 || Boolean(props.showDropDownOnEmpty),
            suggestOptions: newOptions
        }));
    };

    handleDropDownEvent = (event, show: boolean) => {
        this.setState((prevState: InputSuggest.State, props: InputSuggest.Props) => ({
            showDropDown: show && (prevState.suggestOptions.length > 0 || Boolean(props.showDropDownOnEmpty))
        }));
    };

    handleSelectOption = (event: any, option: string) => {
        const newOptions = this.filterOptions(this.props.initialSuggestOptions, option.toString());
        this.props.onValueChange(option);
        if (this.props.onSelectOption) {
            this.props.onSelectOption(event, option);
        }
        this.setState({
            showDropDown: false,
            suggestOptions: newOptions
        });
    };

    render() {
        const Styles: InputSuggest.Styles = _.merge({}, this.props.styles);
        return (
            <InputDropDownDrawer
                customInput={this.props.customInput}
                customInputProps={this.props.customInputProps}
                customEmptyStateMessage={this.props.customEmptyStateMessage}
                emptyStateMessage={this.props.emptyStateMessage}
                highlightedChunk={this.props.inputValue}
                inputValue={this.props.inputValue}
                onDropDownEvent={this.handleDropDownEvent}
                onEnterKeyPressed={this.props.onEnterKeyPressed}
                onSelectOption={this.handleSelectOption}
                onValueChange={this.handleChangeValue}
                placeholder={this.props.placeholder}
                readOnly={this.props.readOnly}
                showDropDown={this.state.showDropDown}
                suggestOptions={this.state.suggestOptions}
                styles={Styles.InputDropDownDrawer}
                id={this.props.id && this.props.id}
                name={this.props.name && this.props.name}

            />
        );
    }
}