using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;

namespace Sonex.Client.Controls;

[ContentProperty(Name = nameof(Items))]
public sealed class TabPanel : UserControl
{
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register(
            nameof(SelectedItem),
            typeof(TabPanelItem),
            typeof(TabPanel),
            new PropertyMetadata(null, OnSelectedItemChanged));

    public static readonly DependencyProperty TabHeightProperty =
        DependencyProperty.Register(
            nameof(TabHeight),
            typeof(double),
            typeof(TabPanel),
            new PropertyMetadata(double.NaN, OnTabAppearanceChanged));

    public static readonly DependencyProperty HeaderCommandBarProperty =
        DependencyProperty.Register(
            nameof(HeaderCommandBar),
            typeof(CommandBar),
            typeof(TabPanel),
            new PropertyMetadata(null, OnHeaderCommandBarChanged));

    private readonly TabPanelItemCollection _items = [];
    private readonly List<TabPanelItem> _trackedItems = [];
    private readonly SelectorBar _selectorBar;
    private readonly ContentPresenter _selectedContentPresenter;
    private readonly ContentPresenter _headerCommandBarPresenter;
    private bool _isUpdatingSelection;

    public TabPanel()
    {
        Grid layoutRoot = new()
        {
            RowSpacing = 0
        };
        layoutRoot.CornerRadius = new CornerRadius(10);
        layoutRoot.Padding = new Thickness(0, 0, 0, 0);
        layoutRoot.BorderThickness = new Thickness(1);
        layoutRoot.Background = Application.Current.Resources["CardBackgroundFillColorDefaultBrush"] as Brush;
        layoutRoot.BorderBrush = Application.Current.Resources["CardStrokeColorDefaultBrush"] as Brush;

        layoutRoot.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        layoutRoot.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });

        Grid headerGrid = new()
        {
            ColumnSpacing = 8,
            Padding = new Thickness(8, 0, 0, 0),
            Height = 48d,
            BorderThickness = new Thickness(0, 0, 0, 1),
            BorderBrush = Application.Current.Resources["CardStrokeColorDefaultBrush"] as Brush
        };
        headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
        headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

        _selectorBar = new SelectorBar();
        _selectorBar.Resources["SelectorBarItemPadding"] = new Thickness(0, 0, 0, 0);
        _selectorBar.Resources["SelectorBarPadding"] = new Thickness(0, 0, 0, 0);
        _selectorBar.VerticalAlignment = VerticalAlignment.Center;

        _headerCommandBarPresenter = new ContentPresenter
        {
            HorizontalAlignment = HorizontalAlignment.Right,
            VerticalAlignment = VerticalAlignment.Center
        };

        Grid.SetColumn(_selectorBar, 0);
        Grid.SetColumn(_headerCommandBarPresenter, 1);

        headerGrid.Children.Add(_selectorBar);
        headerGrid.Children.Add(_headerCommandBarPresenter);

        _selectedContentPresenter = new ContentPresenter();
        _selectedContentPresenter.Background = Application.Current.Resources["CardBackgroundFillColorDefaultBrush"] as Brush;

        Grid.SetRow(headerGrid, 0);
        Grid.SetRow(_selectedContentPresenter, 1);

        layoutRoot.Children.Add(headerGrid);
        layoutRoot.Children.Add(_selectedContentPresenter);

        base.Content = layoutRoot;

        _selectorBar.SelectionChanged += SelectorBar_SelectionChanged;
        _items.CollectionChanged += Items_CollectionChanged;

        UpdateHeaderCommandBar();
        EnsureSelection();
        UpdateSelectedContent();
    }

    public TabPanelItemCollection Items => _items;

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

    public double TabHeight
    {
        get => (double)GetValue(TabHeightProperty);
        set => SetValue(TabHeightProperty, value);
    }

    public CommandBar? HeaderCommandBar
    {
        get => (CommandBar?)GetValue(HeaderCommandBarProperty);
        set => SetValue(HeaderCommandBarProperty, value);
    }

    public int SelectedIndex
    {
        get => SelectedItem is null ? -1 : Items.IndexOf(SelectedItem);
        set => SelectedItem = value >= 0 && value < Items.Count ? Items[value] : null;
    }

    public event EventHandler? SelectionChanged;

    private static void OnSelectedItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        if (dependencyObject is TabPanel tabPanel)
        {
            tabPanel.HandleSelectedItemChanged((TabPanelItem?)args.OldValue, (TabPanelItem?)args.NewValue);
        }
    }

    private static void OnTabAppearanceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs _)
    {
        if (dependencyObject is TabPanel tabPanel)
        {
            tabPanel.ApplyTabAppearance();
        }
    }

    private static void OnHeaderCommandBarChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs _)
    {
        if (dependencyObject is TabPanel tabPanel)
        {
            tabPanel.UpdateHeaderCommandBar();
        }
    }

    private void HandleSelectedItemChanged(TabPanelItem? oldItem, TabPanelItem? newItem)
    {
        if (_isUpdatingSelection)
        {
            return;
        }

        if (newItem is not null && !Items.Contains(newItem))
        {
            _isUpdatingSelection = true;
            SetValue(SelectedItemProperty, oldItem);
            _isUpdatingSelection = false;
            return;
        }

        UpdateSelectorBarSelection();
        UpdateSelectedContent();

        if (!ReferenceEquals(oldItem, newItem))
        {
            RaiseItemSelected(newItem);
            SelectionChanged?.Invoke(this, EventArgs.Empty);
        }
    }

    private void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs _)
    {
        RebuildTrackedItems();
        SyncItems();
        EnsureSelection();
        UpdateSelectedContent();
    }

    private void RebuildTrackedItems()
    {
        foreach (TabPanelItem item in _trackedItems)
        {
            item.ContentChanged -= Item_ContentChanged;
        }

        _trackedItems.Clear();

        foreach (TabPanelItem item in Items)
        {
            item.ContentChanged += Item_ContentChanged;
            _trackedItems.Add(item);
        }
    }

    private void SyncItems()
    {
        _selectorBar.Items.Clear();

        foreach (TabPanelItem item in Items)
        {
            ApplyTabAppearance(item);
            _selectorBar.Items.Add(item);
        }
    }

    private void ApplyTabAppearance()
    {
        foreach (TabPanelItem item in Items)
        {
            ApplyTabAppearance(item);
        }
    }

    private void ApplyTabAppearance(TabPanelItem item)
    {
        if (double.IsNaN(TabHeight) || TabHeight <= 0)
        {
            item.ClearValue(HeightProperty);
            return;
        }

        item.Height = TabHeight;
    }

    private void EnsureSelection()
    {
        if (SelectedItem is not null && Items.Contains(SelectedItem))
        {
            UpdateSelectorBarSelection();
            return;
        }

        _isUpdatingSelection = true;
        SetValue(SelectedItemProperty, Items.Count > 0 ? Items[0] : null);
        _isUpdatingSelection = false;

        UpdateSelectorBarSelection();
    }

    private void UpdateSelectorBarSelection()
    {
        _isUpdatingSelection = true;
        _selectorBar.SelectedItem = SelectedItem;
        _isUpdatingSelection = false;
    }

    private void UpdateSelectedContent()
    {
        _selectedContentPresenter.Content = SelectedItem?.Content;
    }

    private void UpdateHeaderCommandBar()
    {
        if (HeaderCommandBar != null)
        {
            HeaderCommandBar.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
        }

        _headerCommandBarPresenter.Content = HeaderCommandBar;
        _headerCommandBarPresenter.Visibility = HeaderCommandBar is null
            ? Visibility.Collapsed
            : Visibility.Visible;
    }

    private void Item_ContentChanged(object? sender, EventArgs e)
    {
        if (ReferenceEquals(sender, SelectedItem))
        {
            UpdateSelectedContent();
        }
    }

    private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
    {
        TabPanelItem? selectedItem = sender.SelectedItem as TabPanelItem;

        if (ReferenceEquals(SelectedItem, selectedItem))
        {
            return;
        }

        _isUpdatingSelection = true;
        SetValue(SelectedItemProperty, selectedItem);
        _isUpdatingSelection = false;

        UpdateSelectedContent();
        RaiseItemSelected(selectedItem);
        SelectionChanged?.Invoke(this, EventArgs.Empty);
    }

    private void RaiseItemSelected(TabPanelItem? item)
    {
        item?.RaiseOnSelect();
    }
}