import * as React from 'react';
import * as _ from 'lodash';
import { CSSProperties } from 'react';
import { IsBoolean, IsString } from 'class-validator';
import { Icon, Input, Select} from '@native-ads/antd';
import { BasicConfig, BasicContainer, BasicContainerPropsInterface } from '../../render/core/Container/types';
import componentLoader from '../../render/util/componentLoader';
import { InputProps } from '@native-ads/antd/lib/input/Input';
import { SelectValue } from '@native-ads/antd/lib/select';

const InputGroup = Input.Group;
const SelectOption = Select.Option;

export class InputConfig extends BasicConfig {
    /**
     * Input的数据模型Key
     */
    name?: string;

    /**
     * Input 分组的配置
     */
    groups?: { name: string, label: string }[];

    /**
     * 默认值
     */
    defaultValue?: string | number;

    /**
     * 延迟一定时间再更新数据模型
     */
    debounce?: number;

    /**
     * 输入框类型
     * @public
     * @default text
     */
    inputType: 'text' | 'number' | 'password' | 'email' | 'search';

    /**
     * 输入框ID
     * @public
     * @default ''
     */
    @IsString()
    id: string;

    /**
     * 空间大小
     * @public
     * @default 'default'
     */
    @IsString()
    size: 'default' | 'large' | 'small';

    /**
     * 是否禁用
     * @public
     * @default false
     */
    @IsBoolean()
    disabled: boolean;

    /**
     * 带标签的 input，设置前置标签
     * @public
     * @default ''
     */
    @IsString()
    addonBefore: string;

    /**
     * 带标签的 input，设置后置标签
     * @public
     * @default ''
     */
    @IsString()
    addonAfter: string;

    /**
     * 没有输入展示的文字
     */
    @IsString()
    placeholder?: string;

    /**
     * 是否可读
     */
    @IsBoolean()
    readOnly?: boolean;

    /**
     * 前置图标
     */
    @IsString()
    prefix?: string;

    /**
     * 后置图标
     */
    @IsString()
    suffix?: string;

    /**
     * 文字检查
     */
    @IsBoolean()
    spellCheck?: boolean;

    /**
     * CSS class
     */
    @IsString()
    className?: string;

    /**
     * 内联CSS属性
     */
    style?: CSSProperties;

    /**
     * 自动聚焦
     */
    @IsBoolean()
    autoFocus?: boolean;
}

export class InputPropsInterface extends BasicContainerPropsInterface {
    info: InputConfig;
}

export interface InputStateInterface {
    /**
     * inputGroup的选中项
     */
    selectedName: string;

    /**
     * 保存一份数据, 提供debounce功能
     */
    value: string | number;
}

class AbstractInput extends BasicContainer<InputPropsInterface, InputStateInterface> {
    private debounce$SetData: (name: string, value: any) => void;

    constructor(props: InputPropsInterface) {
        super(props);

        this.handleChange = this.handleChange.bind(this);
        this.state = {
            selectedName: '',
            value: ''
        };

        const $setData = this.props.$setData;
        if ($setData && this.props.info.debounce) {
            this.debounce$SetData = _.debounce((name: string, value: any) => {
                $setData(name, value);
                this.commonEventHandler('onChange', {
                    event,
                    value: value
                });
            }, this.props.info.debounce);
        }
    }

    componentWillMount() {
        let info = this.getPropsInfo(this.props.info);
        if (this.props.$setData && info.name) {
            const $setData = this.props.$setData;
            const storeValue = this.getValueFromDataStore(info.name);
            let inputValue: string | number = '';

            if (typeof storeValue === 'string' && storeValue.length > 0) {
                inputValue = storeValue;
            } else if (!_.isNil(info.defaultValue)) {
                inputValue = info.defaultValue;
                $setData(info.name, info.defaultValue);
            }

            // debounse模式是由state自主控制，其他是由上层store控制
            if (info.debounce) {
                this.setState({
                    value: inputValue
                });
            }
        }
    }

    private handleChange(key: string) {
        return (event: React.ChangeEvent<HTMLInputElement>) => {
            let value: string | number = event.target.value;
            let info = this.getPropsInfo(this.props.info);

            if (info.inputType === 'number') {
                value = Number(value);
            }

            if (this.props.$setData) {
                if (info.debounce) {
                    this.setState({
                        value: value
                    }, () => {
                        this.debounce$SetData(key, value);
                    });
                } else {
                    this.props.$setData(key, value);
                    this.commonEventHandler('onChange', {
                        event,
                        value: value
                    });
                }
            }
        };
    }

