using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using PCL.Core.App.Localization;
using PCL.Core.Logging;
using PCL.Core.Utils.Exts;

namespace PCL;

public partial class FontSelector
{
    public delegate void SelectionChangedEventHandler(object sender, SelectionChangedEventArgs e);

    public static readonly DependencyProperty TooltipProperty = DependencyProperty.Register(nameof(Tooltip),
        typeof(string), typeof(FontSelector), new PropertyMetadata(null, OnTooltipChanged));

    private bool _isInitializing;
    private bool _isListeningLanguageChanged;
    private string? _pendingFontTag;

    public FontSelector()
    {
        InitializeComponent();
        Loaded += FontSelector_Loaded;
        Unloaded += FontSelector_Unloaded;
        ComboFont.SelectionChanged += ComboFont_SelectionChanged;
    }

    public string Tooltip
    {
        get => (string)GetValue(TooltipProperty);
        set => SetValue(TooltipProperty, value);
    }

    public ObservableCollection<CustomFontProperties> CustomFontCollection { get; } = [];

    public string SelectedFontTag
    {
        get
        {
            if (ComboFont.SelectedItem is null) return "";
            return ComboFont.SelectedItem is not CustomFontProperties selectedFont ? "" : selectedFont.Tag;
        }
        set
        {
            // 如果字体还在加载中，延迟设置
            if (CustomFontCollection.Count == 0 ||
                (CustomFontCollection is [{ Name: var name }] && name == Lang.Text("Common.State.Loading")))
            {
                _pendingFontTag = value;
                return;
            }

            _isInitializing = true;

            var targetSelection = CustomFontCollection.FirstOrDefault(i => i.Tag == value);
            if (targetSelection is null)
                ComboFont.SelectedIndex = 0;
            else
                ComboFont.SelectedItem = targetSelection;

            _isInitializing = false;
        }
    }

    public int SelectedIndex
    {
        get => ComboFont.SelectedIndex;
        set => ComboFont.SelectedIndex = value;
    }

    public new bool IsEnabled
    {
        get => ComboFont.IsEnabled;
        set => ComboFont.IsEnabled = value;
    }

    private static void OnTooltipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is FontSelector control) control.ComboFont.ToolTip = e.NewValue;
    }

    public event SelectionChangedEventHandler? SelectionChanged;

    private void FontSelector_Loaded(object sender, RoutedEventArgs e)
    {
        StartListeningLanguageChanged();

        if (CustomFontCollection.Count == 0)
            LoadFonts();
        else
            RefreshDefaultFont();
    }

    private void FontSelector_Unloaded(object sender, RoutedEventArgs e)
    {
        StopListeningLanguageChanged();
    }

    private void StartListeningLanguageChanged()
    {
        if (_isListeningLanguageChanged) return;
        LocalizationService.LanguageChanged += LocalizationService_LanguageChanged;
        _isListeningLanguageChanged = true;
    }

    private void StopListeningLanguageChanged()
    {
        if (!_isListeningLanguageChanged) return;
        LocalizationService.LanguageChanged -= LocalizationService_LanguageChanged;
        _isListeningLanguageChanged = false;
    }

    private void LocalizationService_LanguageChanged()
    {
        RefreshDefaultFont();
    }

    private void RefreshDefaultFont()
    {
        if (!Dispatcher.CheckAccess())
        {
            Dispatcher.Invoke(RefreshDefaultFont);
            return;
        }

        GetDefaultFont()?.Font = LocalizationFontService.BuildLaunchFontFamily();
    }

    private void LoadFonts()
    {
        Dispatcher.BeginInvoke(async () =>
        {
            ComboFont.IsEnabled = false;
            _isInitializing = true;
            CustomFontCollection.Add(new CustomFontProperties { Name = Lang.Text("Common.State.Loading") });
            ComboFont.SelectedIndex = 0;

            var availableFonts = new List<(string Name, FontFamily Font)>();

            await Task.Run(() =>
            {
                foreach (var font in Fonts.SystemFontFamilies)
                    try
                    {
                        if (font.Source.StartsWith("Global ")) continue;

                        foreach (var typeface in font.GetTypefaces())
                        {
                            if (!typeface.TryGetGlyphTypeface(out var glyph))
                                throw new NullReferenceException(
                                    $"字形 {typeface.FaceNames.GetForCurrentUiCulture("(unknown)")} 无法加载");

                            _ = new GlyphTypeface(glyph.FontUri);
                        }

                        availableFonts.Add((font.FamilyNames.GetForCurrentUiCulture(), font));
                    }
                    catch (Exception ex)
                    {
                        LogWrapper.Error(ex, $"发现了一个无法加载的异常的字体：{font.Source}");
                    }

                availableFonts.Sort((l, r) => string.Compare(l.Name, r.Name, StringComparison.Ordinal));
            });

            CustomFontCollection.Clear();
            CustomFontCollection.Add(new CustomFontProperties
            {
                Name = Lang.Text("Common.Option.Default"),
                Font = LocalizationFontService.BuildLaunchFontFamily(),
                Tag = ""
            });

            foreach (var font in availableFonts)
                CustomFontCollection.Add(new CustomFontProperties
                    { Name = font.Name, Font = font.Font, Tag = font.Font.Source });

            ComboFont.IsEnabled = true;

            if (_pendingFontTag is not null)
            {
                var pendingTag = _pendingFontTag;
                _pendingFontTag = null;
                SelectedFontTag = pendingTag;
            }

            _isInitializing = false;
        });
    }

    private void ComboFont_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (!_isInitializing) SelectionChanged?.Invoke(sender, e);
    }

    private CustomFontProperties? GetDefaultFont()
    {
        return CustomFontCollection.FirstOrDefault(i => string.IsNullOrEmpty(i.Tag));
    }

    public class CustomFontProperties : INotifyPropertyChanged
    {
        public string Name
        {
            get;
            init => SetField(ref field, value);
        } = string.Empty;

        public FontFamily Font
        {
            get;
            set => SetField(ref field, value);
        } = LocalizationFontService.BuildLaunchFontFamily();

        public string Tag
        {
            get;
            set => SetField(ref field, value);
        } = string.Empty;

        public event PropertyChangedEventHandler? PropertyChanged;

        private void SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return;
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
