using System;
using System.Collections.ObjectModel;
using System.Linq;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using XP.Common.GeneralForm.Views;
using XP.Common.Logging.Interfaces;
using XP.Hardware.Detector.Abstractions.Events;
using XP.Hardware.Detector.Abstractions.Enums;
using XP.Hardware.Detector.Config;
using XP.Hardware.Detector.Services;

namespace XP.Hardware.Detector.ViewModels
{
    /// <summary>
    /// 面阵探测器配置 ViewModel | Area detector configuration ViewModel
    /// 根据探测器类型动态加载参数选项，包含校正参数一致性校验和扫描期间 UI 锁定
    /// </summary>
    public class DetectorConfigViewModel : BindableBase
    {
        private readonly IDetectorService _detectorService;
        private readonly IEventAggregator _eventAggregator;
        private readonly ILoggerService _logger;
        private DetectorConfig _config;

        #region 连接状态 | Connection status

        private bool _isConnected;
        /// <summary>
        /// 探测器是否已连接 | Whether detector is connected
        /// </summary>
        public bool IsConnected
        {
            get => _isConnected;
            private set
            {
                if (SetProperty(ref _isConnected, value))
                    RaiseAllCommandsCanExecuteChanged();
            }
        }

        #endregion

        #region 暗场校正时记录的参数快照 | Parameter snapshot at dark correction time

        private int _darkCorrectionBinningIndex = -1;
        private int _darkCorrectionPga = -1;
        private decimal _darkCorrectionFrameRate = -1m;

        #endregion

        #region 灵敏度（PGA）| Sensitivity (PGA)

        /// <summary>
        /// PGA 可选项列表 | PGA selectable items
        /// </summary>
        public ObservableCollection<int> PgaItems { get; } = new ObservableCollection<int>();

        private int _selectedPga;
        /// <summary>
        /// 当前选中的 PGA 值 | Currently selected PGA value
        /// </summary>
        public int SelectedPga
        {
            get => _selectedPga;
            set => SetProperty(ref _selectedPga, value);
        }

        #endregion

        #region 像素合并（Binning）| Pixel binning

        /// <summary>
        /// Binning 可选项列表 | Binning selectable items
        /// </summary>
        public ObservableCollection<BinningOption> BinningItems { get; } = new ObservableCollection<BinningOption>();

        private int _selectedBinningIndex;
        /// <summary>
        /// 当前选中的 Binning 索引 | Currently selected binning index
        /// </summary>
        public int SelectedBinningIndex
        {
            get => _selectedBinningIndex;
            set
            {
                if (SetProperty(ref _selectedBinningIndex, value))
                {
                    ClampFrameRate();
                    UpdateImageSpec();
                }
            }
        }

        #endregion

        #region 帧率 | Frame rate

        private decimal _frameRate = 5m;
        /// <summary>
        /// 帧率 | Frame rate
        /// </summary>
        public decimal FrameRate
        {
            get => _frameRate;
            set
            {
                if (value < FrameRateMinimum)
                    value = FrameRateMinimum;

                var max = GetMaxFrameRate(_selectedBinningIndex);
                if (value > max)
                {
                    _logger?.Warn("帧率超出当前 Binning 模式上限 {Max}，已自动修正 | Frame rate exceeds max {Max} for current binning, auto corrected", max);
                    value = max;
                }

                SetProperty(ref _frameRate, value);
            }
        }

        /// <summary>
        /// 帧率最小值 | Frame rate minimum
        /// </summary>
        public decimal FrameRateMinimum => 0.1m;

        private decimal _frameRateMaximum = 15m;
        /// <summary>
        /// 帧率最大值（随 Binning 变化）| Frame rate maximum (varies with binning)
        /// </summary>
        public decimal FrameRateMaximum
        {
            get => _frameRateMaximum;
            private set => SetProperty(ref _frameRateMaximum, value);
        }

        #endregion

        #region 帧合并 | Average frames

        private int _avgFrames = 1;
        /// <summary>
        /// 帧合并数 | Average frame count
        /// </summary>
        public int AvgFrames
        {
            get => _avgFrames;
            set
            {
                if (value < 1) value = 1;
                SetProperty(ref _avgFrames, value);
            }
        }

        #endregion

        #region 图像规格（只读，随 Binning 变化）| Image spec (read-only, varies with binning)

        private string _imageSpecText = "";
        /// <summary>
        /// 当前 Binning 模式下的图像规格文本 | Image spec text for current binning mode
        /// </summary>
        public string ImageSpecText
        {
            get => _imageSpecText;
            private set => SetProperty(ref _imageSpecText, value);
        }

