namespace ProximaAnalizer.ViewModels;

internal partial class DBInfoAlarmViewModel : ObservableObject
{
    public DBInfoAlarmViewModel(DBDataHelper dBDataHelper,
            TraceData traceData,
            IRegionManager regionManager,
            IEventAggregator eventAggregator,
            IDialogService dialogService)
    {
        this._traceData = traceData;
        this._DBDataHelper = dBDataHelper;
        this._dialogService = dialogService;
        this._regionManager = regionManager;
        this._eventAggregator = eventAggregator;

        CurrentChangedQueue = new(CurrentChangedQueueHandler);

        eventAggregator.GetEvent<RefreshAlarmData>().Subscribe(InitDisplayData);
        eventAggregator.GetEvent<UpdateSelectData>().Subscribe(SelectedData);

    }
    private readonly IRegionManager _regionManager;
    private readonly IEventAggregator _eventAggregator;
    private readonly IDialogService _dialogService;
    private readonly DBDataHelper _DBDataHelper;
    private readonly TraceData _traceData;
    private readonly EventQueue<long> CurrentChangedQueue;
    private Timer? _AutoPlayTimer;
    private Pages? _FromPage;

    private void SelectedData()
    {
        this.Selected = _eventAggregator.GetEvent<UpdateSelectData>().Selected;
    }
    private void InitDisplayData()
    {
        this.RecipeSteps.Clear();
        this._regionManager.RequestNavigate("AlarmRegion", "AlarmData");

        RefreshAlarmData alarmData = _eventAggregator.GetEvent<RefreshAlarmData>();
        this._FromPage = alarmData.FromPage;

        if (_traceData.ProcessData is null)
            return;

        this.RecipeName = _traceData.ProcessData.Recipe_Name;
        if (!this._DBDataHelper.GetRecipeSteps(_traceData.ProcessData.Guid, out string reason, out List<RecipeStepData>? recipeSteps) || recipeSteps is null)
        {
            MessageBox.Show(reason, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
            return;
        }

        recipeSteps.Foreach(t => this.RecipeSteps.TryAdd(TimeRounder.IngoreMillionSeconds(t.Step_Begin_Time.Ticks), t));
        this.Start = recipeSteps.First().Step_Begin_Time;
        this.End = recipeSteps.Last().Step_End_Time;


        this._DBDataHelper.SetTimeRange(this.Start, this.End);

        if (!this._DBDataHelper.GetAlarmData(out List<EventData>? alarms) || alarms is null)
            return;
        Dictionary<DateTime, StringBuilder> alarmCache = [];
        foreach (var alarm in alarms)
        {
            if (alarm is null || string.IsNullOrEmpty(alarm.Level))
                continue;

            DateTime occorTime = TimeRounder.IngoreMillionSeconds(alarm.Occur_Time.Ticks);

            if (!alarmCache.TryGetValue(occorTime, out StringBuilder? events) || events is null)
            {
                events = new();
                alarmCache[occorTime] = events;
                events.Append(alarm.Description);
                continue;
            }

            events.Append(Environment.NewLine);
            events.Append(alarm.Description);
        }
        this.Alarms = alarmCache;

        if (alarmData.Selected is not null)
        {
            DateTime time = TimeRounder.IngoreMillionSeconds(alarmData.Selected.Occur_Time.Ticks);
            this.Current = time;
            this.CurrentLong = time.Ticks;
        }
        else
        {
            this.Current = this.Start;
            this.CurrentLong = this.Start.Ticks;
        }

    }

    [ObservableProperty]
    private RecipeStepData? _CurrentRecipeStep;

    [ObservableProperty]
    private ObservableDictionary<DateTime, RecipeStepData> _RecipeSteps = [];

    [ObservableProperty]
    private Dictionary<DateTime, StringBuilder> _Alarms = [];

    [ObservableProperty]
    private KeyValuePair<DateTime, StringBuilder>? _SelectedAlarm;
    partial void OnSelectedAlarmChanged(KeyValuePair<DateTime, StringBuilder>? value)
    {
        if (value is null)
            return;
        this.CurrentLong = value.Value.Key.Ticks;
    }

    [ObservableProperty]
    private string? _RecipeName;

    [ObservableProperty]
    private DateTime _Start;
    [ObservableProperty]
    private DateTime _End;
    [ObservableProperty]
    private DateTime _Current;

    [ObservableProperty]
    private long _CurrentLong;
    partial void OnCurrentLongChanged(long value) => this.CurrentChangedQueue.Enqueue(value);
    private void CurrentChangedQueueHandler(long value)
    {
        if (CurrentChangedQueue.Count != 0)
            return;

        DateTime time = TimeRounder.IngoreMillionSeconds(value);

        RecipeStepData? step = this.RecipeSteps.Where(t => t.Key <= this.Current).LastOrDefault().Value;
        App.Current.Dispatcher?.Invoke(() =>
        {
            if (this.Alarms.ContainsKey(time))
                this.SelectedAlarm = this.Alarms.Where(t => t.Key == time).FirstOrDefault();
            else
                this.SelectedAlarm = null;

            this.Current = time;
            this.CurrentRecipeStep = step;
        });

        DateTime startTime = time.AddMilliseconds(-500);
        DateTime endTime = time.AddMilliseconds(500);
        this._DBDataHelper.GetSystemData(startTime, endTime, out List<dynamic>? systemData);
        this._DBDataHelper.GetPMData(startTime, endTime, out List<dynamic>? pmData);
        this._eventAggregator.GetEvent<UpdateAlarmData>().PmData = pmData;
        this._eventAggregator.GetEvent<UpdateAlarmData>().SystemData = systemData;
        this._eventAggregator.GetEvent<UpdateAlarmData>().Publish();

    }


    [ObservableProperty]
    private Visibility _PlayVis = Visibility.Visible;
    [ObservableProperty]
    private Visibility _StopVis = Visibility.Collapsed;

    [RelayCommand]
    private void Play(string para)
    {
        switch (para)
        {
            case "play":
                this._AutoPlayTimer ??= new(TimerCallback, null, 0, 1000);
                this.PlayVis = Visibility.Collapsed;
                this.StopVis = Visibility.Visible;
                break;
            case "stop":
                this._AutoPlayTimer?.Dispose();
                this._AutoPlayTimer = null;
                this.PlayVis = Visibility.Visible;
                this.StopVis = Visibility.Collapsed;
                break;
            default:
                break;
        }
    }

    private void TimerCallback(object? state)
    {
        if (this.CurrentLong >= this.End.Ticks)
        {
            this.Play("stop");
            return;
        }
        App.Current.Dispatcher?.Invoke(() => this.CurrentLong += 10000000);
    }

    [RelayCommand]
    private void TimeOpera(string para)
    {
        this.Play("stop");
        switch (para)
        {
            case "+":
                this.CurrentLong += 10000000;
                break;
            case "-":
                this.CurrentLong -= 10000000;
                break;
            case "++":
                this.CurrentLong += 600000000;
                break;
            case "--":
                this.CurrentLong -= 600000000;
                break;
            case "---":
                {
                    DateTime alarm = this.Alarms.Where(t => t.Key < this.Current).LastOrDefault().Key;
                    if (alarm == DateTime.MinValue)
                        break;
                    this.CurrentLong = alarm.Ticks;
                }
                break;
            case "+++":
                {
                    DateTime alarm = this.Alarms.Where(t => t.Key > this.Current).FirstOrDefault().Key;
                    if (alarm == DateTime.MinValue)
                        break;
                    this.CurrentLong = alarm.Ticks;
                }
                break;
            default:
                break;
        }
    }

    [RelayCommand]
    private void SingleTrace()
    {
        if (this.Selected is null)
            return;

        if (_eventAggregator.GetEvent<UpdateSelectData>().KeyNames is not object keys || keys is null)
            return;

        this.Play("stop");

        IDialogParameters para = new DialogParameters
        {
            { "point", this.Selected },
            { "keys", keys },
            { "time", this.Current}

        };
        this._dialogService.ShowDialog("SingleTrace", para);
    }

    [RelayCommand]
    private void Navi(string para)
    {
        this.Play("stop");
        this.CurrentChangedQueue.Enqueue(this.CurrentLong);
        this._regionManager.RequestNavigate("AlarmRegion", para);
    }

    [RelayCommand]
    private void Return()
    {
        this.Play("stop");
        _eventAggregator.GetEvent<WindowSwitch>().Page = _FromPage is null ? Pages.RecipeStepNavi : _FromPage.Value;
        _eventAggregator.GetEvent<WindowSwitch>().Publish();
    }

    [ObservableProperty]
    private object? _Selected;
    partial void OnSelectedChanged(object? value)
    {

    }
}