import {CSSProperties, useEffect, useRef, useState} from 'react';
import * as echarts from 'echarts';
import {MapSeriesOption} from 'echarts';
import {useInterval, useSize} from 'ahooks';
import {
    ceil,
    isBoolean,
    isEmpty,
    maxBy,
    merge,
    uniqBy,
    size,
    gte,
    last,
    omit,
    map,
} from 'lodash';
import {Empty} from 'antd';
// @ts-ignore
import {getAMapLoader} from '@component/amap';
// @ts-ignore
import classes from './style.module.css';
import {LOADING_COLOR, MAP_COLORS} from './type';
import getFeatures from './getFeatures';
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<MapSeriesOption>;

type Grid = {
    name: string;
    adcode: string | number;
    [key: string]: string | number;
};

export interface MapProps {
    service: (params: any) => Promise<any>;
    color?: Array<string>;
    options?: ECOption;
    conditions?: any;
    breadcrumbClassName?: any;
    lastLevel?: string;
    goDown?: boolean;
    roam?: boolean;
    isCarousel?: boolean;
    isKeepGrid?: boolean;
    searchKey?: string;
    loadingType?: any;
    style?: CSSProperties;
    toolbarloop: 'top5' | 'all';
    onDownCallBack?: (n: Grid, c?: string | number, o?: object) => void;
}

export function ChartsMap({
                              service,
                              color = MAP_COLORS,
                              conditions,
                              options = {},
                              goDown = true,
                              lastLevel = 'district',
                              onDownCallBack,
                              breadcrumbClassName,
                              roam = false,
                              isCarousel = false,
                              style = {},
                              searchKey = '',
                              loadingType = {color: LOADING_COLOR},
                              isKeepGrid = false,
                              toolbarloop = 'top5'
                          }: MapProps) {
    const ele = useRef<any>(null);

    const echartsRef = useRef<any>(null);

    const echartsInstance = useRef<any>(null);

    const [empty, setEmpty] = useState<any>(undefined);

    const [breadcrumb, setBreadcrumb] = useState<Array<Grid>>([]);

    const [interval, setInterval] = useState<number | undefined>(undefined);

    const [time, setTime] = useState<number | undefined>(undefined);

    const count = useRef(0);

    useInterval(() => {
        function getDataLength() {
            const l = size(echartsInstance.current.getOption().series[0]?.data);
            if (toolbarloop === 'top5') {
                return gte(l, 5) ? 5 : l;
            } else {
                return l;
            }
        }

        echartsInstance.current.dispatchAction({
            type: 'hideTip',
        });

        echartsInstance.current.dispatchAction({
            type: 'downplay',
            seriesIndex: 0,
        });

        echartsInstance.current.dispatchAction({
            type: 'highlight',
            seriesIndex: 0,
            dataIndex: count.current % getDataLength(),
        });

        echartsInstance.current.dispatchAction({
            type: 'showTip',
            seriesIndex: 0,
            dataIndex: count.current % getDataLength(),
            position: () => {
            },
        });
        if (gte(count.current, toolbarloop === 'top5'?4:getDataLength()-1)) {
            count.current = 0;
        } else {
            count.current++;
        }
    }, interval);

    function clear(c?: boolean) {
        if (c) {
            count.current = 0;
        }

        setInterval(undefined);

        echartsInstance.current.dispatchAction({
            type: 'hideTip',
        });

        echartsInstance.current.dispatchAction({
            type: 'downplay',
            seriesIndex: 0,
        });
    }

    function handleBreadcrumb(grid: Grid, key: number) {
        setBreadcrumb(breadcrumb.slice(0, key + 1));

        echartsInstance.current?.resetOption(grid);

        onDownCallBack && onDownCallBack(grid);
    }

    const responsive = useSize(ele.current);

    useInterval(() => {
        echartsInstance.current?.resetOption(last(breadcrumb));
    }, time);

    useEffect(() => {
        echartsInstance.current?.resize();
    }, [JSON.stringify(responsive)]);

    useEffect(() => {
        const params = Object.assign(
            conditions,
            isKeepGrid ? omit(last(breadcrumb), 'name') : null,
        );

        if (!echartsRef.current) {
            echartsRef.current = echarts.init(ele.current);
        }
        echartsRef.current.showLoading(Object.assign({text: ''}, loadingType));

        (async function init() {
            const geoJson: any = await getFeatures(params.adcode);
            if (isEmpty(geoJson)) {
                echartsInstance.current?.clear();
                echartsRef.current.hideLoading();
                setEmpty(true);
                return;
            }
            const properties = geoJson?.currentFeature?.properties;
            const {adcode, name} = properties;
            echarts.registerMap(name, geoJson);
            if (!echartsInstance.current) {
                echartsInstance.current = initMap(
                    echartsRef.current,
                    {
                        color,
                        service,
                        goDown: goDown, // 是否下钻
                        lastLevel: lastLevel,
                        mapName: name, // 地图名
                        mapCode: adcode, // 地图code
                        callback: onDownCallBack,
                        setBreadcrumb,
                        roam,
                        searchKey,
                        loadingType,
                    },
                    options,
                );
            }
            if (!isEmpty(params) && echartsInstance.current) {
                try {
                    const _params = {
                        name, // 地图名
                        adcode: adcode, // 地图code
                        [searchKey]: params[searchKey], // 地图code
                    };
                    if (isKeepGrid) {
                        setBreadcrumb(
                            uniqBy([...breadcrumb, _params], (item) => String(item.adcode)),
                        );
                    } else {
                        setBreadcrumb([_params]);
                    }

                    function callback() {
                        setInterval(5000);
                        setTime(300000);
                    }

                    echartsInstance.current?.resetOption(
                        _params,
                        conditions,
                        isCarousel && callback,
                    );

                    if (isCarousel) {
                        clear(true);

                        echartsInstance.current.on('mouseover', () => {
                            clear();
                        });

                        echartsInstance.current.on('mouseout', () => {
                            setInterval(5000);
                        });
                    }
                } catch (e) {
                }
            }
        })();
    }, map(Object.values(conditions), String));
    return (
        <div
            style={{width: '100%', height: '100%', position: 'relative', ...style}}
        >
            {isBoolean(empty) && empty ? (
                <Empty
                    image={Empty.PRESENTED_IMAGE_SIMPLE}
                    description="该地区暂无地图轮廓"
                />
            ) : (
                <div ref={ele} style={{width: '100%', height: '100%'}}/>
            )}
            <div style={{position: 'absolute', top: 10, left: 30, zIndex: 10}}>
                <ul className={[classes.map_breadcrumb, breadcrumbClassName].join(' ')}>
                    {breadcrumb.map((item, key) => (
                        <li key={item.adcode} onClick={() => handleBreadcrumb(item, key)}>
                            {key > 0 && key < breadcrumb?.length && '-'}
                            {item?.name}
                        </li>
                    ))}
                </ul>
            </div>
        </div>
    );
}

