using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;

namespace Sonex.Client.Layouts
{
    public enum ItemsAndContentSortField
    {
        Title,
        Subtitle,
        Description,
        Key,
        Id,
        IsEnabled
    }

    public sealed partial class ItemsAndContentLayout : UserControl
    {
        private const double CompactModeWidthThreshold = 680;
        private bool _isUpdatingSelectedItem;
        private bool _isCompactMode;
        private bool _isCompactDetailVisible;
        private INotifyCollectionChanged? _collectionNotifier;
        private readonly List<ItemsAndContentListItem> _trackedItems = new();

        public ItemsAndContentLayout()
        {

            InitializeComponent();

            PART_ListView.ItemsSource = FilteredItems;
            ApplyItemTemplate();

            UpdateFooterVisibility();
            UpdateToolbarVisibility();
            UpdateSortMenuState();
            UpdateButtonsState();

            AttachItemsSource();
            ApplyFilterAndRestoreSelection();

            SizeChanged += ItemsAndContentLayout_SizeChanged;
            UpdateResponsiveLayout();
        }

        public ObservableCollection<ItemsAndContentListItem> FilteredItems { get; } = new();

        public event EventHandler<SelectedItemChangedEventArgs>? SelectedItemChanged;
        public event EventHandler<SearchTextChangedEventArgs>? SearchTextChanged;
        public event RoutedEventHandler? AddClicked;
        public event RoutedEventHandler? DeleteClicked;

        public IEnumerable<ItemsAndContentListItem>? ItemsSource
        {
            get => (IEnumerable<ItemsAndContentListItem>?)GetValue(ItemsSourceProperty);
            set => SetValue(ItemsSourceProperty, value);
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register(
                nameof(ItemsSource),
                typeof(IEnumerable<ItemsAndContentListItem>),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnItemsSourceChanged));

        public ItemsAndContentListItem? SelectedItem
        {
            get => (ItemsAndContentListItem?)GetValue(SelectedItemProperty);
            set => SetValue(SelectedItemProperty, value);
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register(
                nameof(SelectedItem),
                typeof(ItemsAndContentListItem),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnSelectedItemChanged));

        public long? SelectedItemId
        {
            get => (long?)GetValue(SelectedItemIdProperty);
            set => SetValue(SelectedItemIdProperty, value);
        }

        public static readonly DependencyProperty SelectedItemIdProperty =
            DependencyProperty.Register(
                nameof(SelectedItemId),
                typeof(long?),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnSelectedItemIdChanged));

        public DataTemplate? ItemTemplate
        {
            get => (DataTemplate?)GetValue(ItemTemplateProperty);
            set => SetValue(ItemTemplateProperty, value);
        }

