using Common.Attributes;
using Model.Entity;
using Model.Models;
using NLog;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using Service.Interface;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using TSMasterCAN;
using UIShare.GlobalVariable;
using UIShare.PubEvent;
using UIShare.UIViewModel;
using UIShare.ViewModelBase;


namespace MonitorModule.ViewModels
{
    public class MonitorViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable
    {
        #region 属性
        private IMonitorValueService _monitorValueService;
        public bool KeepAlive => true;
        public ScopedContext _scopedContext { get; set; } = null!;
        public GlobalInfo _globalInfo { get; set; }

        private string _testStatus = string.Empty;
        public string TestStatus
        {
            get => _testStatus;
            set => SetProperty(ref _testStatus, value);
        }

        public PlotModel Plot { get; }

        /// <summary>已添加的监测通道列表</summary>
        public ObservableCollection<MonitorChannelVM> Channels
        {
            get => _systemConfig?.Channels ?? new ObservableCollection<MonitorChannelVM>();
            set
            {
                if (_systemConfig != null && _systemConfig.Channels != value)
                {
                    _systemConfig.Channels = value;
                    RaisePropertyChanged();
                }
            }
        }


        /// <summary>可添加的设备方法列表（供用户选择）</summary>
        public ObservableCollection<AvailableMethodItem> AvailableMethods { get; } = new();

        private MonitorChannelVM? _selectedChannel;
        public MonitorChannelVM? SelectedChannel
        {
            get => _selectedChannel;
            set => SetProperty(ref _selectedChannel, value);
        }

        private AvailableMethodItem? _selectedAvailableMethod;
        public AvailableMethodItem? SelectedAvailableMethod
        {
            get => _selectedAvailableMethod;
            set => SetProperty(ref _selectedAvailableMethod, value);
        }

        private string _statusMessage = "图表已就绪，暂无监测项";
        public string StatusMessage
        {
            get => _statusMessage;
            set => SetProperty(ref _statusMessage, value);
        }
        #endregion

        #region 命令
        public ICommand AddChannelCommand { get; }
        public ICommand DeleteChannelCommand { get; }
        public ICommand ResetViewCommand { get; }
        public ICommand RefreshDataCommand { get; }
        public ICommand RefreshCommand { get; }
        public ICommand SaveCommand { get; }
        #endregion

        #region 私有字段
        private bool IsInitiated = false;
        private IScopedProvider? _scope;
        private DeviceManager? _deviceManager;
        private SystemConfig? _systemConfig;
        private CancellationTokenSource? _monitorCTS = new();

        // 颜色调色盘
        private static readonly OxyColor[] _palette =
        {
            OxyColors.SteelBlue, OxyColors.IndianRed, OxyColors.SeaGreen,
            OxyColors.DarkOrange, OxyColors.MediumPurple, OxyColors.Goldenrod,
            OxyColors.Teal, OxyColors.Crimson, OxyColors.OliveDrab
        };
        private int _colorIndex;

        /// <summary>设备数据广播器（全局单例）</summary>
        private HardwareDataBroadcaster? _broadcaster;

        /// <summary>CAN 信号广播器（全局单例）</summary>
        private CANSignalBroadcaster? _canSignalBroadcaster;

        /// <summary>用于 OxyPlot X 轴相对秒数</summary>
        private readonly Stopwatch _stopwatch = new();

        /// <summary>OxyPlot 刷新定时器（仅负责滚动 X 轴 + InvalidatePlot）</summary>
        private readonly DispatcherTimer _plotRefreshTimer;

        /// <summary>事件订阅令牌，用于 Dispose 时取消订阅</summary>
        private SubscriptionToken? _subscriptionToken;
        private SubscriptionToken? _dbcLoadedToken;
        private SubscriptionToken? _dbcUnloadedToken;

        // ==========================================
        // 批量入库核心结构
        // ==========================================
        private readonly ConcurrentQueue<MonitorValueEntity> _insertQueue = new();
        private Task? _dbFlushTask;
        private const int BulkInsertThreshold = 50;
        private int _isFlushing = 0;
        #endregion

