import * as React from 'react';
import {debounce as db, isEmpty, repeat} from 'lodash';
import OutsideClickHandler from 'react-outside-click-handler';
import classNames from 'classnames';
import FormComponent from '../FormComponent';
import {getId, nullToString, keyPressStringSTop,} from '../function';
import RenderHtml from '../renderFunction';
import PropsTypes from './types';
import Icon from '../Icon'
import * as numeral from 'numeral';

interface State extends PropsTypes {
    focus: boolean,
    ddMenuIsActive: boolean,
    uuid: string,
    dropdownValue: any,
    isActiveComponent: boolean,
    precision: number,
    useThousandSeparator: boolean,
    useNegative: boolean,
    cursorPosition?: { start?: number, end?: number },
    numeral: any
}

function getDropdownValue(dropdown: any) {
    return dropdown && dropdown.data.filter((item: any) => item[dropdown.displayValue] === dropdown.selected)[0]
}

function debounceTime(debounce:number) {
    return debounce ? debounce : 0
}

function numberValue({ useThousandSeparator, precision, useNegative }, val:any) {

    if (!useNegative) {
        val = (val + '').replace(/,/g, '');
        val = Math.abs(val)
    }

    if (useThousandSeparator && !precision) {
        val = numeral(val).format('0,0');
    }

    if (useThousandSeparator && precision) {
        val = numeral(val).format('0,0.' + repeat('0', precision))
    }

    if (!useThousandSeparator && precision) {
        val = numeral(val).format('0.' + repeat('0', precision))
    }

    const x = numeral(val);

    return {_input:'', _value: '', ...x}
}

export default class NumberInput extends FormComponent<PropsTypes, State> {

    public state: State;
    private input = React.createRef<HTMLInputElement>();
    // private caretStart: any;
    // private caretEnd: any;
    // private prevFormatMatch: any;
    private prevThousandsLength: number = 0;

    constructor(props: PropsTypes, {}) {
        super(props);

        const uuid = getId(props.id, this.defaultId);
        const {value, dropdown, debounce} = props;

        this.state = {
            focus: false,
            ddMenuIsActive: false,
            uuid: uuid,
            value: nullToString(value),
            dropdownValue: getDropdownValue(dropdown),
            autoFocus: false,
            isActiveComponent: false,
            precision: 0,
            useThousandSeparator: false,
            useNegative: false,
            numeral: null,
            ...props
        };

        this.debounce = db(this.debounce, debounceTime(debounce || 0));
    }

    async componentDidMount() {
        const {value} = this.state;
        const {autoFocus} = this.props;

        const formatted = numberValue(this.state, value);

        this.setState({
            size: this.size(),
            readOnly: this.readOnly(),
            cursorPosition: this.getCursorPosition(),
            numeral: formatted,
            value: formatted._input
        });

        if (autoFocus) {
            await this.focus();
        }

        this.validate(value);
        this.props.element && this.props.element(this.input);
    }

    focus() {
        const {value} = this.state;
        this.input.current && this.input.current.focus();

        if (value) {
            this.debounce()
        }

        this.setState({
            focus: true,
            isActiveComponent: true
        })
    }

    debounce() {
        this.dataOnReady()
    }

    componentWillReceiveProps(nextProps: any) {
        if (nextProps.value !== this.props.value) {

            const formatted = numberValue(this.state, nextProps.value);

            this.setState({
                cursorPosition: this.getCursorPosition(),
                numeral: formatted,
                value: formatted._input
            }, () => {
                this.updateCursorPosition();
                this.dataOnReady();
                this.validate(nextProps.value);
            });
        }

        if (nextProps.useThousandSeparator !== this.props.useThousandSeparator) {

            const updatedValue = numberValue({...this.state, useThousandSeparator: nextProps.useThousandSeparator }, this.state.numeral._input)._input;

            this.setState({
                useThousandSeparator: nextProps.useThousandSeparator,
                value: updatedValue
            })
        }

        if (nextProps.precision !== this.props.precision) {

            const updatedValue = numberValue({...this.state, precision: nextProps.precision }, this.state.numeral._input)._input;

            this.setState({
                precision: nextProps.precision,
                value: updatedValue
            })
        }

        if (nextProps.useNegative !== this.props.useNegative) {

            const updatedValue = numberValue({...this.state, useNegative: !!nextProps.useNegative }, this.state.numeral._input)._input;

            this.setState({
                useNegative: nextProps.useNegative,
                value: updatedValue
            })
        }
    }