    componentWillReceiveProps(nextProps: InputPropsInterface) {
        let nextInfo = this.getPropsInfo(nextProps.info, nextProps);
        let name = nextInfo.name;

        if (name && nextProps.$data) {
            let nextValue = this.getValueFromDataStore(name, nextProps.$data);
            let existValue = this.state.value;

            if (nextValue !== existValue) {
                this.setState({
                    value: nextValue
                });
            }
        }
    }

    private mapOptions(info: InputConfig): InputProps {
        return {
            placeholder: info.placeholder,
            type: info.inputType,
            id: info.id,
            name: info.name,
            size: info.size,
            disabled: info.disabled,
            readOnly: info.readOnly,
            addonBefore: info.addonBefore,
            addonAfter: info.addonAfter,
            autoComplete: 'off',
            prefix: info.prefix && <Icon type={info.prefix} />,
            suffix: info.suffix && <Icon type={info.suffix} />,
            spellCheck: info.spellCheck,
            autoFocus: info.autoFocus,
            className: info.className,
            style: info.style
        };
    }

    private createInputElement(info: InputConfig, value: string, key: string) {
        let inputProps = this.mapOptions(info);
        return React.createElement(Input, {
            value: value,
            onChange: this.handleChange(key),
            onPressEnter: (event: React.KeyboardEvent<HTMLInputElement>) => {
                this.commonEventHandler('onPressEnter', {
                    event: event,
                    value: value
                });
            },
            onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => {
                this.commonEventHandler('onKeyDown', {
                    event: event,
                    value: value
                });
            },
            onClick: (event: React.MouseEvent<HTMLInputElement>) => {
                this.commonEventHandler('onClick', {
                    event: event,
                    value: value
                });
            },
            onFocus: (event: React.MouseEvent<HTMLInputElement>) => {
                this.commonEventHandler('onFocus', {
                    event: event,
                    value: value
                });
            },
            onBlur: (event: React.MouseEvent<HTMLInputElement>) => {
                this.commonEventHandler('onBlur', {
                    event: event,
                    value: value
                });
            },
            ...inputProps
        });
    }

    render() {
        let info = this.getPropsInfo(this.props.info);

        if (!info.name && !info.groups) {
            return <div>name or groups property is required for Input Element</div>;
        }

        if (!this.isUnderContainerEnv()) {
            console.log('Input Element is out of RCRE control, please put it inside container component');
            return <Input />;
        }

        let childElement;

        if (info.groups instanceof Array && info.groups.length > 0) {
            let groups = info.groups;
            let selectOptions = groups.map(group => {
                return (
                    <SelectOption key={group.name} value={group.name}>
                        {group.label}
                    </SelectOption>
                );
            });

            const selectChangeCallback = (val: SelectValue) => {
                if (typeof val === 'string') {
                    let selectedValue = this.getValueFromDataStore(val);

                    this.setState({
                        selectedName: val,
                        value: selectedValue
                    });
                }
            };

            let name = this.state.selectedName || groups[0].name;
            let inputValue;

            if (info.debounce) {
                inputValue = this.state.value;
            } else {
                inputValue = this.getValueFromDataStore(name);
            }

            let inputElement = this.createInputElement(info, inputValue, name);

            childElement = (
                <InputGroup style={{display: 'flex'}} compact={true}>
                    <Select
                        defaultValue={info.groups[0].name}
                        onChange={selectChangeCallback}
                    >
                        {selectOptions}
                    </Select>
                    {inputElement}
                </InputGroup>
            );
        } else if (info.name) {
            let name = info.name;
            let value;

            if (info.debounce) {
                value = this.state.value;
            } else {
                value = this.getValueFromDataStore(name);
            }

            childElement = this.createInputElement(info, value, name);
        }

        return (
            <div className="ant-form-item-control" style={{ width: '100%' }}>
                {childElement}
            </div>
        );
    }
}

componentLoader.addComponent('input', AbstractInput, InputPropsInterface);

export default AbstractInput;