        public MonitorViewModel(IContainerExtension container) : base(container)
        {
            _globalInfo = container.Resolve<GlobalInfo>();
            _monitorValueService = container.Resolve<IMonitorValueService>();

            Plot = BuildEmptyPlot();

            AddChannelCommand = new DelegateCommand(OnAddChannel);
            DeleteChannelCommand = new DelegateCommand(OnDeleteChannel);
            ResetViewCommand = new DelegateCommand(OnResetView);
            RefreshDataCommand = new DelegateCommand(OnRefreshData);
            RefreshCommand = new DelegateCommand(OnExpand);
            SaveCommand = new DelegateCommand(OnSave);

            // OxyPlot 刷新定时器（1000ms，只负责视觉更新）
            _plotRefreshTimer = new DispatcherTimer(DispatcherPriority.Background)
            {
                Interval = TimeSpan.FromMilliseconds(1000)
            };
            _plotRefreshTimer.Tick += OnPlotRefreshTick;
        }

      

        public void Dispose()
        {
            _monitorCTS?.Cancel();
            _monitorCTS?.Dispose();
            _monitorCTS = null;

            _plotRefreshTimer.Stop();
            _plotRefreshTimer.Tick -= OnPlotRefreshTick;
            _stopwatch.Stop();

            // 取消 EventAggregator 订阅
            if (_subscriptionToken != null)
                _eventAggregator.GetEvent<HardwareDataReportedEvent>().Unsubscribe(_subscriptionToken);
            if (_dbcLoadedToken != null)
                _eventAggregator.GetEvent<DBCLoadedEvent>().Unsubscribe(_dbcLoadedToken);
            if (_dbcUnloadedToken != null)
                _eventAggregator.GetEvent<DBCUnloadedEvent>().Unsubscribe(_dbcUnloadedToken);

            if (_dbFlushTask != null)
            {
                try { _dbFlushTask.Wait(TimeSpan.FromSeconds(1.5)); } catch { }
            }


            // 3. 释放容器作用域
            _scope?.Dispose();
            Channels.Clear();
            AvailableMethods.Clear();
        }
        private void OnSave()
        {
            // 将当前 Channels 序列化为 MonitorChannelConfig 列表保存到 MonitorChannels
            if (_systemConfig != null)
            {
                _systemConfig.MonitorChannels = new ObservableCollection<MonitorChannelConfig>(Channels
                    .Select(c => new MonitorChannelConfig
                    {
                        Fingerprint = c.Fingerprint,
                        MethodName = c.MethodName,
                        IsDisplayed = c.IsDisplayed
                    }));
            }
            ConfigService.Save(_systemConfig);
        }
        #region 事件驱动：接收广播数据
        /// <summary>
        /// 由 HardwareDataBroadcaster 通过 EventAggregator 广播触发。
        /// 已通过 ThreadOption.UIThread 确保在 UI 线程执行。
        /// 通过 HardwareFingerprint + MethodName 匹配监测通道。
        /// </summary>
        private void OnHardwareDataReceived(HardwareReportArgs args)
        {
            // 仅处理属于当前 Scope 的事件
            if (string.IsNullOrEmpty(TestStatus) || args.Scope != TestStatus) return;

            // 以硬件指纹 + 方法名唯一匹配通道（与逻辑名解耦）
            var channel = Channels.FirstOrDefault(c =>
                c.Fingerprint == args.HardwareFingerprint &&
                c.MethodName == args.MethodName &&
                c.IsMonitored);

            if (channel == null) return;

            // 记录数据点（线程安全：Record 内部 ConcurrentQueue + Series 操作）
            channel.Record(args.Time, args.Value);

            // 入队数据库批量写入
            // CreateTime 使用 args.Time（采样触发时刻），而不是 DateTime.Now（设备响应到达时刻），
            // 这样即使设备响应有延迟，数据库里的时间戳仍然按设定采样间隔分布。
            var entity = new MonitorValueEntity
            {
                MonitorName = channel.DisplayName,
                MonitorValue = args.Value,
                Scope = args.Scope,
                CreateTime = args.Time,
                IsDel = 0
            };
            _insertQueue.Enqueue(entity);

            if (_insertQueue.Count >= BulkInsertThreshold)
            {
                _ = Task.Run(async () => await DoFlushWorkAsync().ConfigureAwait(false));
            }
        }
        #endregion

