import * as React from 'react';
import * as _ from 'lodash';
import {CSSProperties} from 'react';
import {BasicConfig, BasicContainer, BasicContainerPropsInterface} from '../../render/core/Container/types';
import {IsBoolean, IsDefined, IsString} from 'class-validator';
import componentLoader from '../../render/util/componentLoader';
import {Checkbox, Tooltip} from '@native-ads/antd';
import {CheckboxProps} from '@native-ads/antd/lib/checkbox/Checkbox';
import {isExpression} from '../../render/util/vm';

export class CheckboxConfig extends BasicConfig {
    /**
     * Checkbox的数据模型Key
     */
    @IsDefined()
    name: string;

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

    /**
     * 文字
     * @public
     * @default ''
     */
    @IsString()
    text?: string;

    /**
     * 多组多选框
     */
    groups?: CheckBoxGroupItem[];

    /**
     * 添加全选框
     */
    groupSelectAll?: boolean;

    /**
     * 添加反选框
     */
    groupUnSelectAll?: boolean;

    /**
     * 初始是否选中
     */
    defaultValue?: boolean | string[];

    /**
     * unknown
     */
    @IsString()
    prefixCls?: string;

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

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

export class CheckBoxGroupItem {
    /**
     * 多选的Key
     */
    key: string;

    /**
     * 多选框文字
     */
    text: string;

    /**
     * 提示文字
     */
    tip?: string;
}

export class CheckboxPropsInterface extends BasicContainerPropsInterface {
    info: CheckboxConfig;

    /**
     * 当鼠标移入的回调
     */
    onMouseEnter?: (event: React.MouseEvent<HTMLInputElement>) => Object;

    /**
     * 当鼠标移开的回调
     */
    onMouseLeave?: (event: React.MouseEvent<HTMLInputElement>) => Object;

    /**
     * 当数据更新的回调
     */
    onChange?: (event: React.MouseEvent<HTMLInputElement>) => Object;
}

export default class AbstractCheckbox extends BasicContainer<CheckboxPropsInterface, {}> {
    private SELECT_ALL: string;
    constructor(props: CheckboxPropsInterface) {
        super(props);

        this.handleChange = this.handleChange.bind(this);
        this.handleGroupChange = this.handleGroupChange.bind(this);
        this.selectAll = this.selectAll.bind(this);

        this.SELECT_ALL = '_RCRE_SELECT_ALL_';
    }

    private initCheckbox(info: CheckboxConfig) {
        if (this.props.$setData && !_.isNil(info.defaultValue)) {
            const defaultValue = info.defaultValue;
            const $setData = this.props.$setData;

            if (_.isArray(defaultValue)) {
                let data = {};
                defaultValue.forEach(key => data[key] = true);
                $setData(info.name, data);
            } else if (typeof defaultValue === 'boolean') {
                $setData(info.name, defaultValue);
            }
        }
    }

    // 如果name是动态计算的，那么name属性在didMount是有可能不存在的
    componentWillMount() {
        let info = this.getPropsInfo(this.props.info);

        if (!info.name) {
            return;
        }

        this.initCheckbox(info);
    }

    // 当上一次的name为空，下一次的name有值，就说明这个是刚刚动态计算出来的值
    componentWillReceiveProps(nextProps: CheckboxPropsInterface) {
        let prevInfo = this.getPropsInfo(this.props.info, this.props);
        let nextInfo = this.getPropsInfo(nextProps.info, nextProps);

        if (!prevInfo.name && nextInfo.name) {
            this.initCheckbox(nextInfo);
        }
    }

    private handleChange(event: React.ChangeEvent<HTMLInputElement>) {
        let checked = event.target.checked;
        let info = this.getPropsInfo(this.props.info);
        if (this.props.$setData) {
            this.props.$setData(info.name, checked);
        }

        this.commonEventHandler('onChange', this.getExternalCallbackArgs([event], this.props.onChange));
    }

    private handleGroupChange(key: string) {
        return () => {
            if (!this.isUnderContainerEnv()) {
                console.error('checkbox group should be under container component control');
                return;
            }

            let info = this.getPropsInfo(this.props.info);
            let data = this.getValueFromDataStore(info.name) || {};
            data[key] = !data[key];

            if (_.every(info.groups, group => data[group.key])) {
                data[this.SELECT_ALL] = true;
            } else if (info.groupSelectAll) {
                data[this.SELECT_ALL] = false;
            }

            if (this.props.$setData) {
                this.props.$setData(info.name, data);
            }
        };
    }

    private selectAll(event: React.ChangeEvent<HTMLInputElement>) {
        if (!this.isUnderContainerEnv()) {
            return;
        }

        let info = this.getPropsInfo(this.props.info);
        let data = this.getValueFromDataStore(info.name) || {};
        let flag = !data[this.SELECT_ALL];
        data[this.SELECT_ALL] = flag;

        _.each(info.groups, (item) => {
            let key = item.key;
            data[key] = flag;
        });

        if (this.props.$setData) {
            this.props.$setData(info.name, data);
        }
        this.commonEventHandler('onSelectAll', {
            selected: flag
        });
    }

    private mapOptions(info: CheckboxConfig): CheckboxProps {
        return {
            prefixCls: info.prefixCls,
            className: info.className,
            defaultChecked: info.defaultChecked,
            style: info.style,
            disabled: info.disabled
        };
    }

    render() {
        let infoCopy = _.clone(this.props.info);
        let info = this.getPropsInfo(this.props.info, this.props);

        if (!info.name && !isExpression(infoCopy.name)) {
            return this.errorReport('name property is required for Checkbox Element', 'div');
        }

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

        let value = this.getValueFromDataStore(info.name);
        let commonProps = {
            onMouseEnter: (event: React.MouseEvent<HTMLInputElement>) => {
                this.commonEventHandler('onMouseEnter',
                    this.getExternalCallbackArgs([event], this.props.onMouseEnter));
            },
            onMouseLeave: (event: React.MouseEvent<HTMLInputElement>) => {
                this.commonEventHandler('onMouseLeave',
                    this.getExternalCallbackArgs([event], this.props.onMouseLeave));
            }
        };
        let checkboxProps = this.mapOptions(info);

        if (!_.isArray(info.groups)) {
            return React.createElement(Checkbox, {
                checked: value,
                onChange: this.handleChange,
                ...checkboxProps,
                ...commonProps
            }, info.text);
        }

        let groupCheckbox = info.groups.map(group => {
            let text = group.text;
            let key = group.key;
            let isChecked = !!value ? !!value[key] : false;

            let checkboxElement = React.createElement(Checkbox, {
                checked: isChecked,
                onChange: this.handleGroupChange(group.key),
                key: group.key,
                ...checkboxProps,
                ...commonProps
            }, text);

            if (group.tip) {
                return (
                    <Tooltip title={group.tip} key={key}>
                        {checkboxElement}
                    </Tooltip>
                );
            }

            return checkboxElement;
        });

        let selectAll;

        if (info.groupSelectAll) {
            let isChecked = false;

            if (!_.isNil(value) && value[this.SELECT_ALL]) {
                isChecked = true;
            }

            selectAll = React.createElement(Checkbox, {
                checked: isChecked,
                onChange: this.selectAll,
                ...checkboxProps,
                ...commonProps
            }, '全选');
        }

        return (
            <div>
                {selectAll}
                {groupCheckbox}
            </div>
        );
    }
}

componentLoader.addComponent('checkbox', AbstractCheckbox, CheckboxPropsInterface);
