using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using SmartAquaViewer.Controls;
using SmartAquaViewer.DataAnalysis;
using SmartAquaViewer.Model;

namespace SmartAquaViewer.ViewModel
{
    public sealed class FieldItem
    {
        public string? Name { get; init; }    // 바인딩 경로 키 (예: "Tank.DOValue")
        public string? Display { get; init; } // UI 표시명 (예: "DO (mg/L)")
        public Type? DataType { get; init; }  // typeof(double), typeof(DateTime) 등
        public StepFieldKind Kind { get; init; }
    }

    public class TanksByTime
    {
        public DateTime RecordedTime { get; }
        public List<WaterTank> Tanks { get; } = new();

        public TanksByTime(DateTime time, List<WaterTank> tanks)
        {
            RecordedTime = time;
            Tanks = tanks;
        }
    }

    public class MonitoringViewModel : INotifyPropertyChanged
    {
        #region Properties
        public GraphControlViewModel GraphControlVM { get; } = new GraphControlViewModel();

        public ObservableCollection<GraphType> GraphTypes { get; }

        public ReadOnlyObservableCollection<WaterQualityVO> WaterQualityList { get; }
        
        public Dictionary<int, ObservableCollection<WaterQualityVO>> TankGroups { get; set; }
        public Dictionary<int, ObservableCollection<WaterQualityVO>> SelectedWaterTanks { get; } = new();

        public ObservableCollection<TanksByTime> TanksByTimes { get; } = new();

        private MonitorTab _selectedTab;
        public MonitorTab SelectedTab
        {
            get => _selectedTab;
            set
            {
                if (_selectedTab != value)
                {
                    _selectedTab = value;
                    OnPropertyChanged();

                    RebuildAvailableFields();   // 탭에 맞춰 필드 목록 재구성
                    SetGraphType();
                    OnPropertyChanged(nameof(IsTankAndLine));

                    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        RebuildFieldCandidates();
                    }), DispatcherPriority.Background);

