/**
 * @tips:
 * 2024年12月10日 当能查到数据,但是数据都是null,则图表的分割线无法正常出来。
 *
 * */
import React, {memo, useEffect, useMemo, useRef, useState} from 'react';
import _, {cloneDeep} from 'lodash';
import {BasicChart} from '@wisdom-components/basicchart';
import PandaEmpty from '@wisdom-components/empty';
import optionGenerator from './utils';
import {getPointAddress, getPointAddressEntry, getSensorsRealName, getSensorType, getStatisticsInfo} from "./apis";
import moment from "moment";

const ChartTitle = ({prefixCls, title, unit}) => {
    const cls = `${prefixCls}-grid-item-title`;
    return (
        <div className={cls}>
            <span className={`${cls}_text`}>{title}</span>
            {unit && <span className={`${cls}_unit`}>(单位:{unit})</span>}
        </div>
    );
};
const ChartWidthRef = (props) => {
    const ref = useRef(null);
    const timerRef = useRef(null);
    const minMaxMarkPoint = (dataSource, chart, isInit) => {
        // 只有一个数据曲线时显示markline
        let isMultiple = props.isMultiple
        if (dataSource.length !== 1) return {};
        // 1. 找出最大、最小的值
        let pointArr = dataSource[0].dataModel;
        let valueArr = pointArr.map(item => item.pv);
        let maxValue = Math.max(...valueArr);
        let minValue = Math.min(...valueArr);
        // 2. 找出点的索引和实际的点
        let maxValueIndex = valueArr.findIndex(val => val === maxValue);
        let minValueIndex = valueArr.findIndex(val => val === minValue);
        let maxPoint = pointArr[maxValueIndex];
        let minPoint = pointArr[minValueIndex];
        if (!maxPoint || !minPoint) return {}
        // 3. 通过最大值、最小值,数组的首值尾值以及图表宽度来确认markpoint的位置
        let _opts = chart.getOption();
        let zoom = _opts.dataZoom[0];
        let startPoint = pointArr[0];
        let endPoint = pointArr[pointArr.length - 1];
        let timePeriod = isInit ? moment(endPoint.pt) - moment(startPoint.pt) : zoom.endValue - zoom.startValue;
        let chartWidth = chart.getWidth();
        // 需要考虑是否为0的情况
        // 4. 计算最大最小值的标签宽度
        let maxLength = 70 + String(maxValue).length * 5;
        let minLength = 70 + String(minValue).length * 5;
        // 5. 确定是否超出边界,确定超出边界是哪一边;
        // 用首尾时间评分
        let gapOfYAxisToEdge = 62;
        let startTime = isInit ? moment(startPoint.pt) : zoom.startValue;
        let maxPointPosition = ((chartWidth - gapOfYAxisToEdge) / timePeriod) * (moment(maxPoint.pt).valueOf() - startTime) + gapOfYAxisToEdge;
        let minPointPosition = ((chartWidth - gapOfYAxisToEdge) / timePeriod) * (moment(minPoint.pt).valueOf() - startTime) + gapOfYAxisToEdge;
        let maxTagLeft = maxPointPosition - maxLength / 2;
        let maxTagRight = maxPointPosition + maxLength / 2;
        let minTagLeft = minPointPosition - minLength / 2;
        let minTagRight = minPointPosition + minLength / 2;

        let maxOutEdge = false, minOutEdge = false;
        let maxOutSide = '', minOutSide = '';
        // 在实际使用中,我们认为不存在一个tag同时超出边界的情况。
        if (maxTagLeft < 0) {
            maxOutEdge = true;
            maxOutSide = 'left';
        }
        if (maxTagRight > chartWidth) {
            maxOutEdge = true;
            maxOutSide = 'right';
        }

        if (minTagLeft < 0) {
            minOutEdge = true;
            minOutSide = 'left';
        }
        if (minTagRight > chartWidth) {
            minOutEdge = true;
            minOutSide = 'right';
        }
        // 6. 确定使用的图形
        // 默认图形,居中
        let maxIconPath = `path://M1233.329493 195.75466633h-1112.064c-34.128213 0-61.781333 27.661653-61.781333 61.781334v473.658026c0 34.117973 27.65312 61.781333 61.781333 61.781334h472.80128l83.23072 144.155306 83.23072-144.155306h472.80128c34.128213 0 61.781333-27.66336 61.781334-61.781334V257.53600033c0-34.117973-27.65312-61.781333-61.781334-61.781334z`;
        let maxSymbolOffset = isMultiple ? [0, -14] : [0, -20];
        let _maxOffset = isMultiple ? -14 : -20;
        if (maxOutEdge) {
            if (maxOutSide === 'left') {
                // 左边界超出,使用朝右的图表
                maxSymbolOffset = ['50%', _maxOffset];
                maxIconPath = 'path://M48.677,6.151v10c0,1.7-1.3,3-3,3h-35.5l-6.7,4c0,0,0.2-5.3,0.2-7v-10c0-1.7,1.3-3,3-3h39C47.277,3.151,48.677,4.551,48.677,6.151z';
            } else if (maxOutSide === 'right') {
                // 右边界超出,使用朝左的图表
                maxSymbolOffset = ['-50%', _maxOffset];
                maxIconPath = 'path://M6.477,3.151h39c1.7,0,3,1.3,3,3v10c0,1.7,0.2,7,0.2,7l-6.7-4h-35.5c-1.7,0-3-1.3-3-3v-10C3.477,4.551,4.877,3.151,6.477,3.151z'
            }
        }
        // 默认图形
        let minIconPath = 'path://M131.999 849.579h1112.064c34.128 0 61.781-27.662 61.781-61.781v-473.658c0-34.118-27.653-61.781-61.781-61.781h-472.801l-83.231-144.155-83.231 144.155h-472.801c-34.128 0-61.781 27.663-61.781 61.781v473.658c0 34.118 27.653 61.781 61.781 61.781z';
        let minSymbolOffset = isMultiple ? [0, 14] : [0, 20];
        let _minOffset = isMultiple ? 14 : 20;
        if (minOutEdge) {
            if (minOutSide === 'left') {
                // 左边界超出,使用朝右的图表
                minSymbolOffset = ['50%', _minOffset];
                minIconPath = 'path://M45.677,23.151h-39c-1.7,0-3-1.3-3-3v-10c0-1.7-0.2-7-0.2-7l6.7,4h35.5c1.7,0,3,1.3,3,3v10C48.677,21.751,47.277,23.151,45.677,23.151z';
            } else if (minOutSide === 'right') {
                // 右边界超出,使用朝左的图表
                minSymbolOffset = ['-50%', _minOffset];
                minIconPath = 'path://M3.477,20.151v-10c0-1.7,1.3-3,3-3h35.5l6.7-4c0,0-0.2,5.3-0.2,7v10c0,1.7-1.3,3-3,3h-39C4.877,23.151,3.477,21.751,3.477,20.151z'
            }
        }

        const data = [
            {
                type: 'min',
                color: 'rgba(255,255,255,1)',
                name: '最小: ',
                symbolOffset: minSymbolOffset,
                symbol: minIconPath,
                symbolSize: (e) => {
                    let str = ![undefined, null].includes(e) ? String(e) : '';
                    let length = (isMultiple ? 40 : 60) + str.length * 6
                    return [length, isMultiple ? 20 : 32]
                },
                label: {
                    show: true,
                    color: '#fff',
                    formatter: '最小: {c}',
                    fontSize: isMultiple ? 12 : 14,
                    fontWeight: isMultiple ? 'normal' : 'bold',
                    verticalAlign: 'top',
                    offset: [0, -2]
                },
                itemStyle: {
                    // color: "#21c8c3",
                }
            },
            {
                type: 'max',
                name: '最大: ',
                position: [20, 200],
                symbol: maxIconPath,
                symbolOffset: maxSymbolOffset,
                symbolSize: (e) => {
                    let str = ![undefined, null].includes(e) ? String(e) : '';
                    let length = (isMultiple ? 40 : 60) + str.length * 6
                    return [length, isMultiple ? 20 : 32]
                },
                itemStyle: {
                    // color: "#1980ff",
                },
                label: {
                    show: true,
                    color: '#fff',
                    formatter: '最大: {c}',
                    fontSize: isMultiple ? 12 : 14,
                    fontWeight: isMultiple ? 'normal' : 'bold',
                    offset: [0, -2]
                },
            },
            {
                name: '',
                type: 'max',
                symbol: 'emptyCircle',
                label: {show: false},
                symbolSize: 6,
            },
            {
                name: '',
                type: 'min',
                symbol: 'emptyCircle',
                label: {show: false},
                symbolSize: 6,
            }
        ];
        return {
            symbol: 'circle',
            symbolSize: 20,
            animation: false,
            silent: true,
            label: {
                show: false,
            },
            data,
        };
    };

    const renderMarkPoint = (isInit) => {
        if (timerRef.current) clearTimeout(timerRef.current);
        const chart = ref.current?.getEchartsInstance?.();
        timerRef.current = setTimeout(() => {
            chart.setOption({
                series: {markPoint: minMaxMarkPoint(props.data.list, chart, isInit)}
            })
        }, 200)
    };
    // 将opt修改一下
    const cur_opt = useMemo(() => {
        let _option = cloneDeep(props.option);
        const chart = ref.current?.getEchartsInstance?.();
        if (_option?.series?.[0] && chart) {
            _option.series[0].markPoint = minMaxMarkPoint(props.data.list, chart, true);
            _option.series[0].markLine = {
                silent: false,
                symbol: 'none',
                data: [{
                    name: '平均线',
                    type: 'average',
                    lineStyle: {
                        color: '#00b8b1',
                        type: 'solid',
                    },
                    label: {
                        position: 'insideEndTop',
                        color: '#00b8b1',
                        formatter: function (param) {
                            return `平均值:${param.value}`;
                        },
                    },
                }]
            }
        }
        return {
            ..._option, grid: {
                ...props.option.grid,
                top: 45,
                bottom: 65,
            }
        }
    }, [props.option, ref, props.data.list]);

    useEffect(() => {
        ref.current?.resize?.();
        if (props.data.list?.length !== 1) return;

        const chart = ref.current?.getEchartsInstance?.();
        chart.setOption({
            series: {
                markLine: {
                    silent: false,
                    symbol: 'none',
                    data: [{
                        name: '平均线',
                        type: 'average',
                        lineStyle: {
                            color: '#00b8b1',
                            type: 'solid',
                        },
                        label: {
                            position: 'insideEndTop',
                            color: '#00b8b1',
                            formatter: function (param) {
                                return `平均值:${param.value}`;
                            },
                        },
                    }]
                }
            }
        })

        function dataZoomFn() {
            renderMarkPoint(false)
        }

        chart.on('legendselectchanged', renderMarkPoint);
        chart.on('datazoom', dataZoomFn);
        return () => {
            chart.off('legendselectchanged', renderMarkPoint);
            chart.off('datazoom', dataZoomFn);
        }
    }, [JSON.stringify(cur_opt)]);
    return <BasicChart
        ref={ref}
        {...props}
        option={cur_opt}
    />
}
const GridChart = memo((props) => {
    const {
        dataSource,
        contrast = false,
        contrastOption = 'day',
        smooth = true,
        curveCenter,
        allPointAddress,
        allSensorType,
        dateRange
    } = props;
    const {prefixCls} = props;
    const [gridData, setGridData] = useState([]);
    const [pointAddressEntryData, setPointAddressEntryData] = useState(null);
    const [sensorType, setSensorType] = useState(null);
    const [isInit, setIsInit] = useState(true);
    // 新增逻辑:需要区分出哪些是统计值

    /**
     *  @param {array} dataSource
     */
    const handleDataSource = async (dataSource) => {
        props.setLoading(true);
        // 1. 统计设备
        try {
            let _deviceTypes = [];
            let _deviceCodes = dataSource.reduce((final, cur) => {
                if (!final.includes(cur.stationCode) && !_deviceTypes.includes(cur.deviceType)) {
                    final.push(cur.stationCode);
                    _deviceTypes.push(cur.deviceType);
                }
                return final;
            }, []);
            // 2. 获取对应的版本id
            let _ids = [];
            let _idRequest = await getPointAddress({code: _deviceCodes.join(',')});
            _ids = _idRequest?.data ?? [];
            // 3. 获取对应的点表
            let _map = {};
            for await (let item of _ids) {
                let _index = _deviceCodes.findIndex(code => code === item.code);
                if (pointAddressEntryData && pointAddressEntryData[item.id]) {
                    _map[_deviceTypes[_index]] = pointAddressEntryData[item.id];
                } else {
                    let _entry = await getPointAddressEntry({versionId: item.id});
                    _map[_deviceTypes[_index]] = _entry?.data ?? [];
                    setPointAddressEntryData({...pointAddressEntryData, [item.id]: _entry?.data})
                }
            }
            // 4. 获取点类型
            let _sensorType = []
            if (sensorType) {
                _sensorType = sensorType;
            } else {
                _sensorType = (await getSensorType())?.data ?? [];
            }
            //5. 找出统计值,合并
            let _dataSource = cloneDeep(dataSource);
            let _nameListMap = {};
            let _indexArr = [];
            let _tempValue = {};
            let _finalData = {};
            _dataSource.forEach((item, index) => {
                let _sensorTypeId = _map[item.deviceType].find(sensor => sensor.name === item.sensorName)?.sensorTypeID || 0;
                let _type = _sensorType.find(sensor => sensor.id === _sensorTypeId)?.type ?? '';
                if (_type === '统计值') {
                    // 移除掉,并存储
                    _tempValue[`needToReplace_${item.stationCode}_${item.sensorName}`] = _dataSource.splice(index, 1, `needToReplace_${item.stationCode}_${item.sensorName}`)?.[0];
                    if (!_nameListMap[item.stationCode]) {
                        _nameListMap[item.stationCode] = {
                            code: item.stationCode,
                            deviceType: item.deviceType,
                            sensors: [item.sensorName]
                        }
                    } else {
                        _nameListMap[item.stationCode].sensors.push(item.sensorName)
                    }
                }
            })
            //6. 请求数据并替换数据。grid模式下,请求的时间是一致的。
            let baseParam = {
                pageIndex: 1,
                pageSize: 999,
                dateFrom: dateRange[0].dateFrom,
                dateTo: dateRange[0].dateTo,
            }
            let _arr = Object.values(_nameListMap)
            for await (let item of _arr) {
                let _params = {
                    ...baseParam,
                    accountName: item.deviceType,
                    deviceCode: item.code,
                    nameTypeList: item.sensors.map(sensor => ({
                        name: sensor,
                        type: 'Sub'
                    })),
                    /*                nameTypeList: ['今日用电量', '今日供水量'].map(sensor => ({
                                        name: sensor,
                                        type: 'Sub'
                                    })),*/
                    dateType: returnDateType(dateRange[0])
                };
                // 虚拟点需要查出实际点后,进行查找
                let _realSensors = {};
                let _realSensorsMap = {};
                // 统计类的如果是虚拟点,那么需要查出实际数据来源的点,查出映射关系
                (await getSensorsRealName(_params))?.data?.forEach(sensor => {
                    // name 虚拟点 staticName实际的点
                    _realSensors[sensor.staticName] = sensor.name;
                    _realSensorsMap[sensor.name] = sensor.staticName;
                });
                // 请求统计数据时,需要使用实际点去获取
                _params.nameTypeList.forEach(sensor => {
                    if (_realSensors[sensor.name]) {
                        sensor.name = _realSensors[sensor.name]
                    }
                });
                // 获取数据后,将原始数据中的dataModel这部分替换掉
                ((await getStatisticsInfo(_params))?.data?.list?.[0].dNameDataList ?? [])?.forEach(obj => {
                    if (_realSensorsMap[obj.dName]) {
                        let _v = _tempValue[`needToReplace_${item.code}_${_realSensorsMap[obj.dName]}`];
                        _v.dataModel = obj.nameDate.map(d => {
                            return {
                                pt: moment(d.time),
                                pv: d.value,
                                maxPV: d.value, minPV: d.value, firstPV: d.value, lastPV: d.value,
                            }
                        });
                        _finalData[`needToReplace_${item.code}_${_realSensorsMap[obj.dName]}`] = _v;
                    }
                });
                // 替换数据
                _dataSource.forEach((d, index) => {
                    if (_.isString(d) && d.includes('needToReplace') && _finalData[d]) {
                        _dataSource[index] = _finalData[d];
                    }
                })
                // 有不存在数据的,将原始数据替换回来
                _dataSource.forEach((d, index) => {
                    if (_.isString(d) && d.includes('needToReplace')) {
                        _dataSource[index] = dataSource[index];
                    }
                })
            }
            props.setLoading(false);
            return _dataSource
        } catch (e) {
            props.setLoading(false);
            return []
        }
    };
    const returnDateType = (date) => {
        let {dateFrom, dateTo} = date;
        let _duration = moment.duration(moment(dateTo) - moment(dateFrom), 'ms').days();
        if (_duration >= 7) return 'month';
        if (_duration >= 30) return 'year';
        return 'day';
    };
    useEffect(() => {
        async function handle() {
            let _data = isInit ? dataSource : (await handleDataSource(dataSource) ?? []);
            setIsInit(false);
            const grids = _data.reduce((pre, item, index) => {
                const {sensorName, deviceType} = item;
                const key = `${deviceType}_${sensorName}`; // 同设备类型同指标才在同一组
                let grid = pre.find((g) => g.key === key);
                if (!grid) {
                    const restProp = _.pick(item, ['equipmentName', 'sensorName', 'stationCode', 'unit']);
                    grid = {
                        key: key,
                        list: [],
                        ...restProp,
                    };
                    pre.push(grid);
                }
                grid.list.push(item);
                return pre;
            }, []);
            setGridData(grids);
        }

        handle();
    }, [dataSource])
    const options = useMemo(() => {
        let _options = gridData.map((item) => {
            const {key, list, equipmentName, sensorName, stationCode, unit} = item;
            let max = 300;
            // 5:左侧竖条的宽度;10:标题部分的左侧margin;
            // sensorName长度*单个宽度16.7;5:单位部分的左侧margin;
            // 91:单位部分的宽度(格式固定,宽度相对固定)
            let maxTitleLength = 5 + 10 + sensorName.length * 16.7 + 5 + 91;
            let finalLength = maxTitleLength > max ? max : maxTitleLength
            const cusOption = {
                title: {
                    show: true,
                    // text: `{prefix|}{t|${sensorName}}${unit ? '{suffix|(单位:' + unit + ')}' : ''}`,
                    text: ' ',
                    textStyle: {
                        width: finalLength,
                        overflow: 'truncate',
                    },
                },
                legend: {
                    // orient: 'vertical',
                    itemGap: 10,
                    padding: [0, 0, 0, finalLength],
                    textStyle: {
                        width: 120,
                        overflow: 'truncate',
                    },
                },
            };
            const option = optionGenerator(list, cusOption, null, contrastOption, smooth, {
                curveCenter,
                nameWithSensor: false,
                showGridLine: true,
                isMultiple: gridData.length > 1,
                chartType: 'lineChart'
            });
            // 无数据时,图表需要显示默认图形 2024年3月14日
            // 1. x轴
            let dataEmpty = [];
            option.series.forEach(item => {
                if (item.data.length === 0) {
                    dataEmpty.push(true)
                    item.data = [[moment(dataSource?.[0]?.dateFrom).valueOf(), null], [moment(dataSource?.[0]?.dateTo).valueOf(), null]]
                } else {
                    dataEmpty.push(false);
                }
            })
            // 2. y轴
            let allEmpty = dataEmpty.length ? dataEmpty.reduce((final, cur) => {
                if (!cur) final = false;
                return final
            }, true) : true;
            if (allEmpty) {
                option.yAxis.forEach(item => {
                    item.max = 100;
                    item.min = 0;
                });
                option.tooltip = false;
            }
            delete option.xAxis.max
            delete option.xAxis.min
            return {
                key,
                option: option,
            };
        });
        return _options;
    }, [gridData, smooth, curveCenter]);
    // const chartRef = dataSource.map((item, index) => useRef(null));
    // useEffect(() => {
    //     chartRef.forEach(item => {item?.current?.resize?.()})
    // }, [options]);
    return (
        <div className={`${prefixCls}-grid`}>
            {options.map((item, index) => {
                const {sensorName, unit} = gridData[index];
                const isEmpty =
                    !item.option.series.length ||
                    !item.option.series.find((e) => e.data && e.data.length > 0);
                return (
                    <div
                        key={item.key}
                        className={`${prefixCls}-grid-item`}
                        style={{
                            height: gridData.length === 1 ? '100%' : '',
                            width: gridData.length === 1 ? '100%' : '',
                        }}
                    >
                        <div className={`${prefixCls}-grid-item-wrap`}>
                            <ChartTitle prefixCls={prefixCls} title={sensorName} unit={unit}/>
                            {isEmpty ? (
                                isInit ? '' : <PandaEmpty/>
                            ) : (
                                <ChartWidthRef
                                    isMultiple={gridData.length > 1}
                                    data={gridData[index]}
                                    style={{width: '100%', height: '100%'}}
                                    option={item.option}
                                    notMerge
                                />
                            )}
                        </div>
                    </div>
                );
            })}
        </div>
    );
});

export default GridChart;