import moment from 'moment';
import _, {isArray} from 'lodash';

/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
const AXIS_WIDTH = 40;
const COLOR = {
    NORMAL: '#1685FF',
    UPER: '#fa8c16',
    UPUPER: '#FF0000',
    // LOWER: '#13c2c2',
    // LOWLOWER: '#2f54eb',
    LOWER: '#fa8c16',
    LOWLOWER: '#FF0000',
    AVG: '#00B8B1',
};

const isMobile = () => {
    const userAgent = navigator.userAgent.toLowerCase();
    if (
        /ipad|iphone|midp|rv:1.2.3.4|ucweb|android|windows ce|windows mobile/.test(
            userAgent
        )
    ) {
        return true;
    }
    return false;
};
const returnRem = (num, base = 375) => num * (base / 375);
export const handlePx = (num, unit = '') => {
    const _isMobile = isMobile();
    const _base = document.body.clientWidth;
    let _num = _isMobile ? `${returnRem(num, _base)}` : `${num}`;
    return unit ? `${_num}${unit}` : Number(_num);
};
const PC_OPTION = {
    markPoint: {
        padding: [2, 12],
        lineHeight: 22,
        backgroundColor:
            window.globalConfig &&
            window.globalConfig &&
            window.globalConfig.variableTheme?.primaryColor
                ? window.globalConfig.variableTheme.primaryColor
                : '#0087F7',
        borderWidth: 1
    },
    fontSize: 16,
    fontColor: "#ffffff",
    dataZoomHeight: 28
}
const MOBILE_OPTION = {
    markPoint: {
        padding: [2, 6],
        lineHeight: 18,
        backgroundColor: 'rgba(255,255,255,0.6)',
        borderWidth: 0
    },
    fontSize: handlePx(12),
    fontColor: "#0087F7",
    dataZoomHeight: 20
}
const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
/**
 * 图表系列名称格式化
 *
 * @param {any} data
 * @param {boolean} contrast 是否为同期对比
 * @param {any} contrastOption 同期对比周期配置, day|month
 * @returns
 */
const nameFormatter = (data, contrast, contrastOption, nameWithSensor) => {
    const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data;
    let name = nameWithSensor ? `${equipmentName}-${sensorName}` : equipmentName;
    if (contrast) {
        const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, '');
        name = `${name}-${time}`;
    }
    return name;
};

/**
 * 图表系列数据格式化
 *
 * @param {any} data
 * @param {boolean} contrast 是否为同期对比
 * @param {any} contrastOption 同期对比周期配置, day|month
 * @returns 图表系列数据, [[DateTime, value]]
 */
const dataAccessor = (data, contrast, contrastOption) => {
    const dataModel = data?.dataModel ?? [];
    let _currentYear = moment().format('YYYY');
    const formatStr =
        contrastOption === 'day' ? `${_currentYear}-01-01 HH:mm:00` : `${_currentYear}-01-DD HH:mm:00`;
    return dataModel
        .filter((item) => item.sensorName !== '是否在线')
        .map((item) => {
            const time = contrast ? moment(item.pt).format(formatStr) : item.pt;
            return [moment(time).valueOf(), item.pv];
        });
};

/**
 * 面积图配置(目前默认曲线图)
 *
 * @param {any} data 数据项
 * @returns Null/areaStyle, 为null显示曲线图, 为areaStyle对象显示为面积图.
 */
const areaStyleFormatter = (data) => {
    const {sensorName} = data;
    return sensorName && sensorName.indexOf('流量') > -1 ? {} : null;
};

/**
 * 数据项中指标值最小最大值
 *
 * @param {any} data 数据项
 * @returns
 */
const minMax = (data) => {
    const {dataModel} = data;
    let min = Number.MAX_SAFE_INTEGER;
    let max = Number.MIN_SAFE_INTEGER;
    dataModel.forEach((item) => {
        min = Math.min(min, item.pv ?? 0);
        max = Math.max(max, item.pv ?? 0);
    });
    return [min, max];
};

const markLineItem = (name, value, color) => {
    return {
        name: name,
        yAxis: value,
        value: value,
        lineStyle: {
            color: color || '#000',
        },
        label: {
            position: 'insideEndTop',
            color: color || '#000',
            formatter: function () {
                return `${name}:${value}`;
            },
        },
    };
};