                    OnSystemChanged?.Invoke(SelectedTab);
                }
            }
        }

        private int _selectedGraphIndex = -1;
        public int SelectedGraphIndex
        {
            get => _selectedGraphIndex;
            set
            {
                if (_selectedGraphIndex != value)
                {
                    _selectedGraphIndex = value;
                    OnPropertyChanged();
                    // 인덱스가 바뀌면 enum도 맞춰준다
                    if (value >= 0 && value < GraphTypes.Count)
                        SelectedGraphType = GraphTypes[value];
                }
            }
        }

        private GraphType _selectedGraphType;
        public GraphType SelectedGraphType
        {
            get => _selectedGraphType;
            set
            {
                if (_selectedGraphType != value)
                {
                    _selectedGraphType = value;
                    OnPropertyChanged();
                    OnPropertyChanged(nameof(ShowXSelector));
                    RebuildFieldCandidates();

                    var idx = GraphTypes.IndexOf(value);
                    if (idx != -1 && idx != _selectedGraphIndex)
                    {
                        _selectedGraphIndex = idx;
                        OnPropertyChanged(nameof(SelectedGraphIndex));
                        OnPropertyChanged(nameof(IsTankAndLine));
                    }
                }
            }
        }

        private StepFieldKind _selectedKind = StepFieldKind.Sensor; // 기본값은 센서
        public StepFieldKind SelectedKind
        {
            get => _selectedKind;
            set
            {
                if (_selectedKind != value)
                {
                    _selectedKind = value;
                    OnPropertyChanged();
                    // 라디오 변경 시 Y 후보 재구성
                    RebuildFieldCandidates();
                }
            }
        }

        public bool IsTankAndLine
        {
            get => SelectedTab.Equals(MonitorTab.Tank) && SelectedGraphType.Equals(GraphType.LINE);
        }

        private bool _isOpenMode;
        public bool IsOpenMode
        {
            get => _isOpenMode;
            set
            {
                if (_isOpenMode != value)
                {
                    _isOpenMode = value;
                    OnPropertyChanged();

                    BtnVisibilityDown = _isOpenMode ? Visibility.Visible : Visibility.Collapsed;
                    BtnVisibilityUp = _isOpenMode ? Visibility.Collapsed : Visibility.Visible;
                }
            }
        }

        private Visibility _btnVisibilityDown;
        public Visibility BtnVisibilityDown
        {
            get => _btnVisibilityDown;
            set
            {
                if (_btnVisibilityDown != value)
                {
                    _btnVisibilityDown = value;
                    OnPropertyChanged();
                }
            }
        }

        private Visibility _btnVisibilityUp;
        public Visibility BtnVisibilityUp
        {
            get => _btnVisibilityUp;
            set
            {
                if (_btnVisibilityUp != value)
                {
                    _btnVisibilityUp = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool ShowXSelector => SelectedGraphType == GraphType.SCATTER;

        // [필드 후보 목록] 탭/시스템에 따라 달라짐
        public ObservableCollection<FieldItem> AvailableFields { get; } = new();

        // [X축 후보/선택]
        public ObservableCollection<FieldItem> XFieldCandidates { get; } = new();
        private FieldItem? _selectedXField;
        public FieldItem? SelectedXField
        {
            get => _selectedXField;
            set { if (_selectedXField != value) { _selectedXField = value; OnPropertyChanged(); } }
        }

        // [Y축 후보/선택] — Line/Step: 다중, Scatter/Box: 단일
        public ObservableCollection<FieldItem> YFieldCandidates { get; } = new();

        // 다중 선택(Y)용
        public ObservableCollection<FieldItem> SelectedYFields { get; } = new();

        // 단일 선택(Y)용
        private FieldItem? _selectedYField;
        public FieldItem? SelectedYField
        {
            get => _selectedYField;
            set { if (_selectedYField != value) { _selectedYField = value; OnPropertyChanged(); } }
        }

        // [옵션] 예시 — 필요하면 추가
        private bool _showMarkers;                 // Line
        public bool ShowMarkers { get => _showMarkers; set { _showMarkers = value; OnPropertyChanged(); } }

        private bool _showLegends;
        public bool ShowLegends { get => _showLegends; set { _showLegends = value; OnPropertyChanged(); } }

        private bool _useSmoothing;                // Line
        public bool UseSmoothing { get => _useSmoothing; set { _useSmoothing = value; OnPropertyChanged(); } }

        private double _scatterMarkerSize = 3;     // Scatter
        public double ScatterMarkerSize { get => _scatterMarkerSize; set { _scatterMarkerSize = value; OnPropertyChanged(); } }

        private bool _showRegression;              // Scatter
        public bool ShowRegression { get => _showRegression; set { _showRegression = value; OnPropertyChanged(); } }

        private double _boxWidth = 0.3;            // Box
        public double BoxWidth { get => _boxWidth; set { _boxWidth = value; OnPropertyChanged(); } } 

        private int _boxTimeSpan = 6; // Box
        public int BoxTimeSpan
        {
            get => _boxTimeSpan;
            set
            {
                if (_boxTimeSpan != value)
                {
                    _boxTimeSpan = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion

        public ICommand ChangeDrawerStatusCommand { get; }
        public ICommand DrawGraphCommand { get; }

        public delegate void SystemChangedEventHandler(MonitorTab selectedTab);
        public event SystemChangedEventHandler OnSystemChanged;



        public MonitoringViewModel()
        {
            IsOpenMode = true;
            BtnVisibilityUp = Visibility.Collapsed;

            WaterQualityList = Datas.Instance.WaterQualityView;

            ((INotifyCollectionChanged)WaterQualityList).CollectionChanged += OnWaterQualityChanged;

            RebuildAllGroups();

            GraphTypes = [];
            SelectedTab = MonitorTab.Tank; // Default system
            SetGraphType();

            ChangeDrawerStatusCommand = new RelayCommand(_ => IsOpenMode = !IsOpenMode);
            DrawGraphCommand = new RelayCommand(DrawGraph);

            RebuildAvailableFields();
            RebuildFieldCandidates();
        }

        private void OnWaterQualityChanged(object? sender, NotifyCollectionChangedEventArgs e)
        {
            RebuildAllGroups(); // 변경될 때마다 그룹 재구성
        }

        private void RebuildAllGroups()
        {
            var grouped = WaterQualityList
                .GroupBy(x => x.Tank.Number) // 또는 x.Tank.Num
                .OrderBy(g => g.Key)
                .ToDictionary(
                    g => g.Key,
                    g => new ObservableCollection<WaterQualityVO>(
                             g.OrderBy(r => r.RecordedTime))
                );

            TankGroups = grouped;
            OnPropertyChanged(nameof(TankGroups));

            // TankGroups를 깔끔하게 다시 구성
            TanksByTimes.Clear();
            foreach (var w in WaterQualityList)
            {
                var tbt = new TanksByTime(w.RecordedTime, w.Tanks);
                TanksByTimes.Add(tbt);
                OrderTanksBtTimeByDate();
            }
        }

        private void OrderTanksBtTimeByDate()
        {
            var ordered = TanksByTimes.OrderBy(t => t.RecordedTime).ToList();
            TanksByTimes.Clear();
            foreach (var t in ordered) TanksByTimes.Add(t);
        }

        private void DrawGraph(object obj)
        {
            switch (SelectedGraphType)
            {
                case GraphType.LINE:
                    if (SelectedTab.Equals(MonitorTab.Tank)) 
                        SetGraphData_Line_Tank();
                    else 
                        GraphControlVM.SetDefaultLineGraph(
                            WaterQualityList.ToList(), SelectedTab, SelectedXField, SelectedYField, ShowMarkers);
                    break;
                case GraphType.BOX:
                    var xFieldBox = SelectedXField;
                    SetGraphData_Box_Tank();
                    break;
                case GraphType.SCATTER:
                    SetGraphData_Scatter_Tank();
                    break;
                case GraphType.STEP:
                    var xFieldStep = SelectedXField?.Name == "RecordedTime" ? SelectedXField : null;
                    var tFieldsStep = SelectedYFields;
                    var yFiledStep = SelectedYField;
                    var showMarkerStep = ShowMarkers;
                    if (SelectedKind.Equals(StepFieldKind.Status))
                        GraphControlVM.SetStatusSeriesStopPlot(WaterQualityList.ToList(), SelectedYField, SelectedYFields, ShowMarkers, ShowLegends);
                    else
                        GraphControlVM.SetStepPlot(WaterQualityList.ToList(), SelectedTab, xFieldStep, SelectedYFields, ShowMarkers, ShowLegends);
                    break;
                default:
                    break;
            }
        }

        private void SetGraphData_Line_Tank()
        {
            if (SelectedTab != MonitorTab.Tank) return;
            if (SelectedYField == null) return;

            var keys = SelectedWaterTanks.Keys.ToList();

            GraphControlVM.SetTankLineGraph(WaterQualityList, keys, SelectedXField, SelectedYField, ShowMarkers, ShowLegends);
        }

        private void SetGraphData_Box_Tank()
        {
            if (SelectedTab != MonitorTab.Tank) return;
            if (SelectedYField == null) return;

            var boxTimeSpan = TimeSpan.FromHours(BoxTimeSpan);

            var keys = SelectedWaterTanks.Keys.ToList();

            GraphControlVM.SetBoxPlot(WaterQualityList, keys, SelectedXField!, SelectedYField, BoxWidth, boxTimeSpan);
        }

        private void SetGraphData_Scatter_Tank()
        {
            if (SelectedTab != MonitorTab.Tank) return;
            if (SelectedYField == null) return;

            var keys = SelectedWaterTanks.Keys.ToList();

            GraphControlVM.SetScatterPlot(WaterQualityList, SelectedXField!, SelectedYField, keys, ScatterMarkerSize, ShowRegression, ShowLegends);
        }

        private void SetGraphType()
        {
            GraphTypes.Clear();

            switch (SelectedTab)
            {
                case MonitorTab.Tank:
                    GraphTypes.Add(GraphType.LINE);
                    GraphTypes.Add(GraphType.BOX);
                    GraphTypes.Add(GraphType.SCATTER);
                    break;
                case MonitorTab.Filter:
                case MonitorTab.Sterilizer:
                    GraphTypes.Add(GraphType.LINE);
                    GraphTypes.Add(GraphType.STEP);
                    break;
                default:
                    break;
            }

            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                SelectedGraphIndex = -1;
                SelectedGraphIndex = GraphTypes.Count > 0 ? 0 : -1;
            }), DispatcherPriority.Background);
        }

        private void RebuildAvailableFields()
        {
            AvailableFields.Clear();

            // 공통 시간
            AvailableFields.Add(new FieldItem { Name = "RecordedTime", Display = "시간", DataType = typeof(DateTime), Kind = StepFieldKind.Time });

            if (SelectedTab == MonitorTab.Tank)
            {
                AvailableFields.Add(new FieldItem { Name = "Number", Display = "수조", DataType = typeof(int), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "DOValue", Display = "DO (mg/L)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "PH", Display = "pH", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "ORP", Display = "ORP (mV)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Temperature", Display = "온도 (℃)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "FlowRate", Display = "유량 (m³/s)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
            }
            else if (SelectedTab == MonitorTab.Filter)
            {
                AvailableFields.Add(new FieldItem { Name = "Filtering.SumpPH", Display = "섬프 pH", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.SumpORP", Display = "섬프 ORP (mV)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.SumpWaterLevel", Display = "섬프 수위 (m)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.SumpFlowRate", Display = "섬프 유량 (m³/s)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.SumpTemperature", Display = "섬프 수온 (°C)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.FlowRate", Display = "순환펌프 유량", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Filtering.HeatPumpTemperature", Display = "히트펌프 온도", DataType = typeof(double), Kind = StepFieldKind.Sensor });

                AvailableFields.Add(new FieldItem { Name = "Filtering.SandFilterPower", Display = "모래여과기 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Filtering.CirculationPumpPower", Display = "순환펌프 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Filtering.InverterControllerStatus", Display = "인버터 제어기 상태", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Filtering.HeatPumpPower", Display = "히트펌프 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Filtering.AirBlowerPower", Display = "에어브로와 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
            }
            else // Sterilizer
            {
                AvailableFields.Add(new FieldItem { Name = "Sterilizing.OzoneDissolverPressure", Display = "용해장치 압력 (kPa)", DataType = typeof(double), Kind = StepFieldKind.Sensor });
                AvailableFields.Add(new FieldItem { Name = "Sterilizing.OzoneGeneratorPower", Display = "오존 발생기 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Sterilizing.OzoneDissolverPower", Display = "오존용해장치 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AvailableFields.Add(new FieldItem { Name = "Sterilizing.ExcessOzoneDestroyerPower", Display = "배오존장치 전원", DataType = typeof(int), Kind = StepFieldKind.Status });
                AddUvPowerFieldsPerId(WaterQualityList);
            }
        }

        // rows: 현재 그리려는 데이터(필터링/정렬 반영된)
        private void AddUvPowerFieldsPerId(IEnumerable<WaterQualityVO> rows)
        {
            // 케이스 A: 한 행이 UV 한 대의 상태를 담는 스키마
            var idsA = rows
                .Select(r => r?.Sterilizing?.UVSterilizerId)
                .Where(id => !string.IsNullOrWhiteSpace(id))
                .Distinct()
                .ToList();

            foreach (var id in idsA)
            {
                // Name 규칙: "Sterilizing.UVSterilizerPower[id=XXX]"
                AvailableFields.Add(new FieldItem
                {
                    Name = $"Sterilizing.UVSterilizerPower[id={id}]",
                    Display = $"자외선 살균기 {id} 전원",
                    DataType = typeof(int),
                    Kind = StepFieldKind.Status
                });
            }
        }


        // 그래프 타입이 바뀔 때 후보/기본 선택 재구성
        private void RebuildFieldCandidates()
        {
            // 후보 초기화
            XFieldCandidates.Clear();
            YFieldCandidates.Clear();

            // X축: 시간 우선
            foreach (var f in AvailableFields)
            {
                if ((SelectedGraphType == GraphType.LINE 
                    || SelectedGraphType == GraphType.STEP 
                    || SelectedGraphType == GraphType.SCATTER) 
                    && f.Name!.Equals("Number")) continue;
                if (SelectedGraphType == GraphType.LINE 
                    && (f.Kind.Equals(StepFieldKind.Status) 
                    || f.Name!.Equals("Filtering.InverterControllerStatus"))) continue;
                XFieldCandidates.Add(f);
                if (SelectedGraphType == GraphType.STEP || SelectedGraphType == GraphType.BOX) break;
            }
            SelectedXField = AvailableFields.FirstOrDefault(f => f.DataType == typeof(DateTime))
                             ?? AvailableFields.FirstOrDefault();

            if (SelectedGraphType != GraphType.STEP) SelectedKind = StepFieldKind.Sensor;

            IEnumerable<FieldItem> src = AvailableFields.Where(f => f.Kind == SelectedKind);

            if (SelectedGraphType is GraphType.LINE or GraphType.SCATTER or GraphType.BOX)
            {
                // 수치형만 (LINE/SCATTER/BOX는 연속값 위주)
                src = src.Where(f => f.DataType == typeof(double));
            }
            else if (SelectedGraphType == GraphType.STEP)
            {
                // STEP은 상태 전환에 잘 맞음: int/bool 위주
                src = src.Where(f => f.DataType == typeof(int) || f.DataType == typeof(bool) || f.DataType == typeof(double));
                // (상태가 double로 들어오는 경우도 있을 수 있어 double 허용)
            }

            // Y축 후보: 수치형
            foreach (var f in src) YFieldCandidates.Add(f);

            // 기본 선택 세팅 (타입별)
            SelectedYFields.Clear();
            SelectedYField = null;

            switch (SelectedGraphType)
            {
                case GraphType.LINE:
                    SelectedYField = YFieldCandidates.FirstOrDefault();
                    ShowMarkers = false;
                    UseSmoothing = false;
                    break;
                case GraphType.STEP:
                    ShowMarkers = false;
                    UseSmoothing = false;
                    break;

                case GraphType.SCATTER:
                    SelectedYField = YFieldCandidates.FirstOrDefault();
                    ScatterMarkerSize = 3;
                    ShowRegression = false;
                    break;

                case GraphType.BOX:
                    SelectedYField = YFieldCandidates.FirstOrDefault();
                    BoxWidth = 0.3;
                    break;
            }
            OnPropertyChanged(nameof(SelectedYFields));
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string? name = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}