        #region OxyPlot 视觉刷新定时器
        private void OnPlotRefreshTick(object? sender, EventArgs e)
        {
            if (!_stopwatch.IsRunning) return;

            var now = DateTime.Now;
            var xAxis = Plot.Axes.FirstOrDefault(a => a.Position == AxisPosition.Bottom);
            if (xAxis != null)
            {
                xAxis.Minimum = DateTimeAxis.ToDouble(now.AddSeconds(-20));
                xAxis.Maximum = DateTimeAxis.ToDouble(now.AddSeconds(0.5));
            }
            Plot.InvalidatePlot(true);
        }
        #endregion

        #region 后台数据库批量入库
        private void StartDbFlushWorker(CancellationToken token)
        {
            _dbFlushTask = Task.Run(async () =>
            {
                while (!token.IsCancellationRequested)
                {
                    try
                    {
                        await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
                        await DoFlushWorkAsync().ConfigureAwait(false);
                    }
                    catch (OperationCanceledException) { break; }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine($"[DB Worker Error] {ex.Message}");
                    }
                }
                await DoFlushWorkAsync().ConfigureAwait(false);
            }, token);
        }

        private async Task DoFlushWorkAsync()
        {
            if (_insertQueue.IsEmpty) return;
            if (Interlocked.CompareExchange(ref _isFlushing, 1, 0) != 0) return;

            try
            {
                var listToInsert = new List<MonitorValueEntity>();
                while (_insertQueue.TryDequeue(out var entity)) listToInsert.Add(entity);

                if (listToInsert.Count > 0)
                {
                    var result = await _monitorValueService.InsertRangeAsync(listToInsert).ConfigureAwait(false);
                }
            }
            finally { Interlocked.Exchange(ref _isFlushing, 0); }
        }
        #endregion