        public static readonly DependencyProperty ItemTemplateProperty =
            DependencyProperty.Register(
                nameof(ItemTemplate),
                typeof(DataTemplate),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnItemTemplateChanged));

        public object? DetailContent
        {
            get => GetValue(DetailContentProperty);
            set => SetValue(DetailContentProperty, value);
        }

        public static readonly DependencyProperty DetailContentProperty =
            DependencyProperty.Register(
                nameof(DetailContent),
                typeof(object),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null));


        public object? Footer
        {
            get => GetValue(FooterProperty);
            set => SetValue(FooterProperty, value);
        }

        public static readonly DependencyProperty FooterProperty =
            DependencyProperty.Register(
                nameof(Footer),
                typeof(object),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnFooterChanged));

        public object? ToolbarContent
        {
            get => GetValue(ToolbarContentProperty);
            set => SetValue(ToolbarContentProperty, value);
        }

        public static readonly DependencyProperty ToolbarContentProperty =
            DependencyProperty.Register(
                nameof(ToolbarContent),
                typeof(object),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(null, OnToolbarChanged));

        public GridLength LeftPaneWidth
        {
            get => (GridLength)GetValue(LeftPaneWidthProperty);
            set => SetValue(LeftPaneWidthProperty, value);
        }

        public static readonly DependencyProperty LeftPaneWidthProperty =
            DependencyProperty.Register(
                nameof(LeftPaneWidth),
                typeof(GridLength),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(new GridLength(320), OnLeftPaneWidthChanged));

        public string SearchText
        {
            get => (string)GetValue(SearchTextProperty);
            set => SetValue(SearchTextProperty, value);
        }

        public static readonly DependencyProperty SearchTextProperty =
            DependencyProperty.Register(
                nameof(SearchText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSearchTextChanged));

        public string SearchPlaceholder
        {
            get => (string)GetValue(SearchPlaceholderProperty);
            set => SetValue(SearchPlaceholderProperty, value);
        }

        public static readonly DependencyProperty SearchPlaceholderProperty =
            DependencyProperty.Register(
                nameof(SearchPlaceholder),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata("Search..."));

        public string AddToolTip
        {
            get => (string)GetValue(AddToolTipProperty);
            set => SetValue(AddToolTipProperty, value);
        }

        public static readonly DependencyProperty AddToolTipProperty =
            DependencyProperty.Register(
                nameof(AddToolTip),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata("Add"));

        public string DeleteToolTip
        {
            get => (string)GetValue(DeleteToolTipProperty);
            set => SetValue(DeleteToolTipProperty, value);
        }

        public static readonly DependencyProperty DeleteToolTipProperty =
            DependencyProperty.Register(
                nameof(DeleteToolTip),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata("Delete", OnDeleteToolTipChanged));

        public string SortToolTip
        {
            get => (string)GetValue(SortToolTipProperty);
            set => SetValue(SortToolTipProperty, value);
        }

        public static readonly DependencyProperty SortToolTipProperty =
            DependencyProperty.Register(
                nameof(SortToolTip),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata("Sort"));

        public string SortByTitleMenuText
        {
            get => (string)GetValue(SortByTitleMenuTextProperty);
            set => SetValue(SortByTitleMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortByTitleMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortByTitleMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortBySubtitleMenuText
        {
            get => (string)GetValue(SortBySubtitleMenuTextProperty);
            set => SetValue(SortBySubtitleMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortBySubtitleMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortBySubtitleMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortByDescriptionMenuText
        {
            get => (string)GetValue(SortByDescriptionMenuTextProperty);
            set => SetValue(SortByDescriptionMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortByDescriptionMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortByDescriptionMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortByKeyMenuText
        {
            get => (string)GetValue(SortByKeyMenuTextProperty);
            set => SetValue(SortByKeyMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortByKeyMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortByKeyMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortByIdMenuText
        {
            get => (string)GetValue(SortByIdMenuTextProperty);
            set => SetValue(SortByIdMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortByIdMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortByIdMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortByIsEnabledMenuText
        {
            get => (string)GetValue(SortByIsEnabledMenuTextProperty);
            set => SetValue(SortByIsEnabledMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortByIsEnabledMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortByIsEnabledMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public string SortDescendingMenuText
        {
            get => (string)GetValue(SortDescendingMenuTextProperty);
            set => SetValue(SortDescendingMenuTextProperty, value);
        }

        public static readonly DependencyProperty SortDescendingMenuTextProperty =
            DependencyProperty.Register(
                nameof(SortDescendingMenuText),
                typeof(string),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(string.Empty, OnSortMenuConfigurationChanged));

        public bool ShowSearch
        {
            get => (bool)GetValue(ShowSearchProperty);
            set => SetValue(ShowSearchProperty, value);
        }

        public static readonly DependencyProperty ShowSearchProperty =
            DependencyProperty.Register(
                nameof(ShowSearch),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnToolbarChanged));

        public bool ShowSortMenu
        {
            get => (bool)GetValue(ShowSortMenuProperty);
            set => SetValue(ShowSortMenuProperty, value);
        }

        public static readonly DependencyProperty ShowSortMenuProperty =
            DependencyProperty.Register(
                nameof(ShowSortMenu),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortByTitleOption
        {
            get => (bool)GetValue(ShowSortByTitleOptionProperty);
            set => SetValue(ShowSortByTitleOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortByTitleOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortByTitleOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortBySubtitleOption
        {
            get => (bool)GetValue(ShowSortBySubtitleOptionProperty);
            set => SetValue(ShowSortBySubtitleOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortBySubtitleOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortBySubtitleOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortByDescriptionOption
        {
            get => (bool)GetValue(ShowSortByDescriptionOptionProperty);
            set => SetValue(ShowSortByDescriptionOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortByDescriptionOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortByDescriptionOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortByKeyOption
        {
            get => (bool)GetValue(ShowSortByKeyOptionProperty);
            set => SetValue(ShowSortByKeyOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortByKeyOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortByKeyOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortByIdOption
        {
            get => (bool)GetValue(ShowSortByIdOptionProperty);
            set => SetValue(ShowSortByIdOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortByIdOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortByIdOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortByIsEnabledOption
        {
            get => (bool)GetValue(ShowSortByIsEnabledOptionProperty);
            set => SetValue(ShowSortByIsEnabledOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortByIsEnabledOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortByIsEnabledOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowSortDescendingOption
        {
            get => (bool)GetValue(ShowSortDescendingOptionProperty);
            set => SetValue(ShowSortDescendingOptionProperty, value);
        }

        public static readonly DependencyProperty ShowSortDescendingOptionProperty =
            DependencyProperty.Register(
                nameof(ShowSortDescendingOption),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnSortMenuConfigurationChanged));

        public bool ShowAddButton
        {
            get => (bool)GetValue(ShowAddButtonProperty);
            set => SetValue(ShowAddButtonProperty, value);
        }

        public static readonly DependencyProperty ShowAddButtonProperty =
            DependencyProperty.Register(
                nameof(ShowAddButton),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnToolbarChanged));

        public bool ShowDeleteButton
        {
            get => (bool)GetValue(ShowDeleteButtonProperty);
            set => SetValue(ShowDeleteButtonProperty, value);
        }

        public static readonly DependencyProperty ShowDeleteButtonProperty =
            DependencyProperty.Register(
                nameof(ShowDeleteButton),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnDisplayOptionsChanged));

        public ItemsAndContentSortField SortBy
        {
            get => (ItemsAndContentSortField)GetValue(SortByProperty);
            set => SetValue(SortByProperty, value);
        }

        public static readonly DependencyProperty SortByProperty =
            DependencyProperty.Register(
                nameof(SortBy),
                typeof(ItemsAndContentSortField),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(ItemsAndContentSortField.Title, OnSortOptionsChanged));

        public bool SortDescending
        {
            get => (bool)GetValue(SortDescendingProperty);
            set => SetValue(SortDescendingProperty, value);
        }

        public static readonly DependencyProperty SortDescendingProperty =
            DependencyProperty.Register(
                nameof(SortDescending),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(false, OnSortOptionsChanged));

        public bool ShowPicture
        {
            get => (bool)GetValue(ShowPictureProperty);
            set => SetValue(ShowPictureProperty, value);
        }

        public static readonly DependencyProperty ShowPictureProperty =
            DependencyProperty.Register(
                nameof(ShowPicture),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnDisplayOptionsChanged));

        public bool ShowSubtitle
        {
            get => (bool)GetValue(ShowSubtitleProperty);
            set => SetValue(ShowSubtitleProperty, value);
        }

        public static readonly DependencyProperty ShowSubtitleProperty =
            DependencyProperty.Register(
                nameof(ShowSubtitle),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnDisplayOptionsChanged));

        public bool ShowDescription
        {
            get => (bool)GetValue(ShowDescriptionProperty);
            set => SetValue(ShowDescriptionProperty, value);
        }

        public static readonly DependencyProperty ShowDescriptionProperty =
            DependencyProperty.Register(
                nameof(ShowDescription),
                typeof(bool),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnDisplayOptionsChanged));

        public double PictureSize
        {
            get => (double)GetValue(PictureSizeProperty);
            set => SetValue(PictureSizeProperty, value);
        }

        public static readonly DependencyProperty PictureSizeProperty =
            DependencyProperty.Register(
                nameof(PictureSize),
                typeof(double),
                typeof(ItemsAndContentLayout),
                new PropertyMetadata(true, OnDisplayOptionsChanged));

        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;
            control.DetachItemsSource();
            control.AttachItemsSource();
            control.ApplyFilterAndRestoreSelection();
        }

        private static void OnLeftPaneWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsAndContentLayout)d).UpdateResponsiveLayout();
        }

        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;

            if (control._isUpdatingSelectedItem)
                return;

            control.SelectItemFromExternal(e.NewValue as ItemsAndContentListItem);
        }

        private static void OnSelectedItemIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;

            if (control._isUpdatingSelectedItem)
                return;

            control.SelectItemById((long?)e.NewValue, fallbackToFirstVisible: false, scrollIntoView: true);
        }

        private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsAndContentLayout)d).ApplyItemTemplate();
        }

        private static void OnFooterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsAndContentLayout)d).UpdateFooterVisibility();
        }

        private static void OnToolbarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsAndContentLayout)d).UpdateToolbarVisibility();
        }

        private static void OnDeleteToolTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ItemsAndContentLayout)d).ApplyDeleteToolTipToAllItems();
        }

        private static void OnSortMenuConfigurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;
            control.UpdateSortMenuState();
            control.UpdateToolbarVisibility();
        }

        private static void OnSearchTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;
            var value = e.NewValue as string ?? string.Empty;

            if (control.SearchBox != null && control.SearchBox.Text != value)
                control.SearchBox.Text = value;

            control.ApplyFilterAndRestoreSelection();
        }

        private static void OnDisplayOptionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;
            control.ApplyDisplayOptionsToAllItems();
        }

        private static void OnSortOptionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ItemsAndContentLayout)d;
            control.ApplyFilterAndRestoreSelection();
            control.UpdateSortMenuState();
        }

        private void ItemsAndContentLayout_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateResponsiveLayout(e.NewSize.Width);
        }

        private void PART_ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_isUpdatingSelectedItem)
                return;

            SetSelectedItemInternal(PART_ListView.SelectedItem as ItemsAndContentListItem, updateListView: false, scrollIntoView: false);
        }

        private void PART_ListView_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
        {
            if (!_isCompactMode)
                return;

            if (PART_ListView.SelectedItem is not ItemsAndContentListItem)
                return;

            _isCompactDetailVisible = true;
            UpdateResponsiveLayout();
        }

        private void BackButton_Click(object sender, RoutedEventArgs e)
        {
            if (!_isCompactMode)
                return;

            _isCompactDetailVisible = false;
            UpdateResponsiveLayout();
        }

        private void SearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
        {
            var value = sender.Text ?? string.Empty;

            if (SearchText != value)
                SetValue(SearchTextProperty, value);
            else
                ApplyFilterAndRestoreSelection();

            SearchTextChanged?.Invoke(
                this,
                new SearchTextChangedEventArgs(SearchText, args.Reason));
        }

        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
            AddClicked?.Invoke(this, e);
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            DeleteClicked?.Invoke(this, e);
        }

        private void ItemDeleteButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is FrameworkElement frameworkElement &&
                frameworkElement.DataContext is ItemsAndContentListItem item)
            {
                SetSelectedItemInternal(item, scrollIntoView: false);
            }

            DeleteClicked?.Invoke(this, e);
        }

        private void SortByTitleMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.Title);
        }

        private void SortBySubtitleMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.Subtitle);
        }

        private void SortByDescriptionMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.Description);
        }

        private void SortByKeyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.Key);
        }

        private void SortByIdMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.Id);
        }

        private void SortByIsEnabledMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SetSortBy(ItemsAndContentSortField.IsEnabled);
        }

        private void SortDescendingMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (sender is not ToggleMenuFlyoutItem toggleItem)
                return;

            if (SortDescending != toggleItem.IsChecked)
                SetValue(SortDescendingProperty, toggleItem.IsChecked);
            else
                UpdateSortMenuState();
        }

        private void SetSortBy(ItemsAndContentSortField sortBy)
        {
            if (SortBy != sortBy)
                SetValue(SortByProperty, sortBy);
            else
                UpdateSortMenuState();
        }

        private void ApplyItemTemplate()
        {
            if (PART_ListView == null)
                return;

            PART_ListView.ItemTemplate = ItemTemplate ?? (DataTemplate)Resources["DefaultItemTemplate"];
        }

        private void AttachItemsSource()
        {
            if (ItemsSource is INotifyCollectionChanged notifier)
            {
                _collectionNotifier = notifier;
                _collectionNotifier.CollectionChanged += ItemsSource_CollectionChanged;
            }

            RebuildTrackedItems();
        }

        private void DetachItemsSource()
        {
            if (_collectionNotifier != null)
            {
                _collectionNotifier.CollectionChanged -= ItemsSource_CollectionChanged;
                _collectionNotifier = null;
            }

            foreach (var item in _trackedItems)
            {
                item.IsSelected = false;
                item.PropertyChanged -= Item_PropertyChanged;
            }

            _trackedItems.Clear();
        }

        private void RebuildTrackedItems()
        {
            foreach (var item in _trackedItems)
                item.PropertyChanged -= Item_PropertyChanged;

            _trackedItems.Clear();

            if (ItemsSource == null)
                return;

            foreach (var item in ItemsSource)
            {
                ApplyDisplayOptionsToItem(item);
                _trackedItems.Add(item);
                item.PropertyChanged += Item_PropertyChanged;
            }

            UpdateItemSelectionState(SelectedItem);
        }

        private void ItemsSource_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
        {
            RebuildTrackedItems();
            ApplyFilterAndRestoreSelection();
        }

        private void Item_PropertyChanged(object? sender, PropertyChangedEventArgs e)
        {
            if (sender is not ItemsAndContentListItem)
                return;

            if (ShouldRefreshOnItemPropertyChanged(e.PropertyName))
                ApplyFilterAndRestoreSelection();
        }

        private void ApplyDisplayOptionsToAllItems()
        {
            foreach (var item in _trackedItems)
                ApplyDisplayOptionsToItem(item);
        }

        private void ApplyDeleteToolTipToAllItems()
        {
            foreach (var item in _trackedItems)
                item.DeleteToolTip = DeleteToolTip;
        }

        private void ApplyDisplayOptionsToItem(ItemsAndContentListItem item)
        {
            item.ShowPicture = ShowPicture;
            item.ShowSubtitle = ShowSubtitle;
            item.ShowDescription = ShowDescription;
            item.ShowDeleteButton = ShowDeleteButton;
            item.PictureSize = PictureSize;
            item.DeleteToolTip = DeleteToolTip;
        }

        private void ApplyFilterAndRestoreSelection()
        {
            var source = ItemsSource ?? Enumerable.Empty<ItemsAndContentListItem>();
            var text = (SearchText ?? string.Empty).Trim();
            var selectedItemIdToRestore = SelectedItemId ?? SelectedItem?.Id;

            if (!string.IsNullOrWhiteSpace(text))
                source = source.Where(x => MatchesSearch(x, text));

            source = ApplySorting(source);

            try
            {
                _isUpdatingSelectedItem = true;
                ReplaceFilteredItems(source);
            }
            finally
            {
                _isUpdatingSelectedItem = false;
            }

            SelectItemById(selectedItemIdToRestore, fallbackToFirstVisible: true, scrollIntoView: false);
            UpdateButtonsState();
        }

        private void ReplaceFilteredItems(IEnumerable<ItemsAndContentListItem> items)
        {
            FilteredItems.Clear();

            foreach (var item in items)
                FilteredItems.Add(item);
        }

        private bool ShouldRefreshOnItemPropertyChanged(string? propertyName)
        {
            if (string.IsNullOrWhiteSpace(propertyName))
                return true;

            var isSearchPropertyChanged =
                !string.IsNullOrWhiteSpace(SearchText) &&
                IsSearchProperty(propertyName);

            var isSortPropertyChanged =
                propertyName == GetSortPropertyName(SortBy);

            return isSearchPropertyChanged || isSortPropertyChanged;
        }

        private IEnumerable<ItemsAndContentListItem> ApplySorting(IEnumerable<ItemsAndContentListItem> source)
        {
            return SortBy switch
            {
                ItemsAndContentSortField.Subtitle => SortDescending
                    ? source.OrderByDescending(x => x.Subtitle ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
                    : source.OrderBy(x => x.Subtitle ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id),
                ItemsAndContentSortField.Description => SortDescending
                    ? source.OrderByDescending(x => x.Description ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
                    : source.OrderBy(x => x.Description ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id),
                ItemsAndContentSortField.Key => SortDescending
                    ? source.OrderByDescending(x => x.Key ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
                    : source.OrderBy(x => x.Key ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id),
                ItemsAndContentSortField.Id => SortDescending
                    ? source.OrderByDescending(x => x.Id)
                    : source.OrderBy(x => x.Id),
                ItemsAndContentSortField.IsEnabled => SortDescending
                    ? source.OrderBy(x => x.IsEnabled).ThenBy(x => x.Title ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
                    : source.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Title ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id),
                _ => SortDescending
                    ? source.OrderByDescending(x => x.Title ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
                    : source.OrderBy(x => x.Title ?? string.Empty, StringComparer.CurrentCultureIgnoreCase).ThenBy(x => x.Id)
            };
        }

        private void SelectItemFromExternal(ItemsAndContentListItem? item)
        {
            if (item == null)
            {
                SetSelectedItemInternal(null);
                return;
            }

            var visibleItem = FilteredItems.FirstOrDefault(x => x.Id == item.Id);
            SetSelectedItemInternal(visibleItem, scrollIntoView: true);
        }

        private void SelectItemById(long? itemId, bool fallbackToFirstVisible, bool scrollIntoView)
        {
            if (!itemId.HasValue)
            {
                SetSelectedItemInternal(null, scrollIntoView: false);
                return;
            }

            var item = FilteredItems.FirstOrDefault(x => x.Id == itemId.Value);

            if (item == null && fallbackToFirstVisible)
                item = FilteredItems.FirstOrDefault();

            SetSelectedItemInternal(item, scrollIntoView: scrollIntoView);
        }

        private void SetSelectedItemInternal(
            ItemsAndContentListItem? item,
            bool updateListView = true,
            bool scrollIntoView = false)
        {
            var oldItem = SelectedItem;
            var oldId = SelectedItemId;
            var newId = item?.Id;

            if (oldId == newId && ReferenceEquals(oldItem, item))
            {
                if (updateListView && PART_ListView.SelectedItem != item)
                    PART_ListView.SelectedItem = item;

                if (scrollIntoView && item != null)
                    ScrollToItem(item);

                UpdateItemSelectionState(item);
                SyncCompactDetailVisibilityWithSelection(item);
                UpdateButtonsState();
                return;
            }

            try
            {
                _isUpdatingSelectedItem = true;

                SetValue(SelectedItemProperty, item);
                SetValue(SelectedItemIdProperty, newId);

                if (updateListView && PART_ListView.SelectedItem != item)
                    PART_ListView.SelectedItem = item;

                if (scrollIntoView && item != null)
                    ScrollToItem(item);

                UpdateItemSelectionState(item);
                SyncCompactDetailVisibilityWithSelection(item);
                UpdateButtonsState();
            }
            finally
            {
                _isUpdatingSelectedItem = false;
            }

            SelectedItemChanged?.Invoke(
                this,
                new SelectedItemChangedEventArgs(oldId, newId, oldItem, item));
        }

        private void UpdateItemSelectionState(ItemsAndContentListItem? selectedItem)
        {
            var selectedId = selectedItem?.Id;

            foreach (var trackedItem in _trackedItems)
                trackedItem.IsSelected = selectedId.HasValue && trackedItem.Id == selectedId.Value;
        }

        private void SyncCompactDetailVisibilityWithSelection(ItemsAndContentListItem? selectedItem)
        {
            if (!_isCompactMode)
                return;

            if (selectedItem != null || !_isCompactDetailVisible)
                return;

            _isCompactDetailVisible = false;
            UpdateResponsiveLayout();
        }

        private void ScrollToItem(ItemsAndContentListItem item)
        {
            if (PART_ListView == null)
                return;

            DispatcherQueue.TryEnqueue(() =>
            {
                PART_ListView.UpdateLayout();
                PART_ListView.ScrollIntoView(item);
            });
        }

        private void UpdateResponsiveLayout(double? width = null)
        {
            if (LeftPaneBorder == null ||
                DetailPaneContainer == null ||
                LeftPaneColumn == null ||
                RightPaneColumn == null ||
                BackButton == null)
            {
                return;
            }

            var currentWidth = width ?? ActualWidth;
            var isCompact = currentWidth < CompactModeWidthThreshold;

            if (isCompact != _isCompactMode)
            {
                _isCompactMode = isCompact;

                if (_isCompactMode)
                    _isCompactDetailVisible = false;
            }

            if (_isCompactMode)
            {
                LeftPaneBorder.Margin = new Thickness(0);

                if (_isCompactDetailVisible)
                {
                    LeftPaneBorder.Visibility = Visibility.Collapsed;
                    DetailPaneContainer.Visibility = Visibility.Visible;
                    LeftPaneColumn.Width = new GridLength(0);
                    RightPaneColumn.Width = new GridLength(1, GridUnitType.Star);
                    BackButton.Visibility = Visibility.Visible;
                }
                else
                {
                    LeftPaneBorder.Visibility = Visibility.Visible;
                    DetailPaneContainer.Visibility = Visibility.Collapsed;
                    LeftPaneColumn.Width = new GridLength(1, GridUnitType.Star);
                    RightPaneColumn.Width = new GridLength(0);
                    BackButton.Visibility = Visibility.Collapsed;
                }

                return;
            }

            LeftPaneBorder.Margin = new Thickness(0, 0, 8, 0);
            LeftPaneBorder.Visibility = Visibility.Visible;
            DetailPaneContainer.Visibility = Visibility.Visible;
            LeftPaneColumn.Width = LeftPaneWidth;
            RightPaneColumn.Width = new GridLength(1, GridUnitType.Star);
            BackButton.Visibility = Visibility.Collapsed;
        }

        private void UpdateFooterVisibility()
        {
            if (FooterBorder == null)
                return;

            FooterBorder.Visibility = HasContent(Footer)
                ? Visibility.Visible
                : Visibility.Collapsed;
        }

        private void UpdateToolbarVisibility()
        {
            if (ToolbarBorder == null || SearchBox == null || SortButton == null || AddButton == null)
                return;

            var isSortMenuVisible = ShowSortMenu;
            var isAddButtonVisible = ShowAddButton;

            SearchBox.Visibility = ShowSearch ? Visibility.Visible : Visibility.Collapsed;
            SortButton.Visibility = isSortMenuVisible ? Visibility.Visible : Visibility.Collapsed;
            AddButton.Visibility = isAddButtonVisible ? Visibility.Visible : Visibility.Collapsed;

            ToolbarBorder.Visibility =
                ShowSearch || isSortMenuVisible || isAddButtonVisible
                ? Visibility.Visible
                : Visibility.Collapsed;
        }

        private void UpdateSortMenuState()
        {
            if (SortByTitleMenuItem == null ||
                SortBySubtitleMenuItem == null ||
                SortByDescriptionMenuItem == null ||
                SortByKeyMenuItem == null ||
                SortByIdMenuItem == null ||
                SortByIsEnabledMenuItem == null ||
                SortOptionsSeparator == null ||
                SortDescendingMenuItem == null)
            {
                return;
            }

            SortByTitleMenuItem.Text = SortByTitleMenuText ?? string.Empty;
            SortBySubtitleMenuItem.Text = SortBySubtitleMenuText ?? string.Empty;
            SortByDescriptionMenuItem.Text = SortByDescriptionMenuText ?? string.Empty;
            SortByKeyMenuItem.Text = SortByKeyMenuText ?? string.Empty;
            SortByIdMenuItem.Text = SortByIdMenuText ?? string.Empty;
            SortByIsEnabledMenuItem.Text = SortByIsEnabledMenuText ?? string.Empty;
            SortDescendingMenuItem.Text = HasContent(SortDescendingMenuText)
                ? SortDescendingMenuText
                : "Descending";

            SortByTitleMenuItem.Visibility = CanShowSortByTitleOption() ? Visibility.Visible : Visibility.Collapsed;
            SortBySubtitleMenuItem.Visibility = CanShowSortBySubtitleOption() ? Visibility.Visible : Visibility.Collapsed;
            SortByDescriptionMenuItem.Visibility = CanShowSortByDescriptionOption() ? Visibility.Visible : Visibility.Collapsed;
            SortByKeyMenuItem.Visibility = CanShowSortByKeyOption() ? Visibility.Visible : Visibility.Collapsed;
            SortByIdMenuItem.Visibility = CanShowSortByIdOption() ? Visibility.Visible : Visibility.Collapsed;
            SortByIsEnabledMenuItem.Visibility = CanShowSortByIsEnabledOption() ? Visibility.Visible : Visibility.Collapsed;
            SortDescendingMenuItem.Visibility = Visibility.Visible;

            SortOptionsSeparator.Visibility = HasVisibleSortFieldOption()
                ? Visibility.Visible
                : Visibility.Collapsed;

            EnsureSortByIsVisible();

            SortByTitleMenuItem.IsChecked = SortBy == ItemsAndContentSortField.Title;
            SortBySubtitleMenuItem.IsChecked = SortBy == ItemsAndContentSortField.Subtitle;
            SortByDescriptionMenuItem.IsChecked = SortBy == ItemsAndContentSortField.Description;
            SortByKeyMenuItem.IsChecked = SortBy == ItemsAndContentSortField.Key;
            SortByIdMenuItem.IsChecked = SortBy == ItemsAndContentSortField.Id;
            SortByIsEnabledMenuItem.IsChecked = SortBy == ItemsAndContentSortField.IsEnabled;
            SortDescendingMenuItem.IsChecked = SortDescending;
        }

        private bool HasVisibleSortFieldOption()
        {
            return CanShowSortByTitleOption() ||
                   CanShowSortBySubtitleOption() ||
                   CanShowSortByDescriptionOption() ||
                   CanShowSortByKeyOption() ||
                   CanShowSortByIdOption() ||
                   CanShowSortByIsEnabledOption();
        }

        private bool CanShowSortByTitleOption()
        {
            return ShowSortByTitleOption && HasContent(SortByTitleMenuText);
        }

        private bool CanShowSortBySubtitleOption()
        {
            return ShowSortBySubtitleOption && HasContent(SortBySubtitleMenuText);
        }

        private bool CanShowSortByDescriptionOption()
        {
            return ShowSortByDescriptionOption && HasContent(SortByDescriptionMenuText);
        }

        private bool CanShowSortByKeyOption()
        {
            return ShowSortByKeyOption && HasContent(SortByKeyMenuText);
        }

        private bool CanShowSortByIdOption()
        {
            return ShowSortByIdOption && HasContent(SortByIdMenuText);
        }

        private bool CanShowSortByIsEnabledOption()
        {
            return ShowSortByIsEnabledOption && HasContent(SortByIsEnabledMenuText);
        }

        private void EnsureSortByIsVisible()
        {
            if (IsSortFieldVisible(SortBy))
                return;

            var firstVisible = GetFirstVisibleSortField();
            if (!firstVisible.HasValue)
                return;

            SetValue(SortByProperty, firstVisible.Value);
        }

        private bool IsSortFieldVisible(ItemsAndContentSortField sortBy)
        {
            return sortBy switch
            {
                ItemsAndContentSortField.Subtitle => CanShowSortBySubtitleOption(),
                ItemsAndContentSortField.Description => CanShowSortByDescriptionOption(),
                ItemsAndContentSortField.Key => CanShowSortByKeyOption(),
                ItemsAndContentSortField.Id => CanShowSortByIdOption(),
                ItemsAndContentSortField.IsEnabled => CanShowSortByIsEnabledOption(),
                _ => CanShowSortByTitleOption()
            };
        }

        private ItemsAndContentSortField? GetFirstVisibleSortField()
        {
            if (CanShowSortByTitleOption())
                return ItemsAndContentSortField.Title;
            if (CanShowSortBySubtitleOption())
                return ItemsAndContentSortField.Subtitle;
            if (CanShowSortByDescriptionOption())
                return ItemsAndContentSortField.Description;
            if (CanShowSortByKeyOption())
                return ItemsAndContentSortField.Key;
            if (CanShowSortByIdOption())
                return ItemsAndContentSortField.Id;
            if (CanShowSortByIsEnabledOption())
                return ItemsAndContentSortField.IsEnabled;

            return null;
        }

        private void UpdateButtonsState()
        {
            // Intentionally left blank. Header delete button was removed.
        }

        private static bool MatchesSearch(ItemsAndContentListItem item, string text)
        {
            return Contains(item.Title, text) ||
                   Contains(item.Subtitle, text) ||
                   Contains(item.Description, text) ||
                   Contains(item.SearchText, text) ||
                   Contains(item.Key, text);
        }

        private static bool IsSearchProperty(string propertyName)
        {
            return propertyName == nameof(ItemsAndContentListItem.Title) ||
                   propertyName == nameof(ItemsAndContentListItem.Subtitle) ||
                   propertyName == nameof(ItemsAndContentListItem.Description) ||
                   propertyName == nameof(ItemsAndContentListItem.SearchText) ||
                   propertyName == nameof(ItemsAndContentListItem.Key);
        }

        private static string GetSortPropertyName(ItemsAndContentSortField sortBy)
        {
            return sortBy switch
            {
                ItemsAndContentSortField.Subtitle => nameof(ItemsAndContentListItem.Subtitle),
                ItemsAndContentSortField.Description => nameof(ItemsAndContentListItem.Description),
                ItemsAndContentSortField.Key => nameof(ItemsAndContentListItem.Key),
                ItemsAndContentSortField.Id => nameof(ItemsAndContentListItem.Id),
                ItemsAndContentSortField.IsEnabled => nameof(ItemsAndContentListItem.IsEnabled),
                _ => nameof(ItemsAndContentListItem.Title)
            };
        }

        private static bool Contains(string? value, string search)
        {
            if (string.IsNullOrWhiteSpace(value))
                return false;

            return value.Contains(search, StringComparison.OrdinalIgnoreCase);
        }

        private static bool HasContent(object? value)
        {
            return value switch
            {
                null => false,
                string s => !string.IsNullOrWhiteSpace(s),
                _ => true
            };
        }
    }

    public sealed class SelectedItemChangedEventArgs : EventArgs
    {
        public SelectedItemChangedEventArgs(
            long? oldId,
            long? newId,
            ItemsAndContentListItem? oldItem,
            ItemsAndContentListItem? newItem)
        {
            OldId = oldId;
            NewId = newId;
            OldItem = oldItem;
            NewItem = newItem;
        }

        public long? OldId { get; }
        public long? NewId { get; }
        public ItemsAndContentListItem? OldItem { get; }
        public ItemsAndContentListItem? NewItem { get; }
    }

    public sealed class SearchTextChangedEventArgs : EventArgs
    {
        public SearchTextChangedEventArgs(string text, AutoSuggestionBoxTextChangeReason reason)
        {
            Text = text;
            Reason = reason;
        }

        public string Text { get; }
        public AutoSuggestionBoxTextChangeReason Reason { get; }
    }

}
