/**
 * Chart Manager
 * Handles Plotly.js chart creation, updates, and management
 */

import { formatProcessorName, safeClone, downloadJSON, showConfirmDialog } from './utils.js';
import { renderIcons } from './icons.js';
import { ChartSettingsManager } from './charts/chart-settings.js';
import { BScanClickHandler } from './charts/bscan-click-handler.js';
import {
    defaultPlotlyLayout,
    defaultPlotlyConfig,
    createPlotlyPlot,
    updatePlotlyPlot,
    togglePlotlyFullscreen,
    downloadPlotlyImage,
    cleanupPlotly
} from './plotly-utils.js';

export class ChartManager {
    constructor(config, notifications, websocket = null) {
        this.config = config;
        this.notifications = notifications;
        this.websocket = websocket;

        this.charts = new Map();
        this.chartData = new Map();
        this.disabledProcessors = new Set();

        this.chartsGrid = null;
        this.emptyState = null;

        this.updateQueue = new Map();
        this.isUpdating = false;
        this.isPaused = false;

        this.performanceStats = {
            chartsCreated: 0,
            updatesProcessed: 0,
            avgUpdateTime: 0,
            lastUpdateTime: null
        };

        this.settingsManager = new ChartSettingsManager();
        this.bscanClickHandler = new BScanClickHandler(websocket, notifications);
    }

    async init() {
        console.log('Initializing Chart Manager...');
        this.chartsGrid = document.getElementById('chartsGrid');
        this.emptyState = document.getElementById('emptyState');
        if (!this.chartsGrid || !this.emptyState) throw new Error('Required DOM elements not found');
        if (typeof Plotly === 'undefined') throw new Error('Plotly.js not loaded');
        console.log('Chart Manager initialized');
    }

    addResult(payload) {
        try {
            const { processor_id, timestamp, plotly_config, metadata } = payload;
            if (!processor_id) {
                console.warn('Invalid result - missing processor_id:', payload);
                return;
            }

            if (this.disabledProcessors.has(processor_id)) {
                return;
            }

            // Note: 'data' field is no longer included in broadcasts to reduce traffic
            // Use get_processor_state command to retrieve full data when needed
            this.chartData.set(processor_id, {
                timestamp: new Date((timestamp ?? Date.now()) * 1000),
                metadata: metadata || {},
                plotly_config: plotly_config || { data: [], layout: {} }
            });

            if (!this.charts.has(processor_id)) this.createChart(processor_id);

            this.updateChart(processor_id, plotly_config || { data: [], layout: {} });
            this.hideEmptyState();
        } catch (e) {
            console.error('Error adding chart result:', e);
            this.notifications?.show?.({
                type: 'error',
                title: 'Ошибка графика',
                message: 'Не удалось обновить график'
            });
        }
    }

    createChart(processorId) {
        console.log(`Creating chart for processor: ${processorId}`);
        const card = this.createChartCard(processorId);
        this.chartsGrid.appendChild(card);

        const plotContainer = card.querySelector('.chart-card__plot');
        const layoutOverrides = {
            title: { text: formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } },
            width: plotContainer.clientWidth || 500,
            height: plotContainer.clientHeight || 420
        };

        // Keep interactivity for bscan processor but disable some features
        const configOverrides = processorId === 'bscan' ? {
            displayModeBar: true,
            modeBarButtonsToRemove: ['select2d', 'lasso2d'],
            scrollZoom: false
        } : {};

        createPlotlyPlot(plotContainer, [], layoutOverrides, configOverrides);

        this.charts.set(processorId, { element: card, plotContainer, isVisible: true, settingsInitialized: false });
        this.performanceStats.chartsCreated++;

        // Attach click handler for bscan processor
        if (processorId === 'bscan') {
            this.bscanClickHandler.attachClickHandler(processorId, plotContainer);
        }