        #endregion

        #region 命令 | Commands

        public DelegateCommand DarkCorrectionCommand { get; }
        public DelegateCommand LightCorrectionCommand { get; }
        public DelegateCommand BadPixelCorrectionCommand { get; }
        public DelegateCommand ApplyParametersCommand { get; }

        #endregion

        #region 状态 | Status

        private bool _isBusy;
        /// <summary>
        /// 是否正在执行校正操作 | Whether a correction operation is in progress
        /// </summary>
        public bool IsBusy
        {
            get => _isBusy;
            private set
            {
                SetProperty(ref _isBusy, value);
                RaiseAllCommandsCanExecuteChanged();
            }
        }

        private bool _darkCorrectionDone;
        /// <summary>
        /// 暗场校正是否已完成 | Whether dark correction is done
        /// </summary>
        public bool DarkCorrectionDone
        {
            get => _darkCorrectionDone;
            private set
            {
                SetProperty(ref _darkCorrectionDone, value);
                LightCorrectionCommand.RaiseCanExecuteChanged();
            }
        }

        private bool _isParametersLocked;
        /// <summary>
        /// 参数是否被锁定（扫描采集期间禁止修改）| Whether parameters are locked (during acquisition)
        /// </summary>
        public bool IsParametersLocked
        {
            get => _isParametersLocked;
            private set
            {
                SetProperty(ref _isParametersLocked, value);
                RaisePropertyChanged(nameof(IsParametersEditable));
                RaiseAllCommandsCanExecuteChanged();
            }
        }

        /// <summary>
        /// 参数是否可编辑（已连接、未锁定且不忙）| Whether parameters are editable
        /// </summary>
        public bool IsParametersEditable => _isConnected && !_isParametersLocked && !_isBusy;

        #endregion

        #region 构造函数 | Constructor

        public DetectorConfigViewModel(
            IDetectorService detectorService,
            IEventAggregator eventAggregator,
            ILoggerService logger)
        {
            _detectorService = detectorService ?? throw new ArgumentNullException(nameof(detectorService));
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _logger = logger?.ForModule<DetectorConfigViewModel>() ?? throw new ArgumentNullException(nameof(logger));

            DarkCorrectionCommand = new DelegateCommand(ExecuteDarkCorrectionAsync, CanExecuteCorrection);
            LightCorrectionCommand = new DelegateCommand(ExecuteLightCorrectionAsync, CanExecuteLightCorrection);
            BadPixelCorrectionCommand = new DelegateCommand(ExecuteBadPixelCorrectionAsync, CanExecuteCorrection);
            ApplyParametersCommand = new DelegateCommand(ExecuteApplyParametersAsync, CanExecuteCorrection);

            // 订阅探测器状态变更事件，用于扫描期间锁定参数 | Subscribe to status changed event for parameter locking
            _eventAggregator.GetEvent<StatusChangedEvent>()
                .Subscribe(OnDetectorStatusChanged, ThreadOption.UIThread);

            // 从配置加载 UI 选项 | Load UI options from config
            LoadOptionsFromConfig();
        }

        #endregion

        #region 公共方法 | Public methods

        /// <summary>
        /// 锁定参数（外部调用，如扫描开始时）| Lock parameters (called externally, e.g. when scan starts)
        /// </summary>
        public void LockParameters()
        {
            IsParametersLocked = true;
            _logger?.Info("探测器参数已锁定 | Detector parameters locked");
        }

        /// <summary>
        /// 解锁参数（外部调用，如扫描结束时）| Unlock parameters (called externally, e.g. when scan ends)
        /// </summary>
        public void UnlockParameters()
        {
            IsParametersLocked = false;
            _logger?.Info("探测器参数已解锁 | Detector parameters unlocked");
        }

        /// <summary>
        /// 获取当前参数的图像规格（供扫描配置导出）| Get current image spec for scan config export
        /// </summary>
        public BinningImageSpec GetCurrentImageSpec()
        {
            return _config?.GetImageSpec(_selectedBinningIndex);
        }