export function initMap(chart: any, customOptions: any, options: any) {
    let cacheConditions = {};
    const option = merge(
        {
            backgroundColor: '#F5F5F5', // 画布背景色
            geo: {
                show: true,
                map: customOptions.mapName,
                layoutSize: '100%',
                zoom: 1.2,
                roam: customOptions.roam,
                scaleLimit: {
                    min: 1.2,
                    max: 10,
                },
                label: {
                    show: false,
                },
                itemStyle: {
                    borderColor: '#c5dff9',
                    borderWidth: 1,
                },
                emphasis: {
                    label: {
                        show: false,
                    },
                    itemStyle: {
                        //区域
                        areaColor: '#FFDF34',
                    },
                },
                select: {
                    label: {
                        show: false,
                    },
                    itemStyle: {
                        areaColor: '#BDBDBD',
                    },
                },
            },
            visualMap: {
                min: 0,
                bottom: 70,
                type: 'continuous',
                max: 100,
                text: ['高', '低'],
                top: 'bottom',
                itemWidth: 8,
                itemHeight: 320,
                calculable: true,
                realtime: false,
                show: true,
                inRange: {
                    color: customOptions.color,
                },
            },
            series: [
                {
                    type: 'map',
                    map: customOptions.mapName,
                    geoIndex: 0,
                    seriesIndex: 0,
                },
                {
                    type: 'effectScatter',
                    coordinateSystem: 'geo',
                    showEffectOn: 'render',
                    seriesIndex: 1,
                    roam: customOptions.roam,
                },
            ],
        },
        options,
    );
    chart.setOption(option);
    // 添加事件
    chart.on('click', async function (params: any) {
        const grid = params.data;
        if (!grid?.adcode) {
            return;
        }
        const {adcode, name} = grid;
        const geoJson: any = await getFeatures(adcode);
        if (isEmpty(geoJson)) {
            return;
        }
        const level = geoJson?.currentFeature?.properties?.level;

        if (level === customOptions.lastLevel) {
            return;
        }
        echarts.registerMap(name, geoJson);
        customOptions.setBreadcrumb((d: any) => {
            return uniqBy(
                [
                    ...d,
                    {
                        adcode: adcode,
                        name,
                        [customOptions.searchKey]: grid[customOptions.searchKey],
                    },
                ],
                'adcode',
            );
        });

        customOptions.callback && customOptions.callback(grid, option, chart);

        chart.resetOption({
            adcode: adcode,
            name,
            [customOptions.searchKey]: grid[customOptions.searchKey],
        });
    });

    chart.resetOption = async function (
        grid: Grid,
        conditions = cacheConditions,
        carousel: any,
    ) {
        try {
            const {name, adcode} = grid;
            cacheConditions = {...conditions};
            option.geo.map = name;
            chart.clear();
            chart.showLoading(Object.assign({text: ''}, customOptions.loadingType));
            const data = await customOptions.service({
                ...cacheConditions,
                adcode,
                [customOptions.searchKey]: grid[customOptions.searchKey],
            });
            chart.hideLoading();
            const maxValue: any = maxBy(data, 'value');
            option.visualMap.max = ceil(maxValue?.value) || 100;
            option.series[0].data = data;
            chart.setOption(option);
            carousel && carousel();
        } catch (e) {
            console.log(e, 'error');
        }
    };

    window.onresize = function () {
        chart?.resize();
    };
    return chart;
}