        #region Navigation 导航
        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            base.OnNavigatedTo(navigationContext);
            if (!IsInitiated && navigationContext.Parameters.ContainsKey("Name"))
            {
                TestStatus = navigationContext.Parameters.GetValue<string>("Name");
                Plot.Title = $"监控 - {TestStatus}";
                Plot.InvalidatePlot(false);

                _scope = _globalInfo.ScopeDic[TestStatus];
                _scopedContext = _scope.Resolve<ScopedContext>();
                _deviceManager = _scope.Resolve<DeviceManager>();
                _systemConfig = _scope.Resolve<SystemConfig>();
                RaisePropertyChanged(nameof(Channels));
                _broadcaster = _scope.Resolve<HardwareDataBroadcaster>();
                _canSignalBroadcaster = _scope.Resolve<CANSignalBroadcaster>();
                IsInitiated = true;

                if (_monitorCTS == null) _monitorCTS = new CancellationTokenSource();

                // 1. 启动后台数据库消费
                StartDbFlushWorker(_monitorCTS.Token);

                // 2. 扫描可监测方法（供 UI 选择）
                DiscoverAvailableMethods();

                // 3. 从配置自动恢复已保存的监测通道
                RestoreChannelsFromConfig();

                // 4. 启动广播器（Discover + Start）
                _broadcaster.Discover();
                _broadcaster.Start();
                _canSignalBroadcaster.Discover();
                _canSignalBroadcaster.Start();

                // 5. 订阅 HardwareDataReportedEvent（UI 线程回调）
                _subscriptionToken = _eventAggregator.GetEvent<HardwareDataReportedEvent>()
                    .Subscribe(OnHardwareDataReceived, ThreadOption.UIThread);

                // 5b. 订阅 DBC 加载/卸载事件，动态发现或清除 CAN 信号
                _dbcLoadedToken = _eventAggregator.GetEvent<DBCLoadedEvent>()
                    .Subscribe(OnDbcLoaded, ThreadOption.UIThread);
                _dbcUnloadedToken = _eventAggregator.GetEvent<DBCUnloadedEvent>()
                    .Subscribe(OnDbcUnloaded, ThreadOption.UIThread);

                // 5c. 根据 SystemConfig.ConfigurationList 刷新已加载 DBC 中的 CAN 信号
                RefreshConfiguredCanSignals();

                // 6. 启动 OxyPlot 视觉刷新定时器
                _stopwatch.Start();
                _plotRefreshTimer.Start();
            }
        }
        #endregion

        #region PlotModel 构建
        private static PlotModel BuildEmptyPlot()
        {
            var pm = new PlotModel
            {
                Title = string.Empty,
                PlotAreaBorderColor = OxyColors.LightGray,
                Background = OxyColors.White
            };
            pm.Axes.Add(new DateTimeAxis
            {
                Position = AxisPosition.Bottom,
                Title = "时间",
                StringFormat = "HH:mm:ss",
                MajorGridlineStyle = LineStyle.Dot,
                MinorGridlineStyle = LineStyle.None
            });
            pm.Axes.Add(new LinearAxis
            {
                Position = AxisPosition.Left,
                Title = "值",
                MajorGridlineStyle = LineStyle.Dot,
                MinorGridlineStyle = LineStyle.None
            });
            pm.Legends.Add(new Legend
            {
                LegendPosition = LegendPosition.RightTop,
                LegendBackground = OxyColor.FromAColor(200, OxyColors.White),
                LegendBorder = OxyColors.LightGray
            });
            return pm;
        }
        #endregion

        #region 设备方法发现（仅用于 UI 展示可选项）
        /// <summary>
        /// 通过 GlobalInfo.HardwarePool 构建 设备实例引用→硬件指纹 的反向查找表。
        /// </summary>
        private Dictionary<object, string> BuildInstanceToFingerprint()
        {
            var map = new Dictionary<object, string>(ReferenceEqualityComparer.Instance);
            foreach (var poolEntry in _globalInfo.HardwarePool)
            {
                var lazy = poolEntry.Value;
                if (lazy.IsValueCreated && lazy.Value != null)
                    map[lazy.Value] = poolEntry.Key;
            }
            return map;
        }

        private void DiscoverAvailableMethods()
        {
            AvailableMethods.Clear();
            if (_deviceManager?.DeviceMap == null || _deviceManager.DeviceMap.Count == 0)
            {
                StatusMessage = "当前工位无可用设备";
                return;
            }

            var instanceToFp = BuildInstanceToFingerprint();

            foreach (var kvp in _deviceManager.DeviceMap)
            {
                string deviceName = kvp.Key;
                var device = kvp.Value;
                var deviceType = device.GetType();

                // 反查该实例的硬件指纹
                if (!instanceToFp.TryGetValue(device, out string? fingerprint))
                    continue;

                var methods = deviceType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
                    .Where(m =>
                    {
                        if (m.GetCustomAttribute<MonitorableAttribute>() == null) return false;
                        if (m.ReturnType != typeof(Task<string>)) return false;
                        var parms = m.GetParameters();
                        return parms.Length == 0 ||
                               (parms.Length == 1 && parms[0].ParameterType == typeof(CancellationToken));
                    });

                foreach (var method in methods)
                {
                    var attr = method.GetCustomAttribute<MonitorableAttribute>();
                    string displayName = !string.IsNullOrEmpty(attr?.Description)
                        ? $"{deviceName}.{attr.Description}"
                        : $"{deviceName}.{method.Name}";

                    AvailableMethods.Add(new AvailableMethodItem
                    {
                        DeviceName = deviceName,
                        Fingerprint = fingerprint,
                        MethodName = method.Name,
                        DisplayName = displayName,
                        MethodInfo = method,
                        Device = device
                    });
                }
            }

            StatusMessage = AvailableMethods.Count > 0
                ? $"发现 {AvailableMethods.Count} 个可监测项"
                : "未发现可监测的设备方法";
        }

        /// <summary>
        /// 从 SystemConfig.MonitorChannels 持久化列表自动还原监测通道。
        /// </summary>
        private void RestoreChannelsFromConfig()
        {
            if (_systemConfig?.MonitorChannels == null || _systemConfig.MonitorChannels.Count == 0) return;

            int restored = 0;
            foreach (var config in _systemConfig.MonitorChannels)
            {
                if (string.IsNullOrWhiteSpace(config.Fingerprint) ||
                    string.IsNullOrWhiteSpace(config.MethodName)) continue;

                // 已在 Channels 中存在则跳过
                if (Channels.Any(c => c.Fingerprint == config.Fingerprint && c.MethodName == config.MethodName))
                    continue;

                // 在 AvailableMethods 中查找匹配项
                var method = AvailableMethods.FirstOrDefault(m =>
                    m.Fingerprint == config.Fingerprint && m.MethodName == config.MethodName);
                if (method == null) continue;

                var color = _palette[_colorIndex % _palette.Length];
                _colorIndex++;

                var channel = new MonitorChannelVM
                {
                    DeviceName = method.DeviceName,
                    Fingerprint = method.Fingerprint,
                    MethodName = method.MethodName,
                    DisplayName = method.DisplayName,
                    Color = color,
                    IsMonitored = true,
                    IsDisplayed = config.IsDisplayed
                };

                // 仅当 IsDisplayed=true 时才创建 LineSeries
                if (channel.IsDisplayed)
                {
                    channel.Series = new LineSeries
                    {
                        Title = channel.DisplayName,
                        Color = color,
                        StrokeThickness = 1.5
                    };
                    Plot.Series.Add(channel.Series);
                }

                channel.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
                        OnChannelDisplayChanged(channel);
                };

                Channels.Add(channel);
                restored++;
            }

            if (restored > 0)
            {
                Plot.InvalidatePlot(true);
                StatusMessage = $"已从配置自动恢复 {restored} 个监测通道";
            }
        }

        /// <summary>
        /// 根据 SystemConfig.ConfigurationList 刷新所有已加载 DBC 中的 CAN 信号。
        /// 只添加 ConfigurationList 中配置且实际存在于 DBC 的信号；不存在则清理。
        /// </summary>
        private void RefreshConfiguredCanSignals()
        {
            if (_systemConfig?.ConfigurationList == null || _systemConfig.ConfigurationList.Count == 0) return;
            if (_deviceManager?.CANFD == null) return;

            var msgDb = DBCParse.MsgDatabase;
            foreach (var channel in _systemConfig.ConfigurationList.Select(c => c.Channel).Distinct())
            {
                if (channel < 0 || channel >= msgDb.Count) continue;
                RefreshCanSignalsForChannel((uint)channel, msgDb[channel]);
            }
        }

        /// <summary>
        /// 刷新单个通道的 CAN 信号：以 ConfigurationList 为白名单，对照 DBC 实际存在性。
        /// - DBC 中存在 → 加入 AvailableMethods；若 MonitorChannels 中也有，则恢复 Channel。
        /// - DBC 中不存在 → 从 AvailableMethods 移除；若已在 Channels 中，则删除。
        /// </summary>
        private void RefreshCanSignalsForChannel(uint channel, List<_Msg_> messages)
        {
            if (_systemConfig?.ConfigurationList == null) return;

            string canDeviceFingerprint = _deviceManager?.GetCanDeviceFingerprint() ?? string.Empty;
            string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, channel);
            var configs = _systemConfig.ConfigurationList.Where(c => c.Channel == (int)channel).ToList();
            bool changed = false;

            foreach (var cfg in configs)
            {
                if (string.IsNullOrEmpty(cfg.SignalName)) continue;

                string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName);
                bool existsInDbc = messages.Any(m =>
                    m.msg_id == cfg.MessageID &&
                    m.signal_Name != null &&
                    m.signal_Name.Contains(cfg.SignalName));

                if (existsInDbc)
                {
                    // 加入 AvailableMethods（去重）
                    if (!AvailableMethods.Any(m => m.Fingerprint == fingerprint && m.MethodName == methodName))
                    {
                        string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
                        AvailableMethods.Add(new AvailableMethodItem
                        {
                            DeviceName = $"CAN{channel}",
                            Fingerprint = fingerprint,
                            MethodName = methodName,
                            DisplayName = displayName,
                            MethodInfo = null!,
                            Device = null!
                        });
                        changed = true;
                    }

                    // 如果 MonitorChannels 中有持久化记录，自动恢复 Channel
                    var monitorConfig = _systemConfig.MonitorChannels?.FirstOrDefault(m =>
                        m.Fingerprint == fingerprint && m.MethodName == methodName);
                    if (monitorConfig != null && !Channels.Any(c =>
                        c.Fingerprint == fingerprint && c.MethodName == methodName))
                    {
                        AddCanChannel(cfg, fingerprint, methodName, monitorConfig.IsDisplayed);
                        changed = true;
                    }
                }
                else
                {
                    // DBC 中不存在：从 AvailableMethods 移除
                    var methodToRemove = AvailableMethods.FirstOrDefault(m =>
                        m.Fingerprint == fingerprint && m.MethodName == methodName);
                    if (methodToRemove != null)
                    {
                        AvailableMethods.Remove(methodToRemove);
                        changed = true;
                    }

                    // 如果该信号已经在 Channels（或 MonitorChannels）中，删除
                    var channelToRemove = Channels.FirstOrDefault(c =>
                        c.Fingerprint == fingerprint && c.MethodName == methodName);
                    if (channelToRemove != null)
                    {
                        if (channelToRemove.Series != null) Plot.Series.Remove(channelToRemove.Series);
                        Channels.Remove(channelToRemove);
                        changed = true;
                    }
                }
            }

            if (changed)
            {
                Plot.InvalidatePlot(true);
                StatusMessage = $"CAN{channel} 已刷新配置信号";
            }
        }

        /// <summary>
        /// DBCLoadedEvent 回调：DBC 加载后刷新该通道的配置信号。
        /// </summary>
        private void OnDbcLoaded(DBCLoadedArgs args)
        {
            if (args.Scope != TestStatus) return;
            if (_deviceManager?.CANFD == null) return;
            int channel = (int)args.Channel;
            var msgDb = DBCParse.MsgDatabase;
            if (channel < 0 || channel >= msgDb.Count) return;

            RefreshCanSignalsForChannel(args.Channel, msgDb[channel]);
        }

        /// <summary>
        /// DBCUnloadedEvent 回调：移除对应通道的 CAN 信号（AvailableMethods + Channels）。
        /// </summary>
        private void OnDbcUnloaded(DBCUnloadedArgs args)
        {
            if (args.Scope != TestStatus) return;

            string canDeviceFingerprint = _deviceManager?.GetCanDeviceFingerprint() ?? string.Empty;
            string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel);

            // 从 AvailableMethods 中移除
            var toRemoveMethods = AvailableMethods.Where(m => m.Fingerprint == fingerprint).ToList();
            foreach (var m in toRemoveMethods)
                AvailableMethods.Remove(m);

            // 从 Channels 中移除（并清理 Plot）
            var toRemoveChannels = Channels.Where(c => c.Fingerprint == fingerprint).ToList();
            foreach (var c in toRemoveChannels)
            {
                if (c.Series != null) Plot.Series.Remove(c.Series);
                Channels.Remove(c);
            }

            if (toRemoveMethods.Count > 0 || toRemoveChannels.Count > 0)
            {
                Plot.InvalidatePlot(true);
                StatusMessage = $"CAN{args.Channel} DBC 已卸载，移除 {toRemoveMethods.Count} 个信号";
            }
        }

        /// <summary>
        /// 将单个 CAN 信号添加为监测通道。
        /// </summary>
        private void AddCanChannel(CANSignalConfig cfg, string fingerprint, string methodName, bool isDisplayed)
        {
            var color = _palette[_colorIndex % _palette.Length];
            _colorIndex++;

            string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName);
            var channel = new MonitorChannelVM
            {
                DeviceName = $"CAN{cfg.Channel}",
                Fingerprint = fingerprint,
                MethodName = methodName,
                DisplayName = displayName,
                Color = color,
                IsMonitored = true,
                IsDisplayed = isDisplayed
            };

            if (channel.IsDisplayed)
            {
                channel.Series = new LineSeries
                {
                    Title = channel.DisplayName,
                    Color = color,
                    StrokeThickness = 1.5
                };
                Plot.Series.Add(channel.Series);
            }

            channel.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
                    OnChannelDisplayChanged(channel);
            };

            Channels.Add(channel);
        }
        #endregion

        #region 命令处理
        private void OnAddChannel()
        {
            var method = SelectedAvailableMethod;
            if (method == null)
            {
                StatusMessage = "请先在列表中选择一个监测项";
                return;
            }

            // 以指纹+方法名去重，防止同一物理设备重复添加
            if (Channels.Any(c => c.Fingerprint == method.Fingerprint && c.MethodName == method.MethodName))
            {
                StatusMessage = $"监测项 [{method.DisplayName}] 已存在";
                return;
            }

            var color = _palette[_colorIndex % _palette.Length];
            _colorIndex++;

            var channel = new MonitorChannelVM
            {
                DeviceName = method.DeviceName,
                Fingerprint = method.Fingerprint,
                MethodName = method.MethodName,
                DisplayName = method.DisplayName,
                Color = color,
                IsMonitored = true,
                IsDisplayed = true
            };

            channel.Series = new LineSeries
            {
                Title = channel.DisplayName,
                Color = color,
                StrokeThickness = 1.5
            };
            Plot.Series.Add(channel.Series);

            channel.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(MonitorChannelVM.IsDisplayed))
                    OnChannelDisplayChanged(channel);
            };

            Channels.Add(channel);
            SelectedChannel = channel;

            Plot.InvalidatePlot(true);
            StatusMessage = $"已添加监测项 [{channel.DisplayName}]，当前共 {Channels.Count} 项";
        }

        private void OnDeleteChannel()
        {
            var target = SelectedChannel ?? Channels.LastOrDefault();
            if (target == null)
            {
                StatusMessage = "无可删除的监测项";
                return;
            }

            if (target.Series != null) Plot.Series.Remove(target.Series);
            Channels.Remove(target);
            SelectedChannel = Channels.LastOrDefault();

            Plot.InvalidatePlot(true);
            StatusMessage = $"已删除监测项 [{target.DisplayName}]，剩余 {Channels.Count} 项";
        }

        private void OnResetView()
        {
            Plot.ResetAllAxes();
            Plot.InvalidatePlot(false);
            StatusMessage = "视图已按数据范围复原";
        }

        private void OnRefreshData()
        {
            Plot.InvalidatePlot(true);
            StatusMessage = $"已刷新（{DateTime.Now:HH:mm:ss}）";
        }

        private void OnExpand()
        {
            if (string.IsNullOrEmpty(TestStatus)) return;
            _globalInfo.CurrentScope = TestStatus;
            _eventAggregator.GetEvent<ExpandViewEvent>().Publish(TestStatus);
        }
        #endregion

        #region 显示/隐藏切换与数学变换
        public void OnChannelDisplayChanged(MonitorChannelVM channel)
        {
            if (channel.IsDisplayed)
            {
                if (channel.Series == null)
                {
                    channel.Series = new LineSeries
                    {
                        Title = channel.DisplayName,
                        Color = channel.Color,
                        StrokeThickness = 1.5
                    };

                    var recent = channel.DataPoints.ToArray();
                    var startIdx = Math.Max(0, recent.Length - 10000);
                    for (int i = startIdx; i < recent.Length; i++)
                        channel.Series.Points.Add(new DataPoint(recent[i].Time.ToOADate(), recent[i].DisplayValue));

                    Plot.Series.Add(channel.Series);
                    Plot.InvalidatePlot(true);
                }
            }
            else
            {
                if (channel.Series != null)
                {
                    Plot.Series.Remove(channel.Series);
                    channel.Series = null;
                    Plot.InvalidatePlot(true);
                }
            }
        }

        public void OnChannelMathChanged(MonitorChannelVM channel)
        {
            if (channel.Series == null) return;
            var points = channel.DataPoints.ToArray();
            channel.Series.Points.Clear();
            var startIdx = Math.Max(0, points.Length - 10000);
            for (int i = startIdx; i < points.Length; i++)
                channel.Series.Points.Add(new DataPoint(points[i].Time.ToOADate(), points[i].DisplayValue));
            Plot.InvalidatePlot(true);
        }
        #endregion
    }
}