    dataOnReady() {
        this.props.onChange && this.props.onChange(this.getInputData()['output'])
    }

    updateCursorPosition() {
        const { cursorPosition,  value, useThousandSeparator } = this.state;

        let gap: number = 0;

        if (useThousandSeparator && typeof value !== 'undefined' && value !== null) {
            const currentThousandsLength =  value.match(/,/g) ? value.split(',').length : 0;

            if (currentThousandsLength > this.prevThousandsLength) {
                gap = 1;
            } else if (currentThousandsLength < this.prevThousandsLength) {
                gap = -1;
            } else {
                gap = 0;
            }

            this.prevThousandsLength = currentThousandsLength;
        }


        if (this.input && this.input.current && cursorPosition && cursorPosition.start && cursorPosition.end) {
            this.input.current.selectionStart = cursorPosition.start + gap;
            this.input.current.selectionEnd = cursorPosition.end + gap;
        }
    }


    getCursorPosition(): { start: number, end: number } {
        const input = this.input.current;

        if (input && typeof input.selectionStart !== 'undefined' && typeof input.selectionEnd !== 'undefined' ) {
            return { start: input.selectionStart || 0, end: input.selectionEnd || 0 }
        }

        return { start: 0, end: 0 }
    }


    handleChange(e: any) {
        let inputValue: string = e.target.value;

        const formatted = numberValue(this.state, inputValue);

        this.setState({
            cursorPosition: this.getCursorPosition(),
            numeral: formatted,
            value: formatted._input
        }, () => {
            this.updateCursorPosition();
            this.dataOnReady();
            this.validate(inputValue);
        })

    }

    handleClick = () => {
        this.props.onClick && this.props.onClick()
    };

    handlePress = (e: any) => {
        keyPressStringSTop(e);
        
        const { maxLength } = this.props;

        const ml = (maxLength || 25);
        let currentVal = e.target.value.substr(0, e.target.value.indexOf('.') !== -1 ? e.target.value.indexOf('.') : e.target.value.length);
        currentVal = currentVal.replace(/,/g, '');

        if (currentVal.length > ml - 1 && e.target.selectionStart === e.target.selectionEnd) {
            e.preventDefault();
        }

        this.props.onKeyPress && this.props.onKeyPress(e)
    };

    getInputData() {
        const {value, dropdownValue} = this.state;
        const {dropdown} = this.props;

        let result: Object = {};
        let dropdownData: Object = {};
        let output: any = null;
        let str: any = numberValue(this.state, value && (value + '').replace(/(,)/g, ''));
        output = {
            value: str
        };

        if (dropdown && !isEmpty(dropdown)) {
            dropdownData = {
                dropdown: dropdownValue[dropdown.displayValue]
            };

            output = {
                value: str,
                ...dropdownData
            }
        }

        result = {
            value: str,
            output
        };

        return result
    }

    setDropdownValue(val: any) {
        this.setState({
            dropdownValue: val,
            ddMenuIsActive: false
        }, () => {
            this.dataOnReady()
        })
    }

