import { Checkbox, Empty, Typography } from 'antd';
import { DataNode } from 'antd/lib/tree';
import React, { useContext, useEffect } from 'react';
import './nTree.less';
import { CaretDownOutlined, CaretRightOutlined, LoadingOutlined } from '@ant-design/icons';

interface ValueProps {
    label: string,
    value: string
}
interface NTreeReactContent {
    expandedKeys: React.Key[],
    setExpandedKeys: React.Dispatch<React.SetStateAction<React.Key[]>>
    selected: ValueProps[]
    setSelected: React.Dispatch<React.SetStateAction<ValueProps[]>>
    onSelect?: (selectedKeys: ValueProps[]) => void
}

export const DispatchNTree = React.createContext<NTreeReactContent | null>(null);

/**
 * 展开组件
 * @param param0 
 * @returns 
 */
const OnSwitcher: React.FC<{
    node: DataNode,
    elKey: string,
    loadData?: ((treeNode?: DataNode) => Promise<any>)
}> = ({ node, elKey, loadData }) => {

    /***********************************/
    const { expandedKeys, setExpandedKeys } = useContext(DispatchNTree)!;
    const { isLeaf } = node;
    const [loading, setLoading] = React.useState<boolean>(false);
    const [expanded, setExpanded] = React.useState<boolean>(false);
    /***********************************/

    return <div
        className='nTree-switcher'
        style={loading ? { cursor: 'progress' } : {}}
        onClick={(e) => {
            e.stopPropagation();
            e.preventDefault();
            if (loading || isLeaf) return;
            if (expandedKeys.includes(elKey)) {
                setExpandedKeys(expandedKeys.filter(item => item !== elKey))
            } else {
                if (expanded) {
                    setExpandedKeys([...expandedKeys, elKey])
                } else {
                    if (typeof loadData === 'function') {
                        setLoading(true);
                        loadData(node).then(() => {
                            setExpanded(true);
                            setExpandedKeys([...expandedKeys, elKey]);
                            setLoading(false);
                        })
                    } else {
                        setExpandedKeys([...expandedKeys, elKey]);
                        setExpanded(true);
                    }
                }
            }
        }}
    >{loading ? <LoadingOutlined /> : !isLeaf && (expandedKeys.includes(elKey) ? <CaretDownOutlined /> : <CaretRightOutlined />)}</div>
}

const renderTitle = (title: React.ReactNode | ((data: DataNode) => React.ReactNode), data: DataNode): React.ReactNode => {
    if (typeof title === 'function') {
        return (title as (data: DataNode) => React.ReactNode)(data);
    }
    return title;
};


interface NTreeItemProps {
    treeData?: DataNode[],
    loadData?: ((treeNode?: DataNode) => Promise<any>),
    fatherKey?: React.Key
}

/**
 * 自定义树表item
 * @returns 
 */
const NTreeItem: React.FC<NTreeItemProps> = ({ treeData, fatherKey, loadData }) => {

    /*************************/
    const { expandedKeys, selected, setSelected, onSelect } = useContext(DispatchNTree)!;
    /*************************/

    return <>
        {treeData ? treeData?.map((item: DataNode, index: number) => {
            const { title, key, children, checkable = true, disabled } = item;
            const elKey = fatherKey ? fatherKey + '-' + index + '-' + key : index + '-' + key;
            return <div key={elKey}>
                <div
                    className='nTree-item'
                    style={{ cursor: checkable === false ? 'default' : 'pointer' }}
                    onClick={(e) => {
                        e.stopPropagation();
                        e.preventDefault();
                        if (checkable) {
                            let newSelected = [...selected];
                            if (selected.findIndex(item2 => item2.value === key) !== -1) {
                                newSelected = newSelected.filter(item2 => item2.value !== key)
                            } else {
                                const item = { label: title as string, value: key as string }
                                newSelected = [...newSelected, item];
                            }
                            setSelected(newSelected)
                            onSelect?.(newSelected)
                        }
                    }}
                >
                    {/* 展开组件 */}
                    <OnSwitcher
                        node={item}
                        elKey={elKey}
                        loadData={loadData}
                    />
                    {checkable && <div className='nTree-checkbox'>
                        <Checkbox
                            checked={selected.findIndex(item => item.value === key) !== -1}
                            disabled={disabled}
                            onChange={(e) => {
                                e.stopPropagation();
                                e.preventDefault();
                                let newSelected = [...selected];
                                if (e.target.checked) {
                                    const item = { label: title as string, value: key as string }
                                    if (selected.findIndex(item2 => item2.value === key) !== -1) return;
                                    newSelected = [...newSelected, item];
                                } else {
                                    newSelected = newSelected.filter(item2 => item2.value !== key)
                                }
                                setSelected(newSelected)
                                onSelect?.(newSelected)
                            }}
                        ></Checkbox>
                    </div>}
                    <Typography.Text ellipsis>{renderTitle(title, item)}</Typography.Text>
                </div>
                {(children && expandedKeys.includes(elKey)) && <div className='nTree-children'>
                    <NTreeItem
                        treeData={children}
                        loadData={loadData}
                        fatherKey={elKey}
                    />
                </div>}
            </div>
        }) : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
    </>
};



/**
 * 自定义树表
 * @returns 
 */
const NTree: React.FC<{
    treeData?: DataNode[],
    loadData?: ((treeNode?: DataNode) => Promise<any>),
    fatherKey?: React.Key
    selectedKeys?: ValueProps[]
    onSelect?: (selectedKeys: ValueProps[]) => void
}> = ({ treeData, loadData, fatherKey, selectedKeys, onSelect }) => {

    /****************************************/
    const [expandedKeys, setExpandedKeys] = React.useState<React.Key[]>([]);
    const [selected, setSelected] = React.useState<ValueProps[]>(selectedKeys || []);
    /****************************************/

    return <div className='nTree'>
        <DispatchNTree.Provider value={{
            expandedKeys,
            setExpandedKeys,
            selected,
            setSelected,
            onSelect
        }}>
            <NTreeItem
                treeData={treeData}
                loadData={loadData}
                fatherKey={fatherKey}
            />
        </DispatchNTree.Provider>
    </div>
};

export default React.memo(NTree);