import React, { Component } from 'react';
import { is, List, Map } from 'immutable';
import * as Tools from 'jad-tool';
import { Select, Empty, Checkbox, Icon, AutoComplete, Input } from 'antd';
import classNames from 'classnames';
import { throttle } from 'jad-tool';
import { initOptions, onCheckLow2High, onCheckHigh2Low, transform2FlatData, transform2TreeData } from './utils';

const { Option: AutoOption } = AutoComplete;
const { Search } = Input;

type OptionType = {
    value: string | number,
    name?: string,
    fullName?: (string)[],
    leaf: number, // 从 0 开始
    parent?: OptionType,
    children?: OptionType[],
    checked: boolean,
    indeterminate: boolean,
    [propName: string]: any,
};
type quickOptionsType = {
    value: string | number,
    name: string,
    options: OptionType[],
    index?: number,
    checked?: boolean,
    indeterminate?: boolean,
}
type State = {
    options: OptionType[],
    optionsFlat: OptionType[],
    quickOptions: quickOptionsType[],
    selectedValue: (string | number)[],
    selectedMap: OptionType[],
    clickedShowMap: { [stateName: string]: string | number },
    searchValue: string,
};

type PropsKeyValue = {
    propPValue: string,
    propValue: string,
    propName: string,
}
type SelectProps = {
    options?: OptionType[], // options 与 optionsFlat二选一
    optionsFlat?: OptionType[],
    value?: string[] | number[],
    disabled?: boolean,
    keyValue?: PropsKeyValue,
    showCheckedStrategy?: 'SHOW_PARENT' | 'SHOW_CHILD' | 'SHOW_ALL', // 默认 SHOW_PARENT
    showCheckedFormat?: 'ALL' | 'SELF', // 默认 ALL —— 放在 select 里面或者展示里面，去 state.selected Map中的propName
    showSearch?: boolean,
    searchProps?: { [propName: string]: any },
    showSearchFormat?: 'ALL' | 'SELF', // 默认 ALL —— 放在  search 面板 里面或者展示里面，去 state.selectedMap 中的propName
    onChange?: (values: string[] | number[]) => void,
    onCreateQuickOptions?: (options: OptionType[]) => quickOptionsType[],
}
export class MultiCascaderCard extends Component<SelectProps>{
    state: State;
    keyValue = {
        propPValue: 'pValue',
        propValue: 'value',
        propName: 'name',
    }
    multiCasacaderSelectRef = null;
    initState = {}
    constructor(props: SelectProps) {
        super(props);

        if (props['keyValue'])
            this.keyValue = {
                propPValue: props['keyValue']['propPValue'] || this.keyValue.propPValue,
                propValue: props['keyValue']['propValue'] || this.keyValue.propValue,
                propName: props['keyValue']['propName'] || this.keyValue.propName,
            }
        const initValueOption = props['options'] ? props['options'] : (props['optionsFlat'] ? transform2TreeData(props['optionsFlat'], this.keyValue) : []);
        const initValueOptionFlat = transform2FlatData(initValueOption, this.keyValue.propValue);
        this.state = {
            optionsFlat: initValueOptionFlat,
            options: initValueOption,
            quickOptions: null,
            selectedValue: props.value,
            selectedMap: [],
            clickedShowMap: {}, // 点击
            searchValue: undefined,
        }
        // 初始化 快捷选择数据源
        this.state.quickOptions = props.onCreateQuickOptions && this.state.optionsFlat ? props.onCreateQuickOptions(this.state.optionsFlat) : null;
        this.initState = { ...this.state }
    }
    componentDidMount() {
        const { value } = this.props;
        const { options } = this.state;
        if (!Tools.isEmptyArray(options) && value) {
            this.initOptions(value, options);
        }
    }