    renderDropDown(): JSX.Element | null {
        const {ddMenuIsActive, dropdownValue} = this.state;
        const {dropdown, disabled} = this.props;



        if (dropdown && !isEmpty(dropdown)) {
            return <div className="krax-control">
                <div className={`krax-dropdown is-right ${ddMenuIsActive ? 'is-active' : ''}`}>
                    <div className="krax-dropdown-trigger">
                        <button
                            disabled={disabled}
                            onClick={() => this.setState({ddMenuIsActive: !ddMenuIsActive, focus: true})}
                            className={`krax-button ${this.size()}`} type="button" aria-haspopup="true"
                            aria-controls={`ddMenu-${this.defaultId}`}>
                            <span>{dropdownValue[dropdown.displayField]}</span>
                            <span className="krax-icon">
                                <Icon icon="fas fa-caret-down"/>
                            </span>
                        </button>
                    </div>
                    <div className="krax-dropdown-menu" id={`ddMenu-${this.defaultId}`} role="menu">
                        <div className="krax-dropdown-content">
                            {
                                dropdown.data.map((item, i) => {
                                    return <a key={i} className="krax-dropdown-item"
                                              onClick={() => this.setDropdownValue(item)}> {item[dropdown.displayField]} </a>
                                })
                            }
                        </div>
                    </div>
                </div>
            </div>
        }

        return null
    }

    outsideClick() {
        const {isActiveComponent} = this.state;

        if (isActiveComponent) {
            this.setState({
                focus: false,
                ddMenuIsActive: false,
                isActiveComponent: false,
            })
        }
    }

    public render(): JSX.Element {

        const {value, uuid } = this.state;
        const {label, icon, button, color, disabled, dropdown, maxLength, readOnly, infoText, loading, rounded, caption, noRadius, noBorder} = this.props;
        const uiClass = classNames(`krax-input ${this.size()} is-${color || 'default'}`, {
                'is-rounded': rounded,
                'is-danger': this.error(),
                'is-focused': this.error(),
                'no-border': noBorder,
                'no-radius': noRadius,
            }),
            containerClass = classNames(`krax-control is-expanded ${this.size()}`, {
                'has-icons-left': this.iconLeft(),
                'has-icons-right': this.iconRight(),
                'is-loading': loading,
                'is-data': value
            }),
            fieldClass = classNames(`krax-field`, {
                'has-addons': label || dropdown || button,
            }),
            captionClass = classNames('caption-text-ui', {
                'is-data': value
            });

        return (
            <OutsideClickHandler onOutsideClick={() => {
                this.outsideClick()
            }}>
                <div className="krax-field is-expanded" onClick={() => this.focus()}>
                    <div className={fieldClass}>
                        {RenderHtml.renderLabel(label, 'left', this.size())}
                        <div className={containerClass} key={1}>
                            <input
                                className={uiClass}
                                id={this.props.id || `input-${uuid}`}
                                name={this.props.name || `name-${uuid}`}
                                type="text"
                                value={value}
                                placeholder={caption}
                                autoComplete={'off'}
                                onChange={(val: any) => this.handleChange(val)}
                                onClick={() => this.handleClick()}
                                onKeyPress={(e) => {
                                    this.handlePress(e)
                                }}
                                spellCheck={false}
                                disabled={disabled}
                                readOnly={readOnly}
                                maxLength={maxLength}
                                ref={this.input}
                                tabIndex={0}
                                autoCorrect={'off'}
                            />
                            <div className={`${captionClass}`}>
                                {caption}
                            </div>
                            {RenderHtml.renderIcon(icon, 'left', this.size())}
                            {!loading ? RenderHtml.renderIcon(icon, 'right', this.size()) : null}
                        </div>
                        {RenderHtml.renderLabel(label, 'right', this.size())}
                        {this.renderDropDown()}

                        {button ? <div className="krax-control">
                            {button()}
                        </div> : null}

                    </div>
                    {RenderHtml.renderError(this.error())}
                    {isEmpty(this.error()) ? <div className="krax-help">{infoText}</div> : null}
                </div>

            </OutsideClickHandler>
        );
    }

}
