import React, { Component, ReactNode } from 'react';
import moment from 'moment';
import { Checkbox, Icon } from 'antd';
import { is, List } from 'immutable';
import * as Tools from 'jad-tool';
import { cloneDeepWith } from 'lodash';
import { initOptions } from './utils';

type PropsKeyValue = {
    propPValue?: string,
    propValue: string,
    propName: string,
}
type OptionType = {
    value: string | number,
    name?: string,
    fullName?: (string)[],
    leaf: number, // 从 0 开始
    parent?: OptionType,
    children?: OptionType[],
    checked: boolean,
    indeterminate: boolean, // 我们这一版只考虑父子节点有关联的，即 indeterminate 为 fasle 的，且此时 showCheckedStrategy 最好设置为 SHOW_ALL
    [propName: string]: any,
};

type State = {
    options: OptionType[],
    selectedMap: OptionType[], // 所有选中的选项，用于抛出给上层
    clickedShowMap: { [stateName: string]: string | number },  // 点击了那些层级
};
type PanelProps = {
    options: OptionType[],
    value?: (string | number)[], // 一般纯面板 MultiCascaderPanel 传这个
    selectedMap?: OptionType[], // 一般 MultiCascaderSelect 可传这个 
    disabled?: boolean,
    keyValue?: PropsKeyValue,
    onChange?: (selectedMap: OptionType[]) => void,
    showCheckedStrategy?: 'SHOW_PARENT' | 'SHOW_CHILD' | 'SHOW_ALL', // 默认 SHOW_PARENT
    [propName: string]: any,
}
export class MultiCascaderPanel extends Component<PanelProps>{
    state: State;
    keyValue = {
        propPValue: 'pValue',
        propValue: 'value',
        propName: 'name',
    }
    constructor(props: PanelProps) {
        super(props);

        this.state = {
            options: props.options,
            selectedMap: [],
            clickedShowMap: {},
        }
        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,
            }
    }

    componentDidMount() {
        const { options, value, onChange } = this.props;
        if (!Tools.isEmptyArray(options) && value) {
            this.initOptions(value, options)
        }
    }

    /**
     * 这些场景需要有效触发 UNSAFE_componentWillReceiveProps 更新
     * 1. options
     * 2. value：
     * 3. 或者 selectedMap： 
     */
    UNSAFE_componentWillReceiveProps(nextProps) {
        const { options: nextPropsOptions, value, selectedMap } = nextProps;
        if (!is(List(nextPropsOptions), List(this.props.options)) || !is(List(value), List(this.props.value))) {
            this.initOptions(value, nextPropsOptions)
        }

        if (!is(List(nextPropsOptions), List(this.props.options)) || !is(List(selectedMap), List(this.props.selectedMap))) {
            this.setState({ selectedMap: selectedMap, options: nextPropsOptions })
        }
    }
    initOptions = (initValue: (string | number)[], initValueOptions: OptionType[]) => {
        const { selectedMap, options, optionsFlat, selectedValue } = initOptions.bind(this)(initValue, initValueOptions);
        const { onChange } = this.props;
        this.setState({
            selectedMap,
            options,
            optionsFlat,
            selectedValue,
        })
        onChange && onChange(selectedValue)
    }
    // 从低到高
    onCheckLow2High = (checked: boolean, record: OptionType) => {
        record['checked'] = checked;
        if (!record['parent']) {
            return
        } else {
            let parentChildren = record['parent']['children'];
            let flag = false;
            if (parentChildren.every(item => item.checked)) {
                flag = true;
            }
            if (!flag && parentChildren.some(item => item.checked || item.indeterminate)) {
                record['parent']['indeterminate'] = true;
            } else {
                record['parent']['indeterminate'] = false;
            }
            record['parent']['checked'] = flag;
            this.onCheckLow2High(flag, record['parent'])
        };
    }
    // 从高到低
    onCheckHigh2Low = (checked: boolean, record: OptionType) => {
        record['checked'] = checked;
        record['indeterminate'] = false; // 从高到低，indeterminate其实可直接设置为 checked 的值，不需要遍历children 的 checked
        if (record['children'] && record['children'].length) {
            record['children'].forEach(child => {
                this.onCheckHigh2Low(checked, child)
            })
        }
    }
    // 找到根结点 
    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();
        this.onShow(record);
        let { options } = this.state;
        let { onChange } = this.props;
        let newRecord = record;
        let newOptions = options;
        this.onCheckLow2High(checked, newRecord);
        this.onCheckHigh2Low(checked, newRecord);
        const collect = this.onCollect(newRecord, newOptions);

        this.setState({
            selectedMap: collect.selectedMap,
            options: collect.options
        }, () => {
            const { selectedMap } = this.state;
            onChange && onChange(selectedMap)
        })
    }
    // 打开子面板
    onShow = (record: OptionType) => {
        if(!record) return console.error('MultiCascaderPanel 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
            }
        })
    }
    renderColumns = () => {
        const { options, clickedShowMap } = this.state;
        const { disabled } = this.props;
        const { propValue, propName } = this.keyValue;

        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={active ? 'active' : ''}>
                            <Checkbox
                                checked={item['checked']}
                                indeterminate={item['indeterminate']}
                                onClick={e => this.onCheck(!item['checked'], item, e)}
                                disabled={!!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 columns;
    }
    render() {
        const { onMouseDown } = this.props;

        return (
            <div className={'jad-multi-cascader-panel-wrap'} onMouseDown={onMouseDown}>
                {this.renderColumns()}
            </div>
        );
    }
}
