using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using OpenCvSharp;
using VisionBuilder.UI.Avalonia.Services;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.Common.ViewModel.Classes;

namespace VisionBuilder.UI.Avalonia.Views;

public partial class SingleCameraView : UserControl
{
    private SingleCameraVM? _vm;
    private ObservableCollection<ErrorData>? _subscribedErrors;
    private readonly Dictionary<string, (TextBlock label, TextBlock value)> _statsFields = new();
    private readonly Dictionary<string, Control> _dynamicButtonControls = new();

    private const string StartedAtLabel = "Started at";
    private const string ProcessingTimeLabel = "Processing time";
    private const string BadLabel = "Bad";
    private const string TotalLabel = "Total";
    private const string ErrorRateLabel = "Error rate";
    private const string DetailsLabel = "Details";

    public SingleCameraView()
    {
        InitializeComponent();
    }

    public void SetViewModel(SingleCameraVM singleCameraVm)
    {
        _vm = singleCameraVm;
        _vm.SynchronizationContext = SynchronizationContext.Current;

        DataContext = _vm;

        // Bind commands
        BtnSelectRecipe.Click += async (_, _) => await _vm.SelectRecipe();
        BtnStart.Click += (_, _) => _vm.StartCommand.Execute(null);
        BtnStop.Click += (_, _) => _vm.StopCommand.Execute(null);
        BtnPause.Click += (_, _) => _vm.PauseCommand.Execute(null);
        BtnResume.Click += (_, _) => _vm.ResumeCommand.Execute(null);
        BtnHotReload.Click += (_, _) => _vm.HotReloadCommand.Execute(null);

        // Subscribe to VM property changes
        _vm.PropertyChanged += Vm_PropertyChanged;
        _vm.PreviewVm.PropertyChanged += PreviewVm_PropertyChanged;
        _vm.StatisticsVm.PropertyChanged += StatisticsVm_PropertyChanged;

        // Subscribe to errors collection
        _subscribedErrors = _vm.ErrorsVm.Errors;
        _subscribedErrors.CollectionChanged += Errors_CollectionChanged;

        // Initial UI state
        UpdateButtonVisibility();
        UpdateCameraLabel();
        InitializeStatsFields();

        // Subscribe to dynamic buttons
        _vm.DynamicButtons.CollectionChanged += DynamicButtons_CollectionChanged;
        foreach (var btn in _vm.DynamicButtons)
            AddDynamicButton(btn);
    }