        /// <summary>
        /// 导出当前探测器参数为字典（供扫描配置保存）| Export current parameters as dictionary
        /// </summary>
        public System.Collections.Generic.Dictionary<string, string> ExportParameters()
        {
            var dict = new System.Collections.Generic.Dictionary<string, string>();
            var spec = GetCurrentImageSpec();

            var binningName = _selectedBinningIndex < BinningItems.Count
                ? BinningItems[_selectedBinningIndex].DisplayName : "1×1";

            dict["Det_Binning"] = binningName;
            dict["Det_PGA"] = _selectedPga.ToString();
            dict["Det_Frame_rate"] = _frameRate.ToString(System.Globalization.CultureInfo.InvariantCulture);
            dict["Det_Avg_Frames"] = _avgFrames.ToString();

            if (spec != null)
            {
                dict["Pixel_X"] = spec.PixelX.ToString(System.Globalization.CultureInfo.InvariantCulture);
                dict["Pixel_Y"] = spec.PixelY.ToString(System.Globalization.CultureInfo.InvariantCulture);
                dict["Image_Size_Width"] = spec.ImageWidth.ToString();
                dict["Image_Size_Height"] = spec.ImageHeight.ToString();
            }

            return dict;
        }

        #endregion

        #region 私有方法 | Private methods

        /// <summary>
        /// 从探测器配置加载下拉框选项 | Load combo box options from detector config
        /// </summary>
        private void LoadOptionsFromConfig()
        {
            _config = _detectorService.GetCurrentConfig();

            // 加载 Binning 选项 | Load binning options
            BinningItems.Clear();
            var binnings = _config?.GetSupportedBinnings();
            if (binnings != null && binnings.Count > 0)
            {
                foreach (var b in binnings) BinningItems.Add(b);
            }
            else
            {
                BinningItems.Add(new BinningOption("1×1", 0));
                BinningItems.Add(new BinningOption("2×2", 1));
            }

            // 加载 PGA 选项 | Load PGA options
            PgaItems.Clear();
            var pgas = _config?.GetSupportedPgaValues();
            if (pgas != null && pgas.Count > 0)
            {
                foreach (var p in pgas) PgaItems.Add(p);
            }
            else
            {
                foreach (var p in new[] { 2, 3, 4, 5, 6, 7 }) PgaItems.Add(p);
            }

            // 尝试从持久化配置恢复参数 | Try to restore parameters from saved config
            var saved = ConfigLoader.LoadSavedParameters();
            if (saved.HasValue)
            {
                var (binIdx, pga, fr, avg) = saved.Value;
                SelectedBinningIndex = binIdx < BinningItems.Count ? binIdx : 0;
                SelectedPga = PgaItems.Contains(pga) ? pga : PgaItems.FirstOrDefault();
                FrameRate = fr > 0 ? fr : 5m;
                AvgFrames = avg > 0 ? avg : 1;
                _logger?.Info("从持久化配置恢复参数，Binning={Binning}，PGA={PGA}，帧率={FrameRate}，帧合并={AvgFrames} | Restored parameters from saved config",
                    binIdx, pga, fr, avg);
            }
            else
            {
                SelectedBinningIndex = 0;
                SelectedPga = PgaItems.FirstOrDefault();
            }

            // 初始化帧率上限 | Initialize frame rate maximum
            FrameRateMaximum = GetMaxFrameRate(_selectedBinningIndex);
            UpdateImageSpec();

            _logger?.Info("探测器配置选项已加载，类型={Type}，Binning选项数={BinCount}，PGA选项数={PgaCount} | Detector config options loaded",
                _config?.Type.ToString() ?? "未知", BinningItems.Count, PgaItems.Count);
        }

        /// <summary>
        /// 获取最大帧率（优先从配置获取）| Get max frame rate (prefer from config)
        /// </summary>
        private decimal GetMaxFrameRate(int binningIndex)
        {
            return _config?.GetMaxFrameRate(binningIndex) ?? 15m;
        }

        private void ClampFrameRate()
        {
            var max = GetMaxFrameRate(_selectedBinningIndex);
            FrameRateMaximum = max;
            if (_frameRate > max)
            {
                _logger?.Warn("Binning 变更，帧率已从 {Old} 调整为上限 {Max} | Binning changed, frame rate adjusted from {Old} to max {Max}", _frameRate, max);
                FrameRate = max;
            }
        }

        /// <summary>
        /// 更新图像规格显示文本 | Update image spec display text
        /// </summary>
        private void UpdateImageSpec()
        {
            var spec = _config?.GetImageSpec(_selectedBinningIndex);
            if (spec != null)
            {
                ImageSpecText = $"{spec.ImageWidth}×{spec.ImageHeight}  像素尺寸 {spec.PixelX}×{spec.PixelY} mm";
            }
            else
            {
                ImageSpecText = "";
            }
        }