export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
    // 只有一个数据曲线时显示markline
    if (!dataItem || !schemes || dataSource.length !== 1) return {};
    const {deviceType, stationCode, sensorName, decimalPoint} = dataItem;
    const curSchemes = schemes.filter(
        (item) =>
            item.deviceCode === stationCode &&
            item.sensorName === sensorName &&
            item.valueType === '直接取值',
    );
    const data = [];
    curSchemes.forEach((scheme) => {
        const {hLimit, hhLimit, lLimit, llLimit} = scheme;
        lLimit !== null && lLimit !== void 0 && data.push(markLineItem('低限', lLimit, '#fa8c16'));
        hLimit !== null && hLimit !== void 0 && data.push(markLineItem('高限', hLimit, '#fa8c16'));
        llLimit !== null && llLimit !== void 0 && data.push(markLineItem('低低限', llLimit, '#FF0000'));
        hhLimit !== null && hhLimit !== void 0 && data.push(markLineItem('高高限', hhLimit, '#FF0000'));
    });
    data.push({
        name: '平均线',
        type: 'average',
        lineStyle: {
            color: '#00b8b1',
            type: 'solid',
        },
        label: {
            position: 'insideEndTop',
            color: '#00b8b1',
            formatter: function (param) {
                return `平均值:${param.value}`;
            },
        },
    });
    return {
        symbol: ['none', 'none'],
        data,
    };
};

export const minMaxMarkPoint = (dataItem, index, dataSource) => {
    const _isMobile = isMobile();
    // 只有一个数据曲线时显示markline
    if (!dataItem || dataSource.length !== 1) return {};
    const data = [];
    data.push({type: 'min', name: '最小: '});
    data.push({type: 'max', name: '最大: '});
    return {
        symbolSize: 1,
        symbolOffset: [0, '50%'],
        label: {
            formatter: '{b|{b} }{c|{c}}',
            ...currentOption["markPoint"],
            borderColor: '#ccc',
            borderRadius: 4,
            position: 'top',
            distance: 10,
            rich: {
                b: {
                    color: currentOption['fontColor'],
                },
                c: {
                    color: currentOption['fontColor'],
                    fontSize: currentOption["fontSize"],
                    fontWeight: 700,
                },
            },
        },
        data,
    };
};

/**
 * 坐标轴添加网格线配置
 *
 * @param {any} axis
 */
export const decorateAxisGridLine = (axis, showGrid) => {
    if (!axis) return;
    axis.minorTick = {
        lineStyle: {
            color: '#e2e2e2',
        },
        ...(axis.minorTick || {}),
        show: showGrid,
        splitNumber: 2,
    };
    axis.minorSplitLine = {
        lineStyle: {
            color: '#e2e2e2',
            type: 'dashed',
        },
        ...(axis.minorSplitLine || {}),
        show: showGrid,
    };
    axis.splitLine = {
        ...(axis.splitLine || {}),
        show: showGrid,
    };
};

/**
 * 坐标轴添加离线区间
 *
 * @param {any} dataItem
 */
export const offlineArea = (dataItem) => {
    if (!dataItem) return {};
    const {dataModel} = dataItem;
    let datas = new Array();
    let offlineData = [];
    let hasOffline = false;
    dataModel.forEach((item, index) => {
        if (!item.pv && !hasOffline) {
            offlineData = [
                {
                    name: '离线',
                    xAxis: new Date(item.pt),
                },
            ];
            hasOffline = true;
        } else if (item.pv && hasOffline) {
            offlineData.push({
                xAxis: new Date(item.pt),
            });
            datas.push(offlineData);
            offlineData = [];
            hasOffline = false;
        }
    });
    return {
        itemStyle: {
            color: '#eee',
        },
        data: datas,
    };
};

export const buildDefaultLegend = (option) => {
    const {title} = option;

    let paddingRight = 0;
    if (title && title.show) paddingRight = 80; // 给标题留够空间

    return {
        show: true,
        right: 20,
        top: 20,
        icon: 'rect',
        itemWidth: 14,
        itemHeight: 8,
        itemGap: 20,
        padding: [0, 0, 0, paddingRight],
        textStyle: {
            padding: [0, 0, 0, 4],
            color: '#2d2d2d',
        },
    };
};