    private void Vm_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        Dispatcher.UIThread.Post(() =>
        {
            switch (e.PropertyName)
            {
                case nameof(SingleCameraVM.CameraLabelAndStatus):
                    UpdateCameraLabel();
                    break;
                case nameof(SingleCameraVM.CurrentRecipeName):
                    BtnSelectRecipe.Content = _vm!.CurrentRecipeName.ToUpperInvariant();
                    break;
                case nameof(SingleCameraVM.CanStart):
                case nameof(SingleCameraVM.CanStop):
                case nameof(SingleCameraVM.CanPause):
                case nameof(SingleCameraVM.CanResume):
                case nameof(SingleCameraVM.CanHotReload):
                case nameof(SingleCameraVM.RecipeNotSelected):
                    UpdateButtonVisibility();
                    break;
            }
        });
    }

    private void UpdateCameraLabel()
    {
        LblCameraName.Text = _vm?.CameraLabelAndStatus ?? "Camera";
    }

    private void UpdateButtonVisibility()
    {
        if (_vm == null) return;
        BtnStart.IsVisible = _vm.CanStart;
        BtnStop.IsVisible = _vm.CanStop;
        BtnPause.IsVisible = _vm.CanPause;
        BtnResume.IsVisible = _vm.CanResume;
        BtnHotReload.IsVisible = _vm.CanHotReload;
    }

    private void PreviewVm_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(PreviewVM.ImagePreview))
        {
            Dispatcher.UIThread.Post(() =>
            {
                var mat = _vm?.PreviewVm.ImagePreview;
                if (mat != null && !mat.Empty())
                {
                    PreviewImage.Source = ImageConverter.MatToAvaloniaBitmap(mat);
                }

                var isError = _vm?.PreviewVm.IsError ?? false;
                PreviewBorder.BorderThickness = isError ? new Thickness(4) : new Thickness(0);
            });
        }
    }

    #region Statistics

    private void InitializeStatsFields()
    {
        StatsPanel.Children.Clear();
        _statsFields.Clear();
        UpdateStat(StartedAtLabel, "");
        UpdateStat(ProcessingTimeLabel, "");
        UpdateStat(TotalLabel, "0");
        UpdateStat(BadLabel, "0");
        UpdateStat(ErrorRateLabel, "0");
        UpdateStat(DetailsLabel, "");
    }

    private void StatisticsVm_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (_vm == null) return;
            var stats = _vm.StatisticsVm;
            switch (e.PropertyName)
            {
                case nameof(StatisticsVM.SessionStarted):
                    InitializeStatsFields();
                    UpdateStat(StartedAtLabel, stats.SessionStarted);
                    break;
                case nameof(StatisticsVM.ProcessingTime):
                    UpdateStat(ProcessingTimeLabel, stats.ProcessingTime);
                    break;
                case nameof(StatisticsVM.Bad):
                    UpdateStat(BadLabel, stats.Bad.ToString());
                    break;
                case nameof(StatisticsVM.Total):
                    UpdateStat(TotalLabel, stats.Total.ToString());
                    break;
                case nameof(StatisticsVM.ErrorRate):
                    UpdateStat(ErrorRateLabel, stats.ErrorRate);
                    break;
                case nameof(StatisticsVM.StatisticsDetails):
                    UpdateStat(DetailsLabel, stats.StatisticsDetails);
                    break;
            }
        });
    }

    private void UpdateStat(string name, string text)
    {
        if (!_statsFields.ContainsKey(name))
        {
            var label = new TextBlock
            {
                Text = name + ":",
                FontWeight = FontWeight.Bold,
                FontSize = 12,
                Margin = new Thickness(0, 6, 0, 0)
            };
            var value = new TextBlock
            {
                Text = text,
                FontSize = 12,
                TextWrapping = TextWrapping.Wrap,
                Margin = new Thickness(0)
            };
            StatsPanel.Children.Add(label);
            StatsPanel.Children.Add(value);
            _statsFields[name] = (label, value);
        }
        else
        {
            _statsFields[name].value.Text = text;
        }
    }

    #endregion

    #region Errors

    private void Errors_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        Dispatcher.UIThread.Post(() =>
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewItems != null)
                    {
                        foreach (ErrorData error in e.NewItems)
                            AddErrorThumbnail(error);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldItems != null)
                    {
                        foreach (ErrorData error in e.OldItems)
                            RemoveErrorThumbnail(error);
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    ErrorsList.ItemsSource = null;
                    _errorControls.Clear();
                    break;
            }
        });
    }

    private readonly Dictionary<ErrorData, Control> _errorControls = new();

    private void AddErrorThumbnail(ErrorData errorData)
    {
        var panel = new StackPanel
        {
            Width = 183,
            Margin = new Thickness(4),
            Cursor = new Cursor(StandardCursorType.Hand)
        };

        Bitmap? thumbnail = null;
        try
        {
            if (errorData.ImageAnalysis != null && !errorData.ImageAnalysis.Empty())
            {
                thumbnail = ImageConverter.MatToAvaloniaBitmap(
                    errorData.ImageAnalysis.Resize(new OpenCvSharp.Size(183, 138)));
            }
        }
        catch
        {
            // Ignore conversion errors
        }

        var image = new Image
        {
            Source = thumbnail,
            Height = 138,
            Stretch = Stretch.Uniform
        };

        var text = new TextBlock
        {
            Text = errorData.Title,
            FontSize = 11,
            HorizontalAlignment = global::Avalonia.Layout.HorizontalAlignment.Center,
            TextTrimming = TextTrimming.CharacterEllipsis
        };

        panel.Children.Add(image);
        panel.Children.Add(text);

        panel.PointerPressed += (_, _) =>
        {
            _vm?.ErrorsVm.ShowImage(errorData);
        };

        _errorControls[errorData] = panel;

        // Insert at the beginning (newest first)
        if (ErrorsList.ItemsSource == null)
        {
            var items = new ObservableCollection<Control>();
            ErrorsList.ItemsSource = items;
        }

        if (ErrorsList.ItemsSource is ObservableCollection<Control> collection)
        {
            collection.Insert(0, panel);
        }
    }

    private void RemoveErrorThumbnail(ErrorData errorData)
    {
        if (_errorControls.TryGetValue(errorData, out var control))
        {
            if (ErrorsList.ItemsSource is ObservableCollection<Control> collection)
            {
                collection.Remove(control);
            }
            _errorControls.Remove(errorData);
        }
    }

    #endregion

    #region Dynamic Buttons

    private void DynamicButtons_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        Dispatcher.UIThread.Post(() =>
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (ButtonDefinition btn in e.NewItems!)
                        AddDynamicButton(btn);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (ButtonDefinition btn in e.OldItems!)
                        RemoveDynamicButton(btn);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    foreach (var ctrl in _dynamicButtonControls.Values)
                        ButtonsPanel.Children.Remove(ctrl);
                    _dynamicButtonControls.Clear();
                    break;
            }
        });
    }

    private void AddDynamicButton(ButtonDefinition buttonDef)
    {
        Control control;
        if (buttonDef.Type == EButtonType.Toggle)
        {
            var toggleBtn = new ToggleButton
            {
                Content = buttonDef.Title.ToUpperInvariant(),
                Height = 60,
                HorizontalAlignment = global::Avalonia.Layout.HorizontalAlignment.Stretch,
                HorizontalContentAlignment = global::Avalonia.Layout.HorizontalAlignment.Center,
                VerticalContentAlignment = global::Avalonia.Layout.VerticalAlignment.Center,
                FontSize = 14,
                FontWeight = FontWeight.Bold,
                IsChecked = buttonDef.IsToggled,
                IsVisible = buttonDef.IsVisible,
                BorderBrush = new SolidColorBrush(Color.FromRgb(0xD3, 0x2F, 0x2F)),
                BorderThickness = new Thickness(2),
                Background = Brushes.White,
                Foreground = new SolidColorBrush(Color.FromRgb(0xD3, 0x2F, 0x2F))
            };
            toggleBtn.Click += (_, _) => buttonDef.Execute();
            buttonDef.PropertyChanged += (_, args) =>
            {
                Dispatcher.UIThread.Post(() =>
                {
                    if (args.PropertyName == nameof(ButtonDefinition.IsVisible))
                        toggleBtn.IsVisible = buttonDef.IsVisible;
                    else if (args.PropertyName == nameof(ButtonDefinition.IsToggled))
                        toggleBtn.IsChecked = buttonDef.IsToggled;
                });
            };
            control = toggleBtn;
        }
        else
        {
            var btn = new Button
            {
                Content = buttonDef.Title.ToUpperInvariant(),
                Height = 60,
                HorizontalAlignment = global::Avalonia.Layout.HorizontalAlignment.Stretch,
                HorizontalContentAlignment = global::Avalonia.Layout.HorizontalAlignment.Center,
                VerticalContentAlignment = global::Avalonia.Layout.VerticalAlignment.Center,
                FontSize = 14,
                FontWeight = FontWeight.Bold,
                IsVisible = buttonDef.IsVisible,
                BorderBrush = new SolidColorBrush(Color.FromRgb(0xD3, 0x2F, 0x2F)),
                BorderThickness = new Thickness(2),
                Background = Brushes.White,
                Foreground = new SolidColorBrush(Color.FromRgb(0xD3, 0x2F, 0x2F))
            };
            btn.Click += (_, _) => buttonDef.Execute();
            buttonDef.PropertyChanged += (_, args) =>
            {
                Dispatcher.UIThread.Post(() =>
                {
                    if (args.PropertyName == nameof(ButtonDefinition.IsVisible))
                        btn.IsVisible = buttonDef.IsVisible;
                });
            };
            control = btn;
        }

        ButtonsPanel.Children.Add(control);
        _dynamicButtonControls[buttonDef.Key] = control;
    }

    private void RemoveDynamicButton(ButtonDefinition buttonDef)
    {
        if (_dynamicButtonControls.TryGetValue(buttonDef.Key, out var ctrl))
        {
            ButtonsPanel.Children.Remove(ctrl);
            _dynamicButtonControls.Remove(buttonDef.Key);
        }
    }

    #endregion
}