    UNSAFE_componentWillReceiveProps(nextProps) {
        const { options, optionsFlat, value } = nextProps;
        if (!Tools.isEmptyArray(options) && !is(List(options), List(this.props['options']))) {
            this.initOptions(value, options)
        } else if (!Tools.isEmptyArray(optionsFlat) && !is(List(optionsFlat), List(this.props['optionsFlat']))) {
            this.initOptions(value, transform2TreeData(optionsFlat, this.keyValue))
        } else if (!is(List(value), List(this.props.value)) && !is(List(value), List(this.state.selectedValue))) {
            if (!Tools.isEmptyArray(options))
                this.resetOptions(value, options)
            else if (!Tools.isEmptyArray(optionsFlat)) {
                this.resetOptions(value, transform2TreeData(optionsFlat, this.keyValue))
            }
        }
    }
    shouldComponentUpdate(nextProps, nextState) {
        if ((is(Map(nextProps), Map(this.props)) &&
            is(Map(nextState), Map(this.state)))) {
            return false
        }
        return true
    }
    initOptions = (initValue: (string | number)[], initValueOptions: OptionType[]) => {
        const { selectedMap, options, optionsFlat, selectedValue } = initOptions.bind(this)(initValue, initValueOptions);
        const quickOptions = this.createQuickOptions(optionsFlat);
        const { onChange } = this.props;
        this.setState({
            selectedMap,
            options,
            optionsFlat,
            selectedValue,
            quickOptions,
        })
        onChange && onChange(selectedValue)
    }
    resetOptions = (initValue: (string | number)[], initValueOptions: OptionType[]) => {
        const { selectedMap, options, optionsFlat, selectedValue } = initOptions.bind(this)(initValue, initValueOptions);
        this.setState({
            selectedMap,
            options,
            optionsFlat,
            selectedValue,
        }, () => {
            this.resetQuickStatus()
        })
    }
    /* ------------------------------下拉选择面板------------------------------ */
    // 找到根结点 
    findRoot = (record: OptionType) => {
        const { propValue } = this.keyValue;
        function walk(r) {
            if (r && r['parent'] && r['parent'][propValue]) {
                return walk(r['parent'])
            } else if (r && !r['parent']) {
                return r
            }
        }
        return walk(record)
    }
    /**
     * @param options 最新操作过后的 options
     * 1. getSelectedMap 获取选中的项，此处可以根据 showCheckedStrategy 定制回填方式
     * 2. 取值： 
     *   2.1 SHOW_ALL：所有选中的节点都展示
     *   2.2 SHOW_PARENT：当父节点的子节点都选中时，展示父节点(父节点：cheked：true ) —— 此为默认
     *   2.3 SHOW_CHILD：展示最底层节点
     */
    getSelectedMap = (options: OptionType[]) => {
        const { showCheckedStrategy } = this.props;
        return options.reduce((prev, current) => {
            if (showCheckedStrategy === 'SHOW_CHILD') {
                if (current.checked && !current.children) {
                    return prev.concat(current)
                } else if ((current.checked || current.indeterminate) && current.children) {
                    return prev.concat(this.getSelectedMap(current.children))
                }
            } else if (showCheckedStrategy === 'SHOW_ALL') {
                if (current.checked) {
                    if (current.children)
                        return prev.concat(current).concat(this.getSelectedMap(current.children))
                    else return prev.concat(current)
                } else if (current.indeterminate) {
                    //后代部分选中
                    return prev.concat(this.getSelectedMap(current.children))
                }
            } else {
                if (current.checked) {
                    return prev.concat(current)
                } else if (current.indeterminate && current.children) {
                    //后代部分选中
                    return prev.concat(this.getSelectedMap(current.children))
                }
            }
            return prev
        }, [])
    }
    // 获取 selectedMap 和 替换 原来的 options中新操作过的 rootRecord
    onCollect = (record: OptionType, options: OptionType[]) => {
        const { propValue } = this.keyValue;
        const rootRecord = this.findRoot(record);
        options.find((item, index) => {
            if (item[propValue] === rootRecord[propValue]) {
                options[index] = rootRecord; // 注意：这里修改了 options
                return true
            };
        })
        const selectedMap = this.getSelectedMap(options);
        return { selectedMap: selectedMap, options: options }
    }
    // 选中
    onCheck = (checked: boolean, record: OptionType, e?) => {
        window.event ? window.event.cancelBubble = true : e.stopPropagation();
        let { options, optionsFlat } = this.state;
        let { onChange } = this.props;
        const { propValue, propName } = this.keyValue;
        this.onShow(record); // 在checkbox 选择面板时才需要触发
        let newRecord = record;
        let newOptions = options;
        onCheckLow2High(checked, newRecord);
        onCheckHigh2Low(checked, newRecord);
        const collect = this.onCollect(newRecord, newOptions);
        const newValues = collect.selectedMap.map(item => item[propValue])
        const newOptionsFlat = collect.options ? transform2FlatData(collect.options, propName) : optionsFlat;

        this.setState({
            selectedMap: collect.selectedMap,
            options: collect.options,
            optionsFlat: newOptionsFlat,
            selectedValue: newValues,
        }, () => {
            // 更新了options、optionsFlat之后要更新
            const { searchValue } = this.state;
            const { showSearch } = this.props;
            showSearch && this.onSearch(searchValue);
            this.resetQuickStatus()
            onChange && onChange(newValues)
        })
    }
    // 打开子面板
    onShow = (record: OptionType) => {
        if(!record) return console.error('MultiCascaderCard onShow  receive OptionType is undefined')
        const { propValue } = this.keyValue;
        const newClickedShowMap = {};

        function walkParent(r: OptionType) {
            newClickedShowMap[r['leaf']] = r[propValue]
            if (r && r['parent'] && r['parent'][propValue]) {
                walkParent(r['parent'])
            }
        }
        walkParent(record)

        this.setState({
            clickedShowMap: {
                ...newClickedShowMap,
                [record['leaf'] + 1]: undefined
            }
        })
    }
    renderCheckedBox = () => {
        const { disabled } = this.props;
        const { propValue, propName } = this.keyValue;
        const { options, clickedShowMap } = this.state;

        const columns = [];
        function mapColumn(options, clickedShowMap) {
            if (!options || !options.length) return
            const column = (<ul className={'jad-cascader-panel-column'} key={options[0][propValue]}>
                {options.map((item, index) => {
                    const active = !!(clickedShowMap[item.leaf] && clickedShowMap[item['leaf']] === item[propValue])
                    if (item['children'] && item['children'].length && active) {
                        mapColumn.bind(this)(item['children'], clickedShowMap)
                    }
                    return (
                        <li key={item[propValue]} className={classNames(active ? 'active' : '')}>
                            <Checkbox
                                checked={item['checked']}
                                indeterminate={item['indeterminate']}
                                onClick={e => this.onCheck(!item['checked'], item, e)}
                                disabled={!!disabled || item['disabled']}
                            >{item[propName]}</Checkbox>
                            {
                                !Tools.isEmptyArray(item['children']) ?
                                    <span className={'icon-right-box'} onClick={(e) => this.onShow(item)}>
                                        <Icon className={'icon-right'} type="right" />
                                    </span>
                                    : null
                            }
                        </li>
                    )
                })
                }
            </ul>)
            columns.unshift(column);
        }
        mapColumn.bind(this)(options, clickedShowMap);
        return <div className={'jad-multi-cascader-panel-wrap'} onMouseDown={e => e.preventDefault()}>
            {columns}
        </div>
    }
    /* ------------------------------快捷选择展示框------------------------------ */
    createQuickOptions = (optionsFlat) => {
        var { onCreateQuickOptions } = this.props;
        if (!onCreateQuickOptions) return;
        const newQuickOptions = onCreateQuickOptions(optionsFlat);
        return newQuickOptions
    }
    resetQuickStatus = () => {
        const { propName, propValue } = this.keyValue;
        let { quickOptions, selectedMap } = this.state
        if (Tools.isEmptyArray(quickOptions)) return null;
        var selectMapBuff = transform2FlatData(selectedMap);
        let newQuickOptions = quickOptions.map(item => {
            let checked = item.options.every(e => selectMapBuff.some(f => f[propValue] === e[propValue]))
            let indeterminate = checked ? false : item.options.some(e => selectMapBuff.some(f => f[propValue] === e[propValue]));
            item['checked'] = checked;
            item['indeterminate'] = indeterminate;
            return item
        })
        this.setState({
            quickOptions: newQuickOptions
        })
    }
    onQuickSelect(checked, record, index) {
        let { options, quickOptions, optionsFlat } = this.state;
        let { onChange } = this.props;
        const { propName, propValue } = this.keyValue;
        let newRecord = record;
        let newOptions = options;
        let newSelectedMap = [];
        newRecord['indeterminate'] = false;
        newRecord['checked'] = checked;
        if (!newRecord['options'] || !newRecord['options'].length) return console.warn('onQuickSelect record has no options, check!')
        newRecord['options'].forEach((item) => {
            onCheckLow2High(checked, item);
            onCheckHigh2Low(checked, item);
            const { options, selectedMap } = this.onCollect(item, newOptions);
            newOptions = options;
            newSelectedMap = selectedMap;
        })
        const newValues = newSelectedMap.map(item => item[propValue])
        const newOptionsFlat = newOptions ? transform2FlatData(newOptions, propName) : optionsFlat;
        const newQuickOption = quickOptions.map(item => {
            if (item['value'] === newRecord['value']) {
                return newRecord
            } else {
                return item
            }
        })
        this.setState({
            selectedMap: newSelectedMap,
            options: newOptions,
            optionsFlat: newOptionsFlat,
            selectedValue: newValues,
            quickOptions: newQuickOption
        }, () => {
            // 更新了options、optionsFlat之后要更新
            const { searchValue } = this.state;
            const { showSearch } = this.props;
            showSearch && this.onSearch(searchValue)
            onChange && onChange(newValues)
        })
    }
    /*
       快捷选择的数据结构只有最底层级，直接children对应数据最下层children
   */
    renderQuickSelect() {
        let { quickOptions } = this.state;
        const { disabled } = this.props
        if (!quickOptions || Tools.isEmptyArray(quickOptions)) {
            return null;
        }
        return (
            <div className={'jad-multi-cascader-card-quick-select-wrap'}>
                <span>快捷选择: </span>
                <span className={'jad-multi-cascader-card-quick-selections'}>
                    {
                        quickOptions.map((item, index) => (
                            <Checkbox
                                onClick={e => this.onQuickSelect(!item['checked'], item, index)}
                                checked={item['checked']}
                                key={item.value}
                                disabled={disabled}
                                indeterminate={item['indeterminate']}
                            >{item.name}</Checkbox>
                        ))
                    }
                </span>
            </div>
        )
    }
    /* ------------------------------select 选中展示框------------------------------ */
    onClearAll = () => {
        const { onChange } = this.props;
        const { searchValue } = this.state;
        this.setState({ ...this.initState, selectedValue: [], searchValue });
        onChange && onChange([])
    }
    renderSelectedFormat = () => {
        const { showCheckedFormat, disabled } = this.props;
        const { selectedMap } = this.state;
        const { propName, propValue } = this.keyValue;
        if (showCheckedFormat === 'ALL') {
            return (selectedMap.map(item => {
                return <span key={item[propValue]} className={classNames('selections-item', disabled ? 'selections-disabled' : '')} >
                    {item && item['fullName'] && item['fullName'].join('/') || item[propName]}
                    {!disabled ? <Icon type="close" onClick={() => this.onCheck(false, item)} /> : <i> </i>}
                </span>
            }))
        } else {
            return (selectedMap.map(item => {
                return <span key={item[propValue]} className={classNames('selections-item', disabled ? 'selections-disabled' : '')} >
                    {item[propName]}
                    {!disabled ? <Icon type="close" onClick={() => this.onCheck(false, item)} /> : <i> </i>}
                </span>
            }))
        }
    }
    renderSelectedList = () => {
        const { selectedMap } = this.state;
        const { disabled } = this.props;
        if (!selectedMap || !selectedMap.length) return null;
        return (
            <div
                className={'jad-multi-cascader-card-selected-wrap'}
            >
                <div className={'selections-title'}>已选择:</div>
                <div className={'selections-container'}>
                    {
                        this.renderSelectedFormat()
                    }
                    <a className={disabled ? 'selections-disabled' : ''} onClick={() => this.onClearAll()}>清空</a>
                </div>
            </div>
        )
    }
    /* ------------------------------search 展示框------------------------------ */
    onSearchSelect = (val, option) => {
        if (val === 'no-data') return
        if (option && option.props && option.props.record) {
            this.onCheck(true, option.props.record || {})
            setTimeout(() => {
                const { searchValue } = this.state;
                this.onSearch(searchValue)
            })
        }
    }
    @throttle(300)
    onSearch(val) {
        this.setState({ searchValue: val });
    }