        if (this.config.animation) {
            setTimeout(() => card.classList.add('chart-card--animated'), 50);
        }
    }

    updateChart(processorId, plotlyConfig) {
        if (this.isPaused) return;

        const chart = this.charts.get(processorId);
        if (!chart?.plotContainer) {
            console.warn(`Chart not found for processor: ${processorId}`);
            return;
        }

        try {
            const start = performance.now();
            this.queueUpdate(processorId, async () => {
                const layoutOverrides = {
                    ...(plotlyConfig.layout || {}),
                    title: { text: formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } }
                };

                // Keep interactivity for bscan processor but disable some features
                const configOverrides = processorId === 'bscan' ? {
                    displayModeBar: true,
                    modeBarButtonsToRemove: ['select2d', 'lasso2d'],
                    scrollZoom: false
                } : {};

                await updatePlotlyPlot(chart.plotContainer, plotlyConfig.data || [], layoutOverrides, configOverrides);

                this.updateChartMetadata(processorId);

                if (!chart.settingsInitialized) {
                    this.updateChartSettings(processorId);
                    chart.settingsInitialized = true;
                } else {
                    this.updateChartSettings(processorId);
                }

                // Clear selection for bscan when data updates
                if (processorId === 'bscan') {
                    this.bscanClickHandler.onDataUpdate(processorId);
                }

                const dt = performance.now() - start;
                this.updatePerformanceStats(dt);
            });
        } catch (e) {
            console.error(`Error updating chart ${processorId}:`, e);
        }
    }

    queueUpdate(id, fn) {
        this.updateQueue.set(id, fn);
        if (!this.isUpdating) this.processUpdateQueue();
    }

    async processUpdateQueue() {
        if (this.isPaused) return;
        this.isUpdating = true;
        while (this.updateQueue.size > 0 && !this.isPaused) {
            const [id, fn] = this.updateQueue.entries().next().value;
            this.updateQueue.delete(id);
            try { await fn(); } catch (e) { console.error(`Error in queued update for ${id}:`, e); }
            await new Promise(r => setTimeout(r, 0));
        }
        this.isUpdating = false;
    }

    createChartCard(processorId) {
        const card = document.createElement('div');
        card.className = 'chart-card';
        card.dataset.processor = processorId;

        card.innerHTML = `
            <div class="chart-card__header">
                <div class="chart-card__title">
                    <span data-icon="bar-chart-3" class="chart-card__icon"></span>
                    ${formatProcessorName(processorId)}
                </div>
                <div class="chart-card__actions">
                    <button class="chart-card__action" data-action="fullscreen" title="Fullscreen">
                        <span data-icon="expand"></span>
                    </button>
                    <button class="chart-card__action" data-action="upload" title="Load History">
                        <span data-icon="upload"></span>
                    </button>
                    <button class="chart-card__action" data-action="append" title="Append History">
                        <span data-icon="plus"></span>
                    </button>
                    <button class="chart-card__action" data-action="download" title="Download JSON">
                        <span data-icon="download"></span>
                    </button>
                    <button class="chart-card__action" data-action="export-sweeps" title="Export Sweeps (TSV)">
                        <span data-icon="database"></span>
                    </button>
                    <button class="chart-card__action" data-action="hide" title="Hide">
                        <span data-icon="eye-off"></span>
                    </button>
                </div>
                <input type="file" id="historyFileInput_${processorId}" accept=".json" style="display: none;">
                <input type="file" id="appendFileInput_${processorId}" accept=".json" style="display: none;">
            </div>
            <div class="chart-card__content">
                <div class="chart-card__plot" id="plot-${processorId}"></div>
                <div class="chart-card__settings" id="settings-${processorId}">
                    <div class="chart-settings">
                        <div class="chart-settings__header">Settings</div>
                        <div class="chart-settings__controls">
                            <!-- Settings will be populated here -->
                        </div>
                    </div>
                </div>
            </div>
            <div class="chart-card__meta">
                <div class="chart-card__timestamp" data-timestamp="">Last update: --</div>
                <div class="chart-card__sweep" data-sweep=""></div>
                ${processorId === 'bscan' ? `
                    <div class="chart-card__shortcuts" style="font-size: 11px; color: #94a3b8; margin-top: 4px;">
                        Клавиши: <kbd>Клик</kbd> - выбрать | <kbd>D</kbd> - удалить | <kbd>P</kbd> - предпросмотр | <kbd>Esc</kbd> - отмена
                    </div>
                ` : ''}
            </div>
        `;

        this.setupChartCardEvents(card, processorId);
        this.updateChartSettings(processorId);

        renderIcons(card);
        return card;
    }

    setupChartCardEvents(card, processorId) {
        card.addEventListener('click', (e) => {
            const action = e.target.closest('[data-action]')?.dataset.action;
            if (!action) return;
            e.stopPropagation();
            switch (action) {
                case 'fullscreen': this.toggleFullscreen(processorId); break;
                case 'upload': this.uploadHistory(processorId); break;
                case 'append': this.appendHistory(processorId); break;
                case 'download': this.downloadChart(processorId); break;
                case 'export-sweeps': this.exportSweeps(processorId); break;
                case 'hide':
                    this.hideChart(processorId);
                    if (window.vnaDashboard?.ui) window.vnaDashboard.ui.setProcessorEnabled(processorId, false);
                    break;
            }
        });

        // Setup file input handler for load history
        const fileInput = card.querySelector(`#historyFileInput_${processorId}`);
        if (fileInput) {
            fileInput.addEventListener('change', async (e) => {
                await this.handleHistoryUpload(processorId, e);
            });
        }

        // Setup file input handler for append history
        const appendFileInput = card.querySelector(`#appendFileInput_${processorId}`);
        if (appendFileInput) {
            appendFileInput.addEventListener('change', async (e) => {
                await this.handleHistoryAppend(processorId, e);
            });
        }
    }

    updateChartMetadata(processorId) {
        const chart = this.charts.get(processorId);
        const latestData = this.chartData.get(processorId);
        if (!chart || !latestData) return;

        const tsEl = chart.element.querySelector('[data-timestamp]');
        if (tsEl) {
            const dt = latestData.timestamp instanceof Date ? latestData.timestamp : new Date();
            tsEl.textContent = `Last update: ${dt.toLocaleTimeString()}`;
            tsEl.dataset.timestamp = dt.toISOString();
        }
    }

    updateChartSettings(processorId) {
        const chart = this.charts.get(processorId);
        const settingsContainer = chart?.element?.querySelector('.chart-settings__controls');
        const latestData = this.chartData.get(processorId);

        if (settingsContainer && latestData) {
            this.settingsManager.updateSettings(processorId, settingsContainer, latestData);
        }
    }

    toggleProcessor(id, enabled) { enabled ? this.showChart(id) : this.hideChart(id); }

    showChart(id) {
        const c = this.charts.get(id);
        if (c) {
            c.element.classList.remove('chart-card--hidden');
            c.isVisible = true;
            setTimeout(() => c.plotContainer && Plotly.Plots.resize(c.plotContainer), 100);
        }
        this.updateEmptyStateVisibility();
    }

    hideChart(id) {
        const c = this.charts.get(id);
        if (c) { c.element.classList.add('chart-card--hidden'); c.isVisible = false; }
        this.updateEmptyStateVisibility();
    }

    removeChart(id) {
        const c = this.charts.get(id);
        if (c) {
            // Cleanup bscan click handler if applicable
            if (id === 'bscan') {
                this.bscanClickHandler.detachClickHandler(id, c.plotContainer);
            }

            cleanupPlotly(c.plotContainer);
            c.element.remove();
            this.charts.delete(id);
            this.chartData.delete(id);
            this.disabledProcessors.delete(id);
        }
        this.updateEmptyStateVisibility();
    }

    clearAll() {
        for (const [id] of this.charts) this.removeChart(id);
        this.charts.clear();
        this.chartData.clear();
        this.updateQueue.clear();
        this.updateEmptyStateVisibility();
    }

    async downloadChart(id) {
        const c = this.charts.get(id);
        if (!c?.plotContainer) return;

        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const baseFilename = `${id}_${timestamp}`;

        try {
            // Download plot image
            await downloadPlotlyImage(c.plotContainer, `${baseFilename}_plot`);

            // Request full processor state from backend via WebSocket
            const websocket = window.vnaDashboard?.websocket;
            if (websocket && websocket.getProcessorState) {
                // Set up one-time listener for processor_state response
                const stateHandler = (payload) => {
                    if (payload.processor_id === id) {
                        websocket.off('processor_state', stateHandler);

                        const processorData = this.prepareDownloadDataFromState(id, payload);
                        if (processorData) {
                            downloadJSON(processorData, `${baseFilename}_data.json`);
                        }
                    }
                };

                websocket.on('processor_state', stateHandler);
                websocket.getProcessorState(id);

                // Timeout fallback in case server doesn't respond
                setTimeout(() => {
                    websocket.off('processor_state', stateHandler);
                }, 10000);
            } else {
                console.warn('WebSocket not available, downloading limited data');
                const processorData = this.prepareDownloadDataFallback(id);
                if (processorData) {
                    downloadJSON(processorData, `${baseFilename}_data.json`);
                }
            }
        } catch (e) {
            console.error('Chart download failed:', e);
            this.notifications?.show?.({
                type: 'error',
                title: 'Ошибка скачивания',
                message: 'Не удалось скачать данные графика'
            });
        }
    }

    prepareDownloadDataFromState(processorId, statePayload) {
        if (!statePayload || !statePayload.state) return null;

        const { state, current_data } = statePayload;

        return {
            processor_info: {
                processor_id: processorId,
                processor_name: formatProcessorName(processorId),
                download_timestamp: new Date().toISOString()
            },
            state: {
                config: state.config,
                history_count: state.history_count,
                max_history: state.max_history
            },
            current_data: current_data ? {
                data: safeClone(current_data.data),
                plotly_config: safeClone(current_data.plotly_config),
                timestamp: current_data.timestamp
            } : null,
            sweep_history: state.sweep_history || [],
            active_reference: state.active_reference || null
        };
    }

    prepareDownloadDataFallback(processorId) {
        const chart = this.charts.get(processorId);
        const latestData = this.chartData.get(processorId);

        if (!chart || !latestData) return null;

        // Fallback when WebSocket is not available - limited data only
        return {
            processor_info: {
                processor_id: processorId,
                processor_name: formatProcessorName(processorId),
                download_timestamp: new Date().toISOString()
            },
            current_data: {
                metadata: safeClone(latestData.metadata),
                timestamp: latestData.timestamp instanceof Date ? latestData.timestamp.toISOString() : latestData.timestamp,
                plotly_config: safeClone(latestData.plotly_config)
            },
            note: "Limited data - sweep history not available without server connection"
        };
    }

    async toggleFullscreen(id) {
        const c = this.charts.get(id);
        if (!c?.element) return;
        await togglePlotlyFullscreen(c.element, c.plotContainer);
    }

    async exportSweeps(id) {
        const c = this.charts.get(id);
        if (!c?.plotContainer) return;

        try {
            // Request full processor state from backend via WebSocket
            const websocket = window.vnaDashboard?.websocket;
            if (!websocket || !websocket.getProcessorState) {
                this.notifications?.show?.({
                    type: 'error',
                    title: 'Ошибка экспорта',
                    message: 'WebSocket не доступен'
                });
                return;
            }

            // Set up one-time listener for processor_state response
            const stateHandler = async (payload) => {
                if (payload.processor_id === id) {
                    websocket.off('processor_state', stateHandler);

                    try {
                        await this.performSweepExport(id, payload);
                    } catch (e) {
                        console.error('Sweep export failed:', e);
                        this.notifications?.show?.({
                            type: 'error',
                            title: 'Ошибка экспорта',
                            message: `Не удалось экспортировать свипы: ${e.message}`
                        });
                    }
                }
            };

            websocket.on('processor_state', stateHandler);
            websocket.getProcessorState(id);

            // Timeout fallback
            setTimeout(() => {
                websocket.off('processor_state', stateHandler);
            }, 10000);

        } catch (e) {
            console.error('Export sweeps failed:', e);
            this.notifications?.show?.({
                type: 'error',
                title: 'Ошибка экспорта',
                message: 'Не удалось экспортировать данные свипов'
            });
        }
    }

    async performSweepExport(processorId, statePayload) {
        if (!statePayload || !statePayload.state) {
            throw new Error('Нет данных состояния процессора');
        }

        const { state } = statePayload;
        const sweepHistory = state.sweep_history || [];

        console.log('performSweepExport: sweepHistory length:', sweepHistory.length);

        if (sweepHistory.length === 0) {
            throw new Error('Нет данных свипов для экспорта');
        }

        // Get latest sweep (most recent)
        const latestSweep = sweepHistory[sweepHistory.length - 1];
        console.log('performSweepExport: latestSweep keys:', Object.keys(latestSweep));
        console.log('performSweepExport: sweep_points length:', latestSweep.sweep_points?.length);
        console.log('performSweepExport: calibrated_points length:', latestSweep.calibrated_points?.length);
        console.log('performSweepExport: calibration_standards:', latestSweep.calibration_standards);
        console.log('performSweepExport: raw_reference_points length:', latestSweep.raw_reference_points?.length);
        console.log('performSweepExport: reference_points length:', latestSweep.reference_points?.length);

        // Prepare filename with timestamp and preset info
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const presetMode = latestSweep.vna_config?.mode || 'unknown';
        const baseFilename = `${processorId}_${presetMode}_${timestamp}`;

        let exportedCount = 0;

        // Export raw sweep
        if (latestSweep.sweep_points && latestSweep.sweep_points.length > 0) {
            console.log('Exporting raw sweep, points:', latestSweep.sweep_points.length);
            this.exportPointsToTSV(latestSweep.sweep_points, latestSweep.vna_config, `${baseFilename}_raw`);
            exportedCount++;
        }

        // Export calibrated sweep
        if (latestSweep.calibrated_points && latestSweep.calibrated_points.length > 0) {
            console.log('Exporting calibrated sweep, points:', latestSweep.calibrated_points.length);
            this.exportPointsToTSV(latestSweep.calibrated_points, latestSweep.vna_config, `${baseFilename}_calibrated`);
            exportedCount++;
        }

        // Export calibration standards if present
        // COMMENTED OUT: Don't export calibration files (may be needed later)
        // if (latestSweep.calibration_standards) {
        //     console.log('Exporting calibration standards:', Object.keys(latestSweep.calibration_standards));
        //     for (const [standardName, standardData] of Object.entries(latestSweep.calibration_standards)) {
        //         if (standardData && standardData.points && standardData.points.length > 0) {
        //             this.exportPointsToTSV(standardData.points, latestSweep.vna_config, `${baseFilename}_cal_${standardName}`);
        //             exportedCount++;
        //         }
        //     }
        // }

        // Export raw reference if present
        if (latestSweep.raw_reference_points && latestSweep.raw_reference_points.length > 0) {
            const refName = latestSweep.reference_info?.name || 'reference';
            console.log('Exporting raw reference:', refName);
            this.exportPointsToTSV(latestSweep.raw_reference_points, latestSweep.vna_config, `${baseFilename}_ref_${refName.replace(/\s/g, '_')}_raw`);
            exportedCount++;
        }

        // Export calibrated reference if present (for comparison)
        if (latestSweep.reference_points && latestSweep.reference_points.length > 0) {
            const refName = latestSweep.reference_info?.name || 'reference';
            console.log('Exporting calibrated reference:', refName);
            this.exportPointsToTSV(latestSweep.reference_points, latestSweep.vna_config, `${baseFilename}_ref_${refName.replace(/\s/g, '_')}_calibrated`);
            exportedCount++;
        }

        console.log('Total exported files:', exportedCount);
    }

    exportPointsToTSV(points, vnaConfig, filename) {
        console.log('exportPointsToTSV called with filename:', filename);
        console.log('exportPointsToTSV points length:', points?.length);

        if (!points || points.length === 0) {
            console.warn('No points to export');
            return;
        }

        const numPoints = points.length;
        console.log('exportPointsToTSV: numPoints =', numPoints);

        // Generate frequency array
        const startFreq = vnaConfig?.start_freq || 100e6;
        const stopFreq = vnaConfig?.stop_freq || 8.8e9;

        let frequencies;
        if (numPoints === 1) {
            frequencies = [startFreq];
        } else {
            const step = (stopFreq - startFreq) / (numPoints - 1);
            frequencies = Array.from({ length: numPoints }, (_, i) => startFreq + i * step);
        }

        // Build TSV content
        let tsv = 'Frequency(Hz)\tReal\tImaginary\n';
        for (let i = 0; i < numPoints; i++) {
            const point = points[i];
            const freq = frequencies[i];
            const real = point[0];
            const imag = point[1];
            tsv += `${freq}\t${real}\t${imag}\n`;
        }

        console.log('exportPointsToTSV: TSV size =', tsv.length, 'chars');

        // Download file
        const blob = new Blob([tsv], { type: 'text/tab-separated-values;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = `${filename}.tsv`;
        document.body.appendChild(link);
        console.log('exportPointsToTSV: Clicking download link for', filename);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }

    hideEmptyState() {
        if (this.emptyState) this.emptyState.classList.add('empty-state--hidden');
    }

    updateEmptyStateVisibility() {
        if (!this.emptyState) return;
        const hasVisible = Array.from(this.charts.values()).some(c => c.isVisible);
        this.emptyState.classList.toggle('empty-state--hidden', hasVisible);
    }

    updatePerformanceStats(dt) {
        this.performanceStats.updatesProcessed++;
        this.performanceStats.lastUpdateTime = new Date();
        const total = this.performanceStats.avgUpdateTime * (this.performanceStats.updatesProcessed - 1) + dt;
        this.performanceStats.avgUpdateTime = total / this.performanceStats.updatesProcessed;
    }

    pause() { this.isPaused = true; console.log('Chart updates paused'); }
    resume() { this.isPaused = false; console.log('Chart updates resumed'); if (this.updateQueue.size) this.processUpdateQueue(); }

    getDisabledProcessors() { return Array.from(this.disabledProcessors); }

    getStats() {
        return {
            ...this.performanceStats,
            totalCharts: this.charts.size,
            visibleCharts: Array.from(this.charts.values()).filter(c => c.isVisible).length,
            disabledProcessors: this.disabledProcessors.size,
            queuedUpdates: this.updateQueue.size,
            isPaused: this.isPaused
        };
    }

    uploadHistory(processorId) {
        const chart = this.charts.get(processorId);
        if (!chart) return;

        const fileInput = chart.element.querySelector(`#historyFileInput_${processorId}`);
        if (fileInput) {
            fileInput.click();
        }
    }

    appendHistory(processorId) {
        const chart = this.charts.get(processorId);
        if (!chart) return;

        const fileInput = chart.element.querySelector(`#appendFileInput_${processorId}`);
        if (fileInput) {
            fileInput.click();
        }
    }

    async handleHistoryUpload(processorId, event) {
        const file = event.target.files?.[0];
        if (!file) return;

        try {
            const text = await file.text();
            const jsonData = JSON.parse(text);

            // Extract sweep_history and processor config from the saved JSON file
            const sweepHistory = jsonData.sweep_history || [];
            const processorConfig = jsonData.state?.config || null;

            if (!sweepHistory || sweepHistory.length === 0) {
                this.notifications?.show?.({
                    type: 'error',
                    title: 'Ошибка загрузки',
                    message: 'Файл не содержит истории свипов'
                });
                return;
            }

            // Show dialog to choose whether to update settings or not
            const choice = await showConfirmDialog({
                title: 'Загрузка истории',
                message: 'Обновить параметры процессора из файла или сохранить текущие настройки?',
                buttons: [
                    { value: 'cancel', text: 'Отмена', class: 'btn--secondary' },
                    { value: 'data_only', text: 'Только данные', class: 'btn--primary' },
                    { value: 'with_settings', text: 'Данные и настройки', class: 'btn--primary' }
                ]
            });

            // User cancelled
            if (!choice || choice === 'cancel') {
                return;
            }

            // Determine whether to include config
            const configToSend = (choice === 'with_settings' && processorConfig) ? processorConfig : null;

            // Send load_history message via WebSocket
            const websocket = window.vnaDashboard?.websocket;
            if (websocket && websocket.ws && websocket.ws.readyState === WebSocket.OPEN) {
                websocket.ws.send(JSON.stringify({
                    type: 'load_history',
                    processor_id: processorId,
                    history_data: sweepHistory,
                    config: configToSend
                }));

                const settingsMsg = configToSend ? ' (с обновлением настроек)' : ' (без изменения настроек)';
                this.notifications?.show?.({
                    type: 'success',
                    title: 'История загружена',
                    message: `Загружено ${sweepHistory.length} записей для ${formatProcessorName(processorId)}${settingsMsg}`
                });
            } else {
                this.notifications?.show?.({
                    type: 'error',
                    title: 'Ошибка подключения',
                    message: 'WebSocket не подключен'
                });
            }

        } catch (err) {
            console.error('Error loading history:', err);
            this.notifications?.show?.({
                type: 'error',
                title: 'Ошибка загрузки',
                message: `Не удалось прочитать файл: ${err.message}`
            });
        }

        // Reset file input
        event.target.value = '';
    }

    async handleHistoryAppend(processorId, event) {
        const file = event.target.files?.[0];
        if (!file) return;

        try {
            const text = await file.text();
            const jsonData = JSON.parse(text);

            // Extract sweep_history from the saved JSON file
            // Note: We do NOT use the config - only append history
            const sweepHistory = jsonData.sweep_history || [];

            if (!sweepHistory || sweepHistory.length === 0) {
                this.notifications?.show?.({
                    type: 'error',
                    title: 'Ошибка дополнения',
                    message: 'Файл не содержит истории свипов'
                });
                return;
            }

            // Send append_history message via WebSocket
            const websocket = window.vnaDashboard?.websocket;
            if (websocket && websocket.ws && websocket.ws.readyState === WebSocket.OPEN) {
                websocket.ws.send(JSON.stringify({
                    type: 'append_history',
                    processor_id: processorId,
                    history_data: sweepHistory
                }));

                this.notifications?.show?.({
                    type: 'success',
                    title: 'История дополнена',
                    message: `Добавлено ${sweepHistory.length} записей к истории ${formatProcessorName(processorId)}`
                });
            } else {
                this.notifications?.show?.({
                    type: 'error',
                    title: 'Ошибка подключения',
                    message: 'WebSocket не подключен'
                });
            }

        } catch (err) {
            console.error('Error appending history:', err);
            this.notifications?.show?.({
                type: 'error',
                title: 'Ошибка дополнения',
                message: `Не удалось прочитать файл: ${err.message}`
            });
        }

        // Reset file input
        event.target.value = '';
    }

    destroy() {
        console.log('Cleaning up Chart Manager...');
        this.clearAll();
        this.settingsManager.destroy();
        this.bscanClickHandler.destroy();
        this.updateQueue.clear();
        this.isUpdating = false;
        this.isPaused = true;
        console.log('Chart Manager cleanup complete');
    }
}