        /// <summary>
        /// 探测器状态变更回调，用于扫描期间自动锁定/解锁参数 | Detector status changed callback
        /// </summary>
        private void OnDetectorStatusChanged(DetectorStatus status)
        {
            // 同步连接状态：非 Uninitialized 即视为已连接 | Sync connection status: connected if not Uninitialized
            IsConnected = status != DetectorStatus.Uninitialized;

            if (status == DetectorStatus.Acquiring)
            {
                IsParametersLocked = true;
                _logger?.Debug("探测器进入采集状态，参数已自动锁定 | Detector acquiring, parameters auto-locked");
            }
            else if (status == DetectorStatus.Ready)
            {
                IsParametersLocked = false;
                _logger?.Debug("探测器就绪，参数已自动解锁 | Detector ready, parameters auto-unlocked");
            }
        }

        /// <summary>
        /// 校验亮场校正前参数是否与暗场校正时一致 | Validate parameters consistency before light correction
        /// </summary>
        /// <returns>参数是否一致 | Whether parameters are consistent</returns>
        private bool ValidateCorrectionParametersConsistency()
        {
            if (_darkCorrectionBinningIndex < 0)
            {
                _logger?.Warn("未执行暗场校正，无法进行亮场校正 | Dark correction not done, cannot perform light correction");
                return false;
            }

            if (_selectedBinningIndex != _darkCorrectionBinningIndex ||
                _selectedPga != _darkCorrectionPga ||
                _frameRate != _darkCorrectionFrameRate)
            {
                _logger?.Warn("暗场校正与亮场校正参数不一致：暗场 Binning={DarkBin}，PGA={DarkPga}，帧率={DarkFr}；当前 Binning={CurBin}，PGA={CurPga}，帧率={CurFr} | Parameter mismatch between dark and light correction",
                    _darkCorrectionBinningIndex, _darkCorrectionPga, _darkCorrectionFrameRate,
                    _selectedBinningIndex, _selectedPga, _frameRate);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 记录暗场校正时的参数快照 | Record parameter snapshot at dark correction time
        /// </summary>
        private void RecordDarkCorrectionParameters()
        {
            _darkCorrectionBinningIndex = _selectedBinningIndex;
            _darkCorrectionPga = _selectedPga;
            _darkCorrectionFrameRate = _frameRate;
        }

        #endregion

        #region 命令执行 | Command execution

        private bool CanExecuteCorrection() => _isConnected && !_isBusy && !_isParametersLocked;
        private bool CanExecuteLightCorrection() => _isConnected && !_isBusy && !_isParametersLocked && _darkCorrectionDone;

        private void RaiseAllCommandsCanExecuteChanged()
        {
            DarkCorrectionCommand.RaiseCanExecuteChanged();
            LightCorrectionCommand.RaiseCanExecuteChanged();
            BadPixelCorrectionCommand.RaiseCanExecuteChanged();
            ApplyParametersCommand.RaiseCanExecuteChanged();
            RaisePropertyChanged(nameof(IsParametersEditable));
        }

        /// <summary>
        /// 执行暗场校正 | Execute dark correction
        /// </summary>
        private async void ExecuteDarkCorrectionAsync()
        {
            var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
            _logger?.Info("开始暗场校正，Binning={Binning}，PGA={PGA}，帧率={FrameRate} | Starting dark correction",
                binningName, _selectedPga, _frameRate);

            // 显示进度条窗口 | Show progress window
            var progressWindow = new ProgressWindow(
                title: "暗场校正 | Dark Correction",
                message: "正在应用参数... | Applying parameters...",
                isCancelable: false,
                logger: _logger);
            progressWindow.Show();

            IsBusy = true;
            try
            {
                // 1. 应用参数到硬件 | Apply parameters to hardware
                progressWindow.UpdateProgress("正在应用参数... | Applying parameters...", 10);
                var applyResult = await _detectorService.ApplyParametersAsync(_selectedBinningIndex, _selectedPga, _frameRate);
                if (!applyResult.IsSuccess)
                {
                    _logger?.Error(applyResult.Exception, "应用参数失败，暗场校正中止：{Message} | Apply parameters failed, dark correction aborted: {Message}", applyResult.ErrorMessage);
                    return;
                }

                // 2. 执行暗场校正 | Execute dark correction
                progressWindow.UpdateProgress("正在采集暗场数据... | Acquiring dark field data...", 30);
                var result = await _detectorService.DarkCorrectionAsync(_avgFrames);

                if (result.IsSuccess)
                {
                    progressWindow.UpdateProgress("暗场校正完成 | Dark correction completed", 100);
                    RecordDarkCorrectionParameters();
                    DarkCorrectionDone = true;
                    _logger?.Info("暗场校正完成 | Dark correction completed");
                    _detectorService.SaveParameters(_selectedBinningIndex, _selectedPga, _frameRate, _avgFrames);
                }
                else
                {
                    _logger?.Error(result.Exception, "暗场校正失败：{Message} | Dark correction failed: {Message}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger?.Error(ex, "暗场校正异常：{Message} | Dark correction exception: {Message}", ex.Message);
            }
            finally
            {
                IsBusy = false;
                progressWindow.Close();
            }
        }

        /// <summary>
        /// 执行亮场校正（含参数一致性校验）| Execute light correction (with parameter consistency check)
        /// </summary>
        private async void ExecuteLightCorrectionAsync()
        {
            // 校验参数一致性 | Validate parameter consistency
            if (!ValidateCorrectionParametersConsistency())
            {
                _logger?.Warn("暗场校正与亮场校正参数不一致，请重新进行暗场校正 | Parameter mismatch, please redo dark correction");
                DarkCorrectionDone = false;
                return;
            }

            var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
            _logger?.Info("开始亮场校正，Binning={Binning}，PGA={PGA}，帧率={FrameRate} | Starting light correction",
                binningName, _selectedPga, _frameRate);

            // 显示进度条窗口 | Show progress window
            var progressWindow = new ProgressWindow(
                title: "亮场校正 | Light Correction",
                message: "正在采集亮场数据... | Acquiring light field data...",
                isCancelable: false,
                logger: _logger);
            progressWindow.Show();

            IsBusy = true;
            try
            {
                progressWindow.UpdateProgress("正在采集亮场数据... | Acquiring light field data...", 30);
                var result = await _detectorService.GainCorrectionAsync(_avgFrames);

                if (result.IsSuccess)
                {
                    progressWindow.UpdateProgress("亮场校正完成 | Light correction completed", 100);
                    _logger?.Info("亮场校正完成 | Light correction completed");
                }
                else
                {
                    _logger?.Error(result.Exception, "亮场校正失败：{Message} | Light correction failed: {Message}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger?.Error(ex, "亮场校正异常：{Message} | Light correction exception: {Message}", ex.Message);
            }
            finally
            {
                IsBusy = false;
                progressWindow.Close();
            }
        }

        /// <summary>
        /// 执行坏像素校正 | Execute bad pixel correction
        /// </summary>
        private async void ExecuteBadPixelCorrectionAsync()
        {
            _logger?.Info("开始坏像素校正 | Starting bad pixel correction");

            // 显示进度条窗口 | Show progress window
            var progressWindow = new ProgressWindow(
                title: "坏像素校正 | Bad Pixel Correction",
                message: "正在检测坏像素... | Detecting bad pixels...",
                isCancelable: false,
                logger: _logger);
            progressWindow.Show();

            IsBusy = true;
            try
            {
                progressWindow.UpdateProgress("正在检测坏像素... | Detecting bad pixels...", 30);
                var result = await _detectorService.BadPixelCorrectionAsync();

                if (result.IsSuccess)
                {
                    progressWindow.UpdateProgress("坏像素校正完成 | Bad pixel correction completed", 100);
                    _logger?.Info("坏像素校正完成 | Bad pixel correction completed");
                }
                else
                {
                    _logger?.Error(result.Exception, "坏像素校正失败：{Message} | Bad pixel correction failed: {Message}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger?.Error(ex, "坏像素校正异常：{Message} | Bad pixel correction exception: {Message}", ex.Message);
            }
            finally
            {
                IsBusy = false;
                progressWindow.Close();
            }
        }

        /// <summary>
        /// 应用参数到探测器硬件 | Apply parameters to detector hardware
        /// </summary>
        private async void ExecuteApplyParametersAsync()
        {
            var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
            _logger?.Info("应用探测器参数，Binning={Binning}，PGA={PGA}，帧率={FrameRate} | Applying detector parameters",
                binningName, _selectedPga, _frameRate);

            IsBusy = true;
            try
            {
                var result = await _detectorService.ApplyParametersAsync(_selectedBinningIndex, _selectedPga, _frameRate);
                if (result.IsSuccess)
                {
                    _logger?.Info("参数应用成功 | Parameters applied successfully");

                    // 持久化参数 | Persist parameters
                    _detectorService.SaveParameters(_selectedBinningIndex, _selectedPga, _frameRate, _avgFrames);
                }
                else
                {
                    _logger?.Error(result.Exception, "参数应用失败：{Message} | Parameters apply failed: {Message}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger?.Error(ex, "参数应用异常：{Message} | Parameters apply exception: {Message}", ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }

        #endregion
    }
}