const headTemplate = (param) => {
    if (!param) return '';
    const {name, axisValueLabel, axisType, axisValue} = param;
    const timeFormat = 'YYYY-MM-DD HH:mm:ss';
    const text =
        axisType === 'xAxis.time' ? moment(axisValue).format(timeFormat) : name || axisValueLabel;
    return `<div style="border-bottom: 1px solid #F0F0F0; color: #808080; margin-bottom:${handlePx(5, 'px')}; padding-bottom: ${handlePx(5, 'px')};">${text}</div>`;
};
const seriesTemplate = (param, unit) => {
    if (!param) return '';
    const {value, encode} = param;
    // const val = value[encode.y[0]];
    const _unit = unit || '';
    const color = '#008CFF';
    if (!isArray(value))
        return ` <div style="display: flex; align-items: center;">
            <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${param.seriesName}</span>
            <span style="display:inline-block;">：</span>
            <span style="color:${color};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value?.toFixed(3) ?? '-'}</span>
            <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
        </div>`;
    return param.componentSubType !== 'candlestick'
        ? `<div style="display: flex; align-items: center;">
              <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${param.seriesName}</span><span style="display:inline-block;">：</span>
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'}</span>
              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
            </div>`
        : `
            <div style="display: flex; align-items: center;">
              <span>首值</span><span style="display:inline-block;">：</span>
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'}</span>
              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
            </div>
            <div style="display: flex; align-items: center;">
              <span>尾值</span><span style="display:inline-block;">：</span>
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[2] ?? '-'}</span>
              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
            </div>
            <div style="display: flex; align-items: center;">
              <span>最小值</span><span style="display:inline-block;">：</span>
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[3] ?? '-'}</span>
              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
            </div>
            <div style="display: flex; align-items: center;">
              <span>最大值</span><span style="display:inline-block;">：</span>
              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[4] ?? '-'}</span>
              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
            </div>
          `;
};
const tooltipAccessor = (unit) => {
    return {
        formatter: function (params, ticket, callback) {
            let tooltipHeader = '';
            let tooltipContent = '';
            if (isArray(params)) {
                tooltipHeader = headTemplate(params[0]);
                params.forEach((param) => {
                    tooltipContent += seriesTemplate(param, unit?.[param?.seriesIndex]);
                });
            } else {
                tooltipHeader = headTemplate(params);
                tooltipContent += seriesTemplate(params, unit?.[params?.seriesIndex]);
            }
            return `
      <div>
        ${tooltipHeader}
        <div>${tooltipContent}</div>
      </div>
    `;
        },
    };
};
const returnXAxis = ({
                         dataSource,
                         contrast,
                         contrastOption,
                         nameWithSensor,
                         showMarkLine,
                         deviceAlarmSchemes,
                         showPoint,
                         restOption,
                         smooth
                     }) => {
    // 根据"指标名称"分类yAxis
    const yAxisInterator = (() => {
        const map = new Map();
        let current = -1;
        const get = (name) => (map.has(name) ? map.get(name) : map.set(name, ++current).get(name));
        return {get};
    })();
    let _offlineData = [];
    let series = dataSource
        .filter((item) => {
            if (item.sensorName === '是否在线') {
                _offlineData.push(item);
            }
            return item.sensorName !== '是否在线';
        })
        .map((item, index) => {
            const {sensorName, unit} = item;
            const name = nameFormatter(item, contrast, contrastOption, nameWithSensor);
            const data = dataAccessor(item, contrast, contrastOption);
            const type = 'line';
            const areaStyle = areaStyleFormatter(item);
            const yAxisIndex = yAxisInterator.get(sensorName);
            const markLine = showMarkLine
                ? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes)
                : {};
            const markPoint = showPoint ? minMaxMarkPoint(item, index, dataSource) : {};
            let markArea = null;
            // 需求变更：设备离线改用“是否在线”的数据，离线的状态标记的数据用该部分的数据。 2023年4月25日09:36:55
            let _offlineAreasData = _offlineData
                .map((item) => {
                    let _item = {...item};
                    _item.dataModel = item.dataModel.map((d) => {
                        let _d = {...d};
                        _d.pv = 0;
                        return _d;
                    });
                    return _item;
                })
                .find((offline) => offline.stationCode === item.stationCode);
            let offlineAreas = offlineArea(_offlineAreasData);
            if (offlineAreas.data?.length) {
                restOption.markArea = null;
                markArea = offlineAreas;
            }
            return {
                notMerge: true,
                name,
                type,
                data,
                areaStyle,
                yAxisIndex,
                smooth,
                unit,
                markLine,
                markPoint,
                markArea,
            };
        });
    // 由于series更新后，没有的数据曲线仍然停留在图表区上，导致图表可视区范围有问题
    const min = Math.min(
        ...series.map((item) => item.data?.[0]?.[0]).filter((item) => item !== undefined),
    );
    const max = Math.max(
        ...series
            .map((item) => item.data?.[item.data.length - 1]?.[0])
            .filter((item) => item !== undefined),
    );
    return {xAxis: {type: 'time', min, max}, series};
}
const handleDefault = (config, cusOption) => {
    const needUnit = _.get(config, 'needUnit', false);
    const curveCenter = _.get(config, 'curveCenter', false);
    const nameWithSensor = _.get(config, 'nameWithSensor', true);
    const showGridLine = _.get(config, 'showGridLine', true);
    const showMarkLine = _.get(config, 'showMarkLine', false);
    const showPoint = _.get(config, 'showPoint', false);
    const deviceAlarmSchemes = _.get(config, 'deviceAlarmSchemes', []);
    const chartType = _.get(config, 'chartType', null);
    // const justLine = _.get(config, 'justLine', false);
    const showBoxOption = _.get(config, 'showBoxOption', false);
    // 自定义属性
    const restOption = _.pick(cusOption, ['title', 'legend']);
    return {
        needUnit,
        curveCenter,
        nameWithSensor,
        showGridLine,
        showMarkLine,
        showPoint,
        deviceAlarmSchemes,
        chartType,
        showBoxOption,
        restOption
    }
}
const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
    // 一种指标一个y轴
    const yAxisMap = new Map();
    dataSource.forEach((item, index) => {
        const {sensorName, unit} = item;
        const key = sensorName;

        if (!yAxisMap.has(key)) {
            const i = yAxisMap.size;
            const axis = {
                type: 'value',
                name: needUnit ? unit : null,
                position: i % 2 === 0 ? 'left' : 'right',
                offset: Math.floor(i / 2) * AXIS_WIDTH,
                axisLabel: {
                    formatter: (value) => (value > 100000 ? `${value / 1000}k` : value),
                },
                axisLine: {
                    show: true,
                },
                nameTextStyle: {
                    align: i % 2 === 0 ? 'right' : 'left',
                },
                minorTick: {
                    lineStyle: {
                        color: '#e2e2e2',
                    },
                },
                minorSplitLine: {
                    lineStyle: {
                        color: '#e2e2e2',
                        type: 'dashed',
                    },
                },
            };
            yAxisMap.set(key, axis);
        }

        // 曲线居中
        if (curveCenter && item.dataModel && item.dataModel.length > 0) {
            const [min, max] = minMax(item);
            const axis = yAxisMap.get(key);
            axis.min = axis.min === void 0 ? min : Math.min(min, axis.min);
            axis.max = axis.max === void 0 ? max : Math.max(max, axis.max);
        }

        // 网格显示
        const axis = yAxisMap.get(key);
        decorateAxisGridLine(axis, showGridLine);
    });
    const yAxis = yAxisMap.size > 0 ? [...yAxisMap.values()] : {type: 'value'};

    // 根据y轴个数调整边距
    const leftNum = Math.ceil(yAxisMap.size / 2);
    const rightNum = Math.floor(yAxisMap.size / 2);
    return {leftNum, rightNum, yAxis};
}
const assignOptions = (restOption, xAxis, legendData) => {
    restOption.dataZoom = [
        {
            show: true,
            bottom: 10,
            start: 0,
            end: 100,
            height: currentOption['dataZoomHeight'],
            type: 'inside',
            zoomOnMouseWheel: true,
        },
        {
            show: true,
            bottom: 10,
            start: 0,
            end: 100,
            height: currentOption['dataZoomHeight'],
            type: 'slider',
            zoomOnMouseWheel: true,
        },
    ];
    xAxis.minInterval = 3600 * (1 * 1000);
    if (legendData) {
        restOption.legend = {...buildDefaultLegend({}), ...{data: legendData}};
    }
};
/**
 * 图表配置项生成
 *
 * @param {any} dataSource 数据源
 * @param {any} cusOption 自定义属性
 * @param {any} contrast 是否为同期对比
 * @param {any} contrastOption 同期对比周期配置, day|month
 * @param {any} smooth Ture/false, 曲线/折线
 * @param {any} config 额外配置信息
 */
const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth, config) => {
    const {
        needUnit,
        curveCenter,
        nameWithSensor,
        showGridLine,
        showMarkLine,
        showPoint,
        deviceAlarmSchemes,
        chartType,
        showBoxOption,
        restOption
    } = handleDefault(config, cusOption);
    const {leftNum, rightNum, yAxis} = handleYAxis({dataSource, needUnit, curveCenter, showGridLine});
    const grid = {
        top: needUnit ? 80 : 60,
        // top: 200,
        left: 10 + leftNum * AXIS_WIDTH,
        right: rightNum === 0 ? 20 : rightNum * AXIS_WIDTH,
        bottom: 60,
    };

    let {xAxis, series} = returnXAxis({
        dataSource,
        contrast,
        contrastOption,
        nameWithSensor,
        showMarkLine,
        deviceAlarmSchemes,
        showPoint,
        smooth,
        restOption
    });
    decorateAxisGridLine(xAxis, showGridLine);
    const tooltipTimeFormat = !contrast
        ? 'YYYY-MM-DD HH:mm:ss'
        : contrastOption === 'day'
            ? 'HH:mm'
            : 'DD HH:mm';
    let tooltip = {};
    // 增加箱线图的逻辑，单曲线才存在该逻辑
    if (chartType && showBoxOption) {
        if (chartType === 'boxChart') {
            const otherData =
                dataSource?.[0]?.dataModel.map((item) => {
                    const {firstPV, lastPV, maxPV, minPV, pt} = item;
                    return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV];
                }) || []; //当存在othersData的时候，只是单曲线
            xAxis = {type: 'time'};
            decorateAxisGridLine(xAxis, showGridLine);
            let unit = [];
            series = series.map((item) => {
                if (item.unit) unit.push(item.unit);
                item.areaStyle = null;
                return {...item, symbol: 'none'};
            });
            series.push({
                type: 'candlestick',
                name: '箱线图',
                symbol: 'none',
                data: otherData,
                itemStyle: {
                    color: '#FFA200',
                    color0: '#44CD00',
                    borderColor: '#FFA200',
                    borderColor0: '#44CD00',
                },
            });
            tooltip = tooltipAccessor(unit);
        }
        if (chartType === 'lineChart') {
            let _maxData = [];
            let _minData = [];
            let _currentYear = moment().format('YYYY');
            const formatStr =
                contrastOption === 'day'
                    ? `${_currentYear}-01-01 HH:mm:00`
                    : `${_currentYear}-01-DD HH:mm:00`; // 用来做同期对比，把日期拉到同一区间
            let _maxValues = [];
            dataSource?.[0]?.dataModel.forEach((item) => {
                const {firstPV, lastPV, maxPV, minPV, pt} = item;
                _maxValues.push(maxPV);
                let time = contrast ? moment(pt).format(formatStr) : pt;
                _maxData.push([moment(time).valueOf(), (maxPV - minPV).toFixed(2)]);
                _minData.push([moment(time).valueOf(), minPV]);
            }); //当存在othersData的时候，只是单曲线
            // xAxis = {type: 'category', data: series[0].data.map(item => moment(item[0]).format('YYYY-MM-DD HH:mm:ss'))};
            xAxis = {type: 'time'};
            decorateAxisGridLine(xAxis, showGridLine);
            let _unit = '';
            series = series.map((item) => {
                _unit = item.unit;
                item.areaStyle = null;
                return {...item, symbol: 'none'};
            });
            [[..._minData], [..._maxData]].forEach((item, index) => {
                series.push({
                    name: index === 0 ? '最小值' : '最大值',
                    type: 'line',
                    data: item,
                    lineStyle: {
                        opacity: 0,
                    },
                    ...(index !== 0
                        ? {
                            areaStyle: {
                                color: series?.[0]?.itemStyle?.color ?? '#65a0d1',
                                opacity: 0.2,
                            },
                        }
                        : {}),
                    stack: 'confidence-band',
                    symbol: 'none',
                });
            });
            tooltip = {
                formatter: (e) => {
                    return `<div>
                    ${headTemplate(e[0])}
                    <div>
                        <div style="display: flex; align-items: center;">
                              <span style="${isMobile() ? 'width: ' + handlePx(90, 'px') + ';overflow:hidden;text-overflow:ellipsis;white-space:nowrap' : ''}">${
                        e[0].seriesName
                    }</span><span style="display:inline-block;">：</span>
                              <span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
                        e[0]?.value?.[1] ?? '-'
                    }</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
                            </div>
                            <div style="display: flex; align-items: center;">
                              <span>最小值</span><span style="display:inline-block;">：</span>
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
                        e[1]?.value?.[1] ?? '-'
                    }</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
                            </div>
                            <div style="display: flex; align-items: center;">
                              <span>最大值</span><span style="display:inline-block;">：</span>
                              <span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
                        _maxValues[e[2].dataIndex] ?? '-'
                    }</span>
                              <span style="font-size: ${handlePx(12, 'px')};">${_unit}</span>
                            </div>
                        </div>
                    </div>`;
                },
            };
        }
    } else {
        tooltip = tooltipAccessor(series.map(item => item.unit));
    }
    tooltip.timeFormat = tooltipTimeFormat;
    let _legendData = series.filter(item => !['最大值', '最小值'].includes(item.name)).map(item => item.name);
    assignOptions(restOption, xAxis, _legendData);
    return {
        yAxis,
        grid,
        xAxis,
        series,
        tooltip,
        ...restOption,
    };
};

export default optionGenerator;