    renderSearchDataSource = () => {
        const { optionsFlat, searchValue } = this.state;
        const { showSearchFormat } = this.props;
        const { propName, propValue } = this.keyValue;
        const searchFlatOption = searchValue ? optionsFlat.filter(item => item[propName].includes(searchValue)) : []; // 内容清空时表示不查询

        if (Tools.isEmptyArray(searchFlatOption)) return [<AutoOption key='no-data'><Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /></AutoOption>]
        if (showSearchFormat === 'ALL') {
            const Node = (searchFlatOption.map(item => {
                const selected = !!item['checked'];
                return <AutoOption key={item[propValue]} {...{ [propName]: item[propName], record: item }} disabled={selected} className={classNames('jad-multi-cascader-card-search-drop-item', selected ? 'selected' : '')}>
                    <span>
                        {item['fullName'].map((f, index) => {
                            return (<span key={f} className={'text-ellipsis'}>
                                {index ? ` / ` : null}
                                {!selected ? f.split(searchValue).map((e, i) => {
                                    return <span key={e + i}>
                                        {i ? <span className={'search-drop-matched'}>{searchValue}</span> : null}
                                        {e}
                                    </span>
                                }) : f}
                            </span>)
                        })}
                    </span>
                    <Icon type="check" className={'icon-right'} />
                </AutoOption>
            }))
            return Node
        } else {
            const Node = (searchFlatOption.map(item => {
                const selected = !!item['checked'];
                return <AutoOption key={item[propValue]} {...{ [propName]: item[propName], record: item }} disabled={selected} className={classNames('jad-multi-cascader-card-search-drop-item', selected ? 'selected' : '')}>
                    <span>
                        {!selected ? item[propName].split(searchValue).map((e, i) => {
                            return <span key={e + i}>
                                {i ? <span className={'search-drop-matched'}>{searchValue}</span> : null}
                                {e}
                            </span>
                        }) : item[propName]}
                    </span>
                    <Icon type="check" className={'icon-right'} />
                </AutoOption>
            }))
            return Node
        }
    }
    renderSearch = () => {
        const { searchProps = {}, disabled } = this.props;
        const { propName, propValue } = this.keyValue;
        return <AutoComplete
            disabled={disabled}
            dataSource={this.renderSearchDataSource()}
            style={{ width: '100%', marginBottom: '8px' }}
            onSelect={this.onSearchSelect}
            onSearch={this.onSearch.bind(this)}
            placeholder="请输入"
            optionLabelProp={propName}
            defaultActiveFirstOption={false}
            {...searchProps}
        >
            <Search enterButton />
        </AutoComplete>
    }
    render() {
        const { showSearch, onCreateQuickOptions } = this.props;
        return <div className={'jad-multi-cascader-card'}>
            {this.renderSelectedList()}
            {onCreateQuickOptions && this.renderQuickSelect()}
            {showSearch && this.renderSearch()}
            {this.renderCheckedBox()}
        </div>
    }
}