window.neoChart = {
    charts: {},
    buffers: {}, // stores historical data: { timestamps: [], series: { label: [] }, formats: { label: "" } }
    selected: {}, // stores legend selected state per chart id
    observers: {}, // stores ResizeObserver per chart id
    _dpiListener: null, // global listener for zoom/dpi changes
    _lastUpdate: {}, // stores last update timestamp per chart id
    _interactionLocks: {}, // stores lock expiration per chart id

    _initDpiListener: function () {
        if (this._dpiListener) return;

        const self = this;
        const updateAll = () => {
            Object.keys(self.charts).forEach(id => {
                const oldChart = self.charts[id];
                if (oldChart && !oldChart.isDisposed()) {
                    try {
                        // 保存旧的 option
                        const oldOption = oldChart.getOption();
                        // 销毁旧实例
                        oldChart.dispose();

                        // 获取容器
                        const container = document.getElementById(id);
                        if (container) {
                            // 使用新 DPI 重新初始化
                            const newChart = echarts.init(container, null, {
                                renderer: 'canvas',
                                devicePixelRatio: window.devicePixelRatio,
                                useDirtyRect: true
                            });

                            // 关键修复：重建后必须立刻重置大小以适应容器新尺寸
                            newChart.resize();

                            // 恢复配置
                            newChart.setOption(oldOption);

                            // 重新绑定事件监听
                            self._bindChartEvents(id, newChart);

                            // 更新引用
                            self.charts[id] = newChart;
                            self.adjustGridTop(id);
                        }
                    } catch (e) {
                        console.error("Error re-initializing chart on zoom:", e);
                    }
                }
            });
            this._setupDpiChangeThread();
        };

        this._setupDpiChangeThread = () => {
            const mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
            mql.addEventListener('change', updateAll, { once: true });
            this._dpiListener = mql;
        };

        this._setupDpiChangeThread();
    },

    formatValue: function (val, formatStr) {
        if (val === null || val === undefined) return '-';
        if (typeof val !== 'number') return val;

        if (!formatStr) return val;

        let displayFormat = formatStr;
        if (formatStr.includes(':')) {
            displayFormat = formatStr.split(':')[1].trim();
        } else {
            // If it's a calculation only (e.g. "/10"), don't treat it as a display format
            if (formatStr.startsWith('+') || formatStr.startsWith('-') || formatStr.startsWith('*') || formatStr.startsWith('/')) {
                displayFormat = '';
            }
        }

        if (!displayFormat) return val;

        try {
            const fmt = displayFormat.toLowerCase();
            if (fmt.startsWith('f')) {
                const digits = parseInt(fmt.substring(1));
                return val.toFixed(isNaN(digits) ? 2 : digits);
            }
            if (fmt.startsWith('n')) {
                const digits = parseInt(fmt.substring(1));
                const d = isNaN(digits) ? 0 : digits;
                return val.toLocaleString(undefined, { minimumFractionDigits: d, maximumFractionDigits: d });
            }
            if (fmt.startsWith('x')) {
                const digits = parseInt(fmt.substring(1));
                return Math.round(val).toString(16).toUpperCase().padStart(isNaN(digits) ? 4 : digits, '0');
            }
            if (fmt.startsWith('p')) {
                const digits = parseInt(fmt.substring(1));
                return (val * 100).toFixed(isNaN(digits) ? 1 : digits) + '%';
            }
        } catch (e) { }

        return val;
    },

    renderDataView: function (id, opt) {
        var series = opt.series;
        var axisData = opt.xAxis[0].data;
        var selectedMap = this.selected[id];

        // Filter selected series
        var selectedIndices = [];
        for (var i = 0; i < series.length; i++) {
            var name = series[i].name;
            var isSelected = true;
            if (selectedMap && selectedMap.hasOwnProperty(name)) {
                isSelected = selectedMap[name];
            }
            if (isSelected) {
                selectedIndices.push(i);
            }
        }

        // Create scrollable container
        var container = document.createElement('div');
        container.style.cssText = 'width:100%;height:100%;overflow:auto;background:#ffffff;color:#333333;';
        container.className = 'dataview-container';

        // Add styles
        var style = document.createElement('style');
        style.innerHTML = `
            .dataview-table { width: 100%; border-collapse: separate; border-spacing: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; background: #fff; }
            .dataview-table thead th { 
                position: sticky; 
                top: 0; 
                background: #f3f3f3; 
                z-index: 20; 
                text-align: center; 
                color: #444; 
                font-weight: 600;
                padding: 4px 6px; 
                white-space: nowrap;
                border-bottom: 1px solid #e0e0e0; 
                border-right: 1px solid #e0e0e0; 
            }
            .dataview-table thead th:last-child { border-right: none; }
            .dataview-table td { 
                text-align: center; 
                white-space: nowrap; 
                padding: 2px 6px; 
                border-bottom: 1px solid #f0f0f0; 
                border-right: 1px solid #f0f0f0; 
            }
            .dataview-table td:last-child { border-right: none; }
            .dataview-table tbody tr:hover { background-color: #f5faff; }
            
            /* Light theme scrollbar */
            .dataview-container::-webkit-scrollbar { width: 10px; height: 10px; }
            .dataview-container::-webkit-scrollbar-track { background: #f1f1f1; }
            .dataview-container::-webkit-scrollbar-thumb { background: #ccc; border: 2px solid #f1f1f1; border-radius: 5px; }
            .dataview-container::-webkit-scrollbar-thumb:hover { background: #bbb; }
        `;
        container.appendChild(style);

        // Create table
        var table = document.createElement('table');
        table.className = 'dataview-table';
        container.appendChild(table);

        // Helper
        function getTheadTd(val) {
            return '<td>' + val + '</td>';
        }
        function getTh(val) {
            return '<th>' + val + '</th>';
        }

        // Headers
        var seriesNameStr = '';
        for (var i = 0; i < selectedIndices.length; i++) {
            seriesNameStr += getTh(series[selectedIndices[i]].name)
        }

        var thead = document.createElement('thead');
        thead.innerHTML = '<tr>'
            + getTh('No.')
            + getTh('Time')
            + seriesNameStr
            + '</tr>';
        table.appendChild(thead);

        var tbody = document.createElement('tbody');
        table.appendChild(tbody);

        // Incremental rendering
        if (selectedIndices.length > 0 && axisData && axisData.length > 0) {
            var dataLen = axisData.length;
            var renderedIndex = 0;
            var batchSize = 1000; // Adjusted batch size

            const renderBatch = () => {
                var end = Math.min(renderedIndex + batchSize, dataLen);
                var html = '';
                // Since dataView shows latest data typically, we might want to iterate normally.
                // The buffer is ordered chronologically.
                for (var i = renderedIndex; i < end; i++) {
                    var seriesDataValue = '';
                    for (var j = 0; j < selectedIndices.length; j++) {
                        var idx = selectedIndices[j];
                        var val = series[idx].data[i];
                        if (val === null || val === undefined) {
                            seriesDataValue += getTheadTd('-');
                        } else {
                            const fmt = this.buffers[id].formats[series[idx].name];
                            seriesDataValue += getTheadTd(this.formatValue(val, fmt));
                        }
                    }
                    html += '<tr>'
                        + getTheadTd(i + 1)
                        + getTheadTd(axisData[i])
                        + seriesDataValue
                        + '</tr>';
                }
                tbody.insertAdjacentHTML('beforeend', html);
                renderedIndex = end;
            }

            // Render first batch
            renderBatch();

            // Bind scroll event
            container.addEventListener('scroll', function () {
                if (this.scrollHeight - this.scrollTop - this.clientHeight < 50) {
                    if (renderedIndex < dataLen) {
                        renderBatch();
                    }
                }
            });
        }

        return container;
    },

    _bindChartEvents: function (id, chart) {
        const self = this;
        const container = chart.getDom();

        // 关键优化：当用户试图点击图表内容时，暂时锁定刷新 1 秒
        // 这确保了在点击图例、工具栏图标时，底层渲染不会突然干扰事件判定
        container.addEventListener('mousedown', () => {
            self._interactionLocks[id] = Date.now() + 1000;
        }, true);

        // Legend selection tracking
        chart.on('legendselectchanged', (e) => {
            if (this.selected[id]) {
                this.selected[id] = e.selected;
            }
            this.adjustGridTop(id);
        });

        // Fix: Adjust grid top and re-apply data after toolbox 'restore'
        chart.on('restore', () => {
            const buffer = self.buffers[id];
            if (buffer && buffer.timestamps.length > 0) {
                const seriesArr = Object.keys(buffer.series).map(name => {
                    return {
                        name: name,
                        type: 'line',
                        data: buffer.series[name],
                        showSymbol: false,
                        smooth: true,
                        lineStyle: { width: 1 },
                        itemStyle: { borderColor: '#1e1e1e', borderWidth: 1 }
                    };
                });
                chart.setOption({
                    xAxis: { data: buffer.timestamps },
                    series: seriesArr
                });
            }
            setTimeout(() => {
                self.adjustGridTop(id);
            }, 100);
        });
    },

    init: function (id) {
        this._initDpiListener();
        const container = document.getElementById(id);
        const self = this;
        const chart = echarts.init(container, null, {
            renderer: 'canvas',
            devicePixelRatio: window.devicePixelRatio, // 初始化时显式指定像素比
            useDirtyRect: true // 开启脏矩形渲染提升性能
        });

        this.charts[id] = chart;
        this.buffers[id] = {
            timestamps: [],
            series: {},
            formats: {}
        };
        this.selected[id] = {};

        const option = {
            backgroundColor: '#1e1e1e',
            animation: false,
            tooltip: {
                trigger: 'axis',
                appendToBody: true,
                confine: true,
                backgroundColor: '#252526',
                borderColor: '#3e3e42',
                textStyle: { color: '#cccccc', fontSize: 12, fontFamily: 'JetBrains Mono' },
                axisPointer: { type: 'cross', label: { backgroundColor: '#3e3e42' } },
                formatter: function (params) {
                    if (!params || params.length === 0) return '';
                    let res = `<div style="padding:2px 4px;"><div style="color:#858585;margin-bottom:4px;font-size:11px;">${params[0].axisValue}</div>`;
                    params.forEach(item => {
                        const fmt = self.buffers[id].formats[item.seriesName];
                        const val = self.formatValue(item.value, fmt);
                        res += `
                            <div style="display:flex;justify-content:space-between;gap:12px;align-items:center;min-width:100px;">
                                <div style="display:flex;align-items:center;gap:6px;">
                                    <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background-color:${item.color};"></span>
                                    <span style="color:#aaa;">${item.seriesName}</span>
                                </div>
                                <span style="color:#eee;font-weight:bold;">${val}</span>
                            </div>`;
                    });
                    res += '</div>';
                    return res;
                }
            },
            legend: {
                show: true,
                top: 25,
                left: 10,
                right: 10,
                textStyle: { color: '#858585', fontSize: 11, fontFamily: 'JetBrains Mono' },
                itemWidth: 10,
                itemHeight: 10,
                selectedMode: true
            },
            toolbox: {
                show: true,
                itemSize: 12, // 图标尺寸由默认 15 调小至 12，更加精致
                feature: {
                    dataView: {
                        show: true,
                        readOnly: false,
                        title: '数据视图',
                        optionToContent: (opt) => {
                            return this.renderDataView(id, opt);
                        }
                    },
                    restore: { show: true, title: '还原' },
                    saveAsImage: { show: true, title: '保存图片' }
                },
                iconStyle: {
                    borderColor: '#858585'
                },
                emphasis: {
                    iconStyle: {
                        borderColor: '#cccccc'
                    }
                },
                right: 10,
                top: 5
            },
            grid: {
                top: 60,
                left: 55,
                right: 20,
                bottom: 40,
                containLabel: true
            },
            dataZoom: [
                { type: 'inside', xAxisIndex: 0, filterMode: 'none' },
                { type: 'inside', yAxisIndex: 0, filterMode: 'none' },
                {
                    type: 'slider',
                    xAxisIndex: 0,
                    bottom: 5,
                    height: 12,
                    borderColor: 'transparent',
                    backgroundColor: 'transparent',
                    fillerColor: 'rgba(133, 133, 133, 0.15)',
                    handleStyle: { color: '#858585' },
                    textStyle: { show: false },
                    brushSelect: false
                },
                {
                    type: 'slider',
                    yAxisIndex: 0,
                    left: 10,
                    width: 12,
                    borderColor: 'transparent',
                    backgroundColor: 'transparent',
                    fillerColor: 'rgba(133, 133, 133, 0.15)',
                    handleStyle: { color: '#858585' },
                    textStyle: { show: false },
                    brushSelect: false
                }
            ],
            xAxis: {
                type: 'category',
                data: [],
                axisLine: { lineStyle: { color: '#3e3e42' } },
                axisLabel: { color: '#858585', fontSize: 10, fontFamily: 'JetBrains Mono' },
                splitLine: { show: true, lineStyle: { color: '#2d2d2d' } }
            },
            yAxis: {
                type: 'value',
                axisLine: { lineStyle: { color: '#3e3e42' } },
                axisLabel: { color: '#858585', fontSize: 10, fontFamily: 'JetBrains Mono' },
                splitLine: { show: true, lineStyle: { color: '#2d2d2d' } },
                scale: true
            },
            series: []
        };

        chart.setOption(option);
        this.adjustGridTop(id);

        const resizeObserver = new ResizeObserver(() => {
            if (this.charts[id]) {
                requestAnimationFrame(() => {
                    this.charts[id].resize();
                    this.adjustGridTop(id);
                });
            }
        });
        // Observe parent element to catch flex container resizes more reliably
        if (container.parentElement) {
            resizeObserver.observe(container.parentElement);
        } else {
            resizeObserver.observe(container);
        }
        this.observers[id] = resizeObserver;

        this._bindChartEvents(id, chart);
    },

    update: function (id, dataMap, formatsMap, maxCount) {
        const chart = this.charts[id];
        const buffer = this.buffers[id];
        if (!chart || !buffer || chart.isDisposed()) return;

        // --- 第一阶段：全量数据存储 (Data Integrity) ---
        // 无论渲染频率如何，必须把每一个采样点存入缓存
        const nowObj = new Date();
        const timeStr = nowObj.getHours().toString().padStart(2, '0') + ':' +
            nowObj.getMinutes().toString().padStart(2, '0') + ':' +
            nowObj.getSeconds().toString().padStart(2, '0');

        buffer.timestamps.push(timeStr);
        const currentSeriesNames = Object.keys(buffer.series);
        const incomingNames = Object.keys(dataMap);

        if (formatsMap) {
            Object.assign(buffer.formats, formatsMap);
        }

        incomingNames.forEach(name => {
            if (!buffer.series[name]) {
                buffer.series[name] = new Array(buffer.timestamps.length - 1).fill(null);
                if (this.selected[id][name] === undefined) {
                    this.selected[id][name] = true;
                }
            }
            buffer.series[name].push(dataMap[name]);
        });

        currentSeriesNames.forEach(name => {
            if (buffer.series[name].length < buffer.timestamps.length) {
                buffer.series[name].push(null);
            }
        });

        const limit = maxCount || 100;
        while (buffer.timestamps.length > limit) {
            buffer.timestamps.shift();
            Object.keys(buffer.series).forEach(name => buffer.series[name].shift());
        }

        // --- 第二阶段：渲染节流与交互保护 (UI Responsiveness) ---
        const now = Date.now();

        // 关键锁定：如果用户刚才点过鼠标 (1秒内)，不执行重绘，给交互留出线程
        const lockUntil = this._interactionLocks[id] || 0;
        if (now < lockUntil) return;

        // 刷新限速：300ms 刷新一次界面，平衡性能与实时感
        const last = this._lastUpdate[id] || 0;
        if (now - last < 300) return;
        this._lastUpdate[id] = now;

        // 如果数据视图面板打开，跳过重绘（防止面板闪动）
        const isDataViewOpen = document.querySelector('.dataview-container') !== null;
        if (isDataViewOpen) return;

        const seriesArr = Object.keys(buffer.series).map(name => {
            return {
                name: name,
                type: 'line',
                data: buffer.series[name],
                showSymbol: false,
                smooth: true,
                lineStyle: { width: 1 },
                itemStyle: { borderColor: '#1e1e1e', borderWidth: 1 }
            };
        });

        chart.setOption({
            xAxis: { data: buffer.timestamps },
            series: seriesArr
        }, { lazyUpdate: true });

        if (incomingNames.some(n => !currentSeriesNames.includes(n))) {
            this.adjustGridTop(id);
        }
    },

    setData: function (id, timestamps, seriesMap, maxCount) {
        const chart = this.charts[id];
        if (!chart || chart.isDisposed()) return;

        // Reset buffer with restored data
        this.buffers[id] = {
            timestamps: [...timestamps],
            series: {},
            formats: {}
        };
        const buffer = this.buffers[id];

        const seriesNames = Object.keys(seriesMap);
        seriesNames.forEach(name => {
            // Deep copy to avoid reference issues
            buffer.series[name] = [...seriesMap[name]];
            if (this.selected[id][name] === undefined) {
                this.selected[id][name] = true;
            }
        });

        // Force immediate render
        const seriesArr = Object.keys(buffer.series).map(name => {
            return {
                name: name,
                type: 'line',
                data: buffer.series[name],
                showSymbol: false,
                smooth: true,
                lineStyle: { width: 1 },
                itemStyle: { borderColor: '#1e1e1e', borderWidth: 1 }
            };
        });

        chart.setOption({
            xAxis: { data: buffer.timestamps },
            series: seriesArr
        }, { lazyUpdate: false }); // Immediate update

        this.adjustGridTop(id);
    },

    adjustGridTop: function (id) {
        const chart = this.charts[id];
        if (!chart) return;
        try {
            var legendModel = chart.getModel().getComponent('legend');
            var legendView = chart.getViewOfComponentModel(legendModel);
            if (legendView && legendView.group) {
                var rect = legendView.group.getBoundingRect();
                var h = rect.height;
                var newTop = 25 + h + 8; // Legend top (25) + height + padding (8)
                if (newTop < 60) newTop = 60; // Minimum top

                var opt = chart.getOption();
                var curTop = 0;
                if (opt.grid && opt.grid[0]) {
                    curTop = parseFloat(opt.grid[0].top);
                }

                if (isNaN(curTop)) curTop = 40;

                if (Math.abs(curTop - newTop) > 5) {
                    chart.setOption({ grid: { top: newTop } });
                }
            }
        } catch (e) {
            console.error("Error adjusting grid top:", e);
        }
    },

    renderDataView: function (id, opt) {
        var series = opt.series;
        var axisData = opt.xAxis[0].data;
        var selectedMap = this.selected[id];

        // Filter selected series
        var selectedIndices = [];
        for (var i = 0; i < series.length; i++) {
            var name = series[i].name;
            var isSelected = true;
            if (selectedMap && selectedMap.hasOwnProperty(name)) {
                isSelected = selectedMap[name];
            }
            if (isSelected) {
                selectedIndices.push(i);
            }
        }

        // Create scrollable container
        var container = document.createElement('div');
        container.style.cssText = 'width:100%;height:100%;overflow:auto;background:#ffffff;color:#333333;';
        container.className = 'dataview-container';

        // Add styles
        var style = document.createElement('style');
        style.innerHTML = `
            .dataview-table { width: 100%; border-collapse: separate; border-spacing: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; background: #fff; }
            .dataview-table thead th { 
                position: sticky; 
                top: 0; 
                background: #f3f3f3; 
                z-index: 20; 
                text-align: center; 
                color: #444; 
                font-weight: 600;
                padding: 4px 6px; 
                white-space: nowrap;
                border-bottom: 1px solid #e0e0e0; 
                border-right: 1px solid #e0e0e0; 
            }
            .dataview-table thead th:last-child { border-right: none; }
            .dataview-table td { 
                text-align: center; 
                white-space: nowrap; 
                padding: 2px 6px; 
                border-bottom: 1px solid #f0f0f0; 
                border-right: 1px solid #f0f0f0; 
            }
            .dataview-table td:last-child { border-right: none; }
            .dataview-table tbody tr:hover { background-color: #f5faff; }
            
            /* Light theme scrollbar */
            .dataview-container::-webkit-scrollbar { width: 10px; height: 10px; }
            .dataview-container::-webkit-scrollbar-track { background: #f1f1f1; }
            .dataview-container::-webkit-scrollbar-thumb { background: #ccc; border: 2px solid #f1f1f1; border-radius: 5px; }
            .dataview-container::-webkit-scrollbar-thumb:hover { background: #bbb; }
        `;
        container.appendChild(style);

        // Create table
        var table = document.createElement('table');
        table.className = 'dataview-table';
        container.appendChild(table);

        // Helper
        function getTheadTd(val) {
            return '<td>' + val + '</td>';
        }
        function getTh(val) {
            return '<th>' + val + '</th>';
        }

        // Headers
        var seriesNameStr = '';
        for (var i = 0; i < selectedIndices.length; i++) {
            seriesNameStr += getTh(series[selectedIndices[i]].name)
        }

        var thead = document.createElement('thead');
        thead.innerHTML = '<tr>'
            + getTh('No.')
            + getTh('Time')
            + seriesNameStr
            + '</tr>';
        table.appendChild(thead);

        var tbody = document.createElement('tbody');
        table.appendChild(tbody);

        // Incremental rendering
        if (selectedIndices.length > 0 && axisData && axisData.length > 0) {
            var dataLen = axisData.length;
            var renderedIndex = 0;
            var batchSize = 1000; // Adjusted batch size

            const renderBatch = () => {
                var end = Math.min(renderedIndex + batchSize, dataLen);
                var html = '';
                // Since dataView shows latest data typically, we might want to iterate normally.
                // The buffer is ordered chronologically.
                for (var i = renderedIndex; i < end; i++) {
                    var seriesDataValue = '';
                    for (var j = 0; j < selectedIndices.length; j++) {
                        var idx = selectedIndices[j];
                        var val = series[idx].data[i];
                        if (val === null || val === undefined) {
                            seriesDataValue += getTheadTd('-');
                        } else {
                            const fmt = this.buffers[id].formats[series[idx].name];
                            seriesDataValue += getTheadTd(this.formatValue(val, fmt));
                        }
                    }
                    html += '<tr>'
                        + getTheadTd(i + 1)
                        + getTheadTd(axisData[i])
                        + seriesDataValue
                        + '</tr>';
                }
                tbody.insertAdjacentHTML('beforeend', html);
                renderedIndex = end;
            }

            // Render first batch
            renderBatch();

            // Bind scroll event
            container.addEventListener('scroll', function () {
                if (this.scrollHeight - this.scrollTop - this.clientHeight < 50) {
                    if (renderedIndex < dataLen) {
                        renderBatch();
                    }
                }
            });
        }

        return container;
    },

    dispose: function (id) {
        if (this.observers[id]) {
            this.observers[id].disconnect();
            delete this.observers[id];
        }
        if (this.charts[id]) {
            this.charts[id].dispose();
            delete this.charts[id];
            delete this.buffers[id];
            delete this.selected[id];
        }
    }
};
