using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Text;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Graphics;
using Windows.Media;
using Windows.Media.Audio;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Media.Render;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Text;
using WinRT.Interop;
using YMhut.Box.Core.App;
using YMhut.Box.Core.Logging;
using YMhut.Box.Core.Media;
using YMhut.Box.WinUI.Services;

namespace YMhut.Box.WinUI.Views;

public enum MediaPlayerShellMode
{
    Embedded,
    Standalone
}

public sealed class MediaPlayerPage : Page
{
    private readonly AppPaths _appPaths = AppServices.GetRequiredService<AppPaths>();
    private readonly IIndependentWindowHostLauncher _windowHostLauncher = AppServices.GetRequiredService<IIndependentWindowHostLauncher>();
    private readonly Window? _ownerWindow;
    private readonly MediaPlayerShellMode _shellMode;
    private readonly DispatcherTimer _hideControlsTimer = new() { Interval = TimeSpan.FromSeconds(3) };
    private readonly DispatcherTimer _slideshowTimer = new() { Interval = TimeSpan.FromSeconds(5) };
    private readonly DispatcherTimer _visualizerTimer = new() { Interval = TimeSpan.FromMilliseconds(180) };
    private readonly DispatcherTimer _audioPositionTimer = new() { Interval = TimeSpan.FromMilliseconds(250) };
    private readonly Grid _root = new() { Background = DarkBrush("#101010"), IsTabStop = true };
    private readonly Grid _layout = new() { Background = DarkBrush("#101010") };
    private readonly Grid _stage = new() { Background = DarkBrush("#050505") };
    private FrameworkElement? _topBar;
    private readonly Border _playlistHost = new()
    {
        Background = DarkBrush("#1B1B1B"),
        BorderBrush = DarkBrush("#333333"),
        BorderThickness = new Thickness(1, 0, 0, 0)
    };
    private readonly StackPanel _playlistPanel = new() { Spacing = 8 };
    private readonly MediaPlayerElement _playerElement = new()
    {
        AreTransportControlsEnabled = false,
        Stretch = Stretch.Uniform,
        Visibility = Visibility.Collapsed
    };
    private readonly Image _imageViewer = new()
    {
        Stretch = Stretch.Uniform,
        Visibility = Visibility.Collapsed
    };
    private readonly Grid _audioVisualizer = new()
    {
        Visibility = Visibility.Collapsed,
        HorizontalAlignment = HorizontalAlignment.Stretch,
        VerticalAlignment = VerticalAlignment.Stretch
    };
    private readonly Border _controlsHost = new()
    {
        VerticalAlignment = VerticalAlignment.Bottom,
        Margin = new Thickness(24),
        CornerRadius = new CornerRadius(18),
        Padding = new Thickness(18),
        Background = DarkBrush("#D8181818")
    };
    private readonly Slider _positionSlider = new() { Minimum = 0, Maximum = 100 };
    private readonly Slider _volumeSlider = new()
    {
        Minimum = MediaVolumeModel.MinPercent,
        Maximum = MediaVolumeModel.MaxPercent,
        Value = 80,
        SmallChange = 5,
        LargeChange = 10,
        StepFrequency = 5,
        Width = 160
    };
    private readonly ComboBox _speedBox = new() { Width = 112 };
    private readonly ToggleSwitch _slideshowSwitch = new() { Header = L("幻灯片", "Slideshow"), Foreground = DarkBrush("#F5F5F5") };
    private readonly ToggleSwitch _loopSwitch = new() { Header = L("循环", "Loop"), Foreground = DarkBrush("#F5F5F5") };
    private readonly Button _playPauseButton = new()
    {
        Width = 52,
        Height = 52,
        Padding = new Thickness(0),
        CornerRadius = new CornerRadius(26),
        Background = DarkBrush("#F5F5F5"),
        BorderBrush = DarkBrush("#F5F5F5"),
        Content = new FontIcon { Glyph = "\uE768", FontSize = 22, Foreground = DarkBrush("#101010") }
    };
    private readonly TextBlock _timeText = LightText("00:00 / 00:00", 13);
    private readonly TextBlock _nowPlayingText = LightText(L("尚未打开媒体", "No media opened"), 16, FontWeights.SemiBold);
    private readonly TextBlock _mediaInfoText = LightText(L("支持常见图片、音频、视频；解码能力跟随 Windows 系统组件。", "Supports common image, audio, and video formats using Windows system codecs."), 12);
    private readonly TextBlock _volumeText = LightText(MediaVolumeModel.FormatPercent(80), 12, FontWeights.SemiBold, maxLines: 1);
    private readonly TextBlock _playlistCountText = LightText("0", 12);
    private readonly TextBlock _emptyText = LightText(L("打开图片、音频或视频后开始播放。", "Open images, audio, or videos to start playback."), 18, FontWeights.SemiBold);
    private readonly List<PlaylistItem> _playlist = [];
    private readonly List<Border> _audioBars = [];
    private Grid? _transportGrid;
    private StackPanel? _nowPlayingPanel;
    private StackPanel? _centerControlsPanel;
    private StackPanel? _rightControlsPanel;
    private TextBlock? _speedLabel;
    private Button? _fullscreenButton;
    private Button? _muteButton;
    private Button? _fitButton;
    private Button? _clearPlaylistButton;

    private MediaPlayer? _player;
    private AudioGraph? _audioGraph;
    private AudioFileInputNode? _audioInputNode;
    private AudioDeviceOutputNode? _audioOutputNode;
    private AppWindow? _appWindow;
    private int _currentIndex = -1;
    private bool _fullscreen;
    private bool _draggingPosition;
    private bool _syncingPosition;
    private bool _syncingVolume;
    private bool _muted;
    private bool _audioGraphPlaying;
    private double _lastVolume = 80;
    private Stretch _mediaStretch = Stretch.Uniform;
    private TimeSpan _duration = TimeSpan.Zero;
    private string? _playbackCapabilityNote;

    public MediaPlayerPage(Window? ownerWindow = null, MediaPlayerShellMode shellMode = MediaPlayerShellMode.Standalone)
    {
        _ownerWindow = ownerWindow;
        _shellMode = shellMode;
        Content = BuildContent();
        Loaded += MediaPlayerPage_Loaded;
        Unloaded += MediaPlayerPage_Unloaded;
        SizeChanged += (_, _) => ArrangeLayout();
        _root.PointerMoved += Root_PointerMoved;
        _root.KeyDown += Root_KeyDown;
        _hideControlsTimer.Tick += HideControlsTimer_Tick;
        _slideshowTimer.Tick += (_, _) => ShowNext();
        _visualizerTimer.Tick += (_, _) => RenderAudioBars(active: true);
        _audioPositionTimer.Tick += AudioPositionTimer_Tick;
    }

    private UIElement BuildContent()
    {
        if (_shellMode == MediaPlayerShellMode.Embedded)
        {
            _root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _root.RowDefinitions.Add(new RowDefinition());

            _topBar = BuildTopBar();
            Grid.SetRow(_topBar, 0);
            _root.Children.Add(_topBar);
        }
        else
        {
            _root.RowDefinitions.Add(new RowDefinition());
        }

        BuildStage();
        BuildPlaylist();
        _layout.Children.Add(_stage);
        _layout.Children.Add(_playlistHost);
        Grid.SetRow(_layout, _shellMode == MediaPlayerShellMode.Embedded ? 1 : 0);
        _root.Children.Add(_layout);
        return _root;
    }

    private FrameworkElement BuildTopBar()
    {
        var open = PlayerCommandButton(L("打开媒体", "Open media"), "\uE8E5", OpenFiles_Click, primary: true);
        var clear = PlayerCommandButton(L("清空列表", "Clear list"), "\uE74D", ClearPlaylist);
        _clearPlaylistButton = clear;

        if (_shellMode == MediaPlayerShellMode.Embedded)
        {
            var detach = PlayerCommandButton(L("独立窗口", "Standalone window"), "\uE8A7", OpenStandaloneWindow);
            return new Grid
            {
                Padding = new Thickness(14, 10, 14, 10),
                Background = DarkBrush("#111111"),
                ColumnDefinitions =
                {
                    new ColumnDefinition(),
                    new ColumnDefinition { Width = GridLength.Auto }
                },
                Children =
                {
                    new StackPanel
                    {
                        Spacing = 1,
                        VerticalAlignment = VerticalAlignment.Center,
                        Children =
                        {
                            LightText(L("媒体播放器", "Media Player"), 16, FontWeights.SemiBold, maxLines: 1),
                            LightText(L("本地媒体播放", "Local media playback"), 12, maxLines: 1)
                        }
                    },
                    WithColumn(new StackPanel
                    {
                        Orientation = Orientation.Horizontal,
                        Spacing = 8,
                        VerticalAlignment = VerticalAlignment.Center,
                        Children = { open, clear, detach, _slideshowSwitch, _loopSwitch }
                    }, 1)
                }
            };
        }

        return new Grid
        {
            Padding = new Thickness(18, 12, 18, 10),
            Background = DarkBrush("#111111"),
            ColumnDefinitions =
            {
                new ColumnDefinition(),
                new ColumnDefinition { Width = GridLength.Auto }
            },
            Children =
            {
                new StackPanel
                {
                    Spacing = 2,
                    VerticalAlignment = VerticalAlignment.Center,
                    Children =
                    {
                        LightText(L("媒体播放器", "Media Player"), 18, FontWeights.SemiBold, maxLines: 1),
                        LightText(L("本地图片、音频和视频播放；双击舞台可进入全屏。", "Local image, audio, and video playback. Double-click the stage for fullscreen."), 12, maxLines: 1)
                    }
                },
                WithColumn(new StackPanel
                {
                    Orientation = Orientation.Horizontal,
                    Spacing = 10,
                    VerticalAlignment = VerticalAlignment.Center,
                    Children = { open, clear, _slideshowSwitch, _loopSwitch }
                }, 1)
            }
        };
    }

    private void BuildStage()
    {
        _stage.DoubleTapped += (_, _) => ToggleFullscreen();
        _stage.Children.Add(new Grid
        {
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center,
            Children = { _emptyText }
        });
        _stage.Children.Add(_playerElement);
        _stage.Children.Add(_imageViewer);
        _stage.Children.Add(BuildAudioVisualizer());
        _stage.Children.Add(BuildControls());
    }

    private UIElement BuildAudioVisualizer()
    {
        var bars = new StackPanel
        {
            Orientation = Orientation.Horizontal,
            Spacing = 8,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
        };
        for (var index = 0; index < 28; index++)
        {
            var bar = new Border
            {
                Width = 8,
                Height = 34,
                CornerRadius = new CornerRadius(4),
                Background = DarkBrush("#60B3FF"),
                VerticalAlignment = VerticalAlignment.Center
            };
            _audioBars.Add(bar);
            bars.Children.Add(bar);
        }

        _audioVisualizer.Children.Add(new StackPanel
        {
            Spacing = 22,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center,
            Children =
            {
                ModernUi.IconTile("\uE8D6", 64, DarkBrush("#26384A"), DarkBrush("#A8D8FF"), 28),
                bars,
                LightText(L("音频播放中", "Audio is playing"), 16, FontWeights.SemiBold)
            }
        });
        return _audioVisualizer;
    }

    private UIElement BuildControls()
    {
        _positionSlider.PointerPressed += (_, _) => _draggingPosition = true;
        _positionSlider.PointerReleased += (_, _) =>
        {
            _draggingPosition = false;
            SeekTo(TimeSpan.FromSeconds(_positionSlider.Value));
        };
        _positionSlider.ValueChanged += (_, e) =>
        {
            if (!_syncingPosition && !_draggingPosition)
            {
                SeekTo(TimeSpan.FromSeconds(e.NewValue));
            }
        };

        _volumeSlider.ValueChanged += (_, e) =>
        {
            ApplyVolume(updateLastVolume: !_syncingVolume);
        };

        foreach (var rate in new[] { "0.5", "0.75", "1.0", "1.25", "1.5", "2.0", "2.5" })
        {
            _speedBox.Items.Add(new ComboBoxItem { Content = $"{rate}x", Tag = rate });
        }
        _speedBox.SelectedIndex = 2;
        _speedBox.SelectionChanged += (_, _) =>
        {
            if (_speedBox.SelectedItem is ComboBoxItem item && double.TryParse(item.Tag?.ToString(), out var rate))
            {
                ApplyPlaybackRate(rate);
            }
        };

        _playPauseButton.Click += (_, _) => TogglePlayback();
        ToolTipService.SetToolTip(_playPauseButton, L("播放 / 暂停", "Play / Pause"));

        var previous = LightIconButton("\uE892", L("上一项", "Previous"), ShowPrevious);
        var rewind = LightIconButton("\uE100", L("后退 10 秒", "Back 10 seconds"), () => SeekBy(-10));
        var forward = LightIconButton("\uE101", L("前进 10 秒", "Forward 10 seconds"), () => SeekBy(10));
        var next = LightIconButton("\uE893", L("下一项", "Next"), ShowNext);
        var stop = LightIconButton("\uE71A", L("停止", "Stop"), StopPlayback);
        _fullscreenButton = LightIconButton("\uE740", L("全屏", "Fullscreen"), ToggleFullscreen);
        _muteButton = LightIconButton("\uE767", L("静音", "Mute"), ToggleMute);
        _fitButton = LightIconButton("\uE9A6", L("适应窗口", "Fit to window"), ToggleFitMode);

        _slideshowSwitch.Toggled += (_, _) => UpdateSlideshowState();

        var timeline = new Grid { ColumnSpacing = 12 };
        timeline.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        timeline.ColumnDefinitions.Add(new ColumnDefinition());
        timeline.Children.Add(_timeText);
        Grid.SetColumn(_positionSlider, 1);
        timeline.Children.Add(_positionSlider);

        var transport = new Grid { ColumnSpacing = 12, RowSpacing = 10 };
        _transportGrid = transport;
        transport.ColumnDefinitions.Add(new ColumnDefinition());
        transport.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        transport.ColumnDefinitions.Add(new ColumnDefinition());

        var nowPlaying = new StackPanel
        {
            Spacing = 2,
            VerticalAlignment = VerticalAlignment.Center,
            Children =
            {
                _nowPlayingText,
                _mediaInfoText
            }
        };
        _nowPlayingPanel = nowPlaying;
        Grid.SetColumn(nowPlaying, 0);
        transport.Children.Add(nowPlaying);

        var centerControls = new StackPanel
        {
            Orientation = Orientation.Horizontal,
            Spacing = 10,
            VerticalAlignment = VerticalAlignment.Center,
            Children = { previous, rewind, _playPauseButton, forward, next, stop }
        };
        _centerControlsPanel = centerControls;
        Grid.SetColumn(centerControls, 1);
        transport.Children.Add(centerControls);

        _speedLabel = LightText(L("倍速", "Speed"), 13);
        var rightControls = new StackPanel
        {
            Orientation = Orientation.Horizontal,
            Spacing = 8,
            HorizontalAlignment = HorizontalAlignment.Right,
            VerticalAlignment = VerticalAlignment.Center,
            Children =
            {
                _muteButton,
                _volumeSlider,
                _volumeText,
                _speedLabel,
                _speedBox,
                _fitButton,
                _fullscreenButton
            }
        };
        _rightControlsPanel = rightControls;
        Grid.SetColumn(rightControls, 2);
        transport.Children.Add(rightControls);

        _controlsHost.Child = new StackPanel { Spacing = 12, Children = { timeline, transport } };
        return _controlsHost;
    }

    private void BuildPlaylist()
    {
        _playlistHost.Child = new Grid
        {
            RowDefinitions =
            {
                new RowDefinition { Height = GridLength.Auto },
                new RowDefinition()
            },
            Children =
            {
                new Grid
                {
                    Padding = new Thickness(16, 16, 16, 10),
                    ColumnSpacing = 10,
                    ColumnDefinitions =
                    {
                        new ColumnDefinition(),
                        new ColumnDefinition { Width = GridLength.Auto }
                    },
                    Children =
                    {
                        new StackPanel
                        {
                            Spacing = 4,
                            Children =
                            {
                                LightText(L("播放列表", "Playlist"), 18, FontWeights.SemiBold),
                                LightText(L("点击条目切换播放，支持图片幻灯片。", "Click an item to play; image slideshow is supported."), 12)
                            }
                        },
                        WithColumn(_playlistCountText, 1)
                    }
                },
                WithRow(new ScrollViewer
                {
                    VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
                    Padding = new Thickness(12, 0, 20, 14),
                    Content = _playlistPanel
                }, 1)
            }
        };
        RenderPlaylist();
    }

    private void MediaPlayerPage_Loaded(object sender, RoutedEventArgs e)
    {
        _player = new MediaPlayer();
        _player.PlaybackSession.PositionChanged += PlaybackSession_PositionChanged;
        _player.PlaybackSession.NaturalDurationChanged += PlaybackSession_NaturalDurationChanged;
        _player.PlaybackSession.PlaybackStateChanged += PlaybackSession_PlaybackStateChanged;
        _player.MediaEnded += Player_MediaEnded;
        _playerElement.SetMediaPlayer(_player);
        ApplyVolume(updateLastVolume: false);
        _appWindow = GetCurrentAppWindow();
        if (_appWindow is not null)
        {
            _appWindow.Changed += AppWindow_Changed;
        }
        ArrangeLayout();
        UpdateFullscreenButton();
        UpdateMuteButton();
        UpdateFitButton();
        UpdateTransportState();
        _root.Focus(FocusState.Programmatic);
    }

    private void MediaPlayerPage_Unloaded(object sender, RoutedEventArgs e)
    {
        _hideControlsTimer.Stop();
        _slideshowTimer.Stop();
        _visualizerTimer.Stop();
        DisposeAudioGraph();
        if (_fullscreen)
        {
            ExitFullscreen();
        }
        if (_player is not null)
        {
            _player.PlaybackSession.PositionChanged -= PlaybackSession_PositionChanged;
            _player.PlaybackSession.NaturalDurationChanged -= PlaybackSession_NaturalDurationChanged;
            _player.PlaybackSession.PlaybackStateChanged -= PlaybackSession_PlaybackStateChanged;
            _player.MediaEnded -= Player_MediaEnded;
        }
        if (_appWindow is not null)
        {
            _appWindow.Changed -= AppWindow_Changed;
        }
        _player?.Dispose();
    }

    public void OpenFilesFromShell()
    {
        OpenFiles_Click();
    }

    public void ClearPlaylistFromShell()
    {
        ClearPlaylist();
    }

    public void StopFromShell()
    {
        StopPlayback();
    }

    public void ShowPreviousFromShell()
    {
        ShowPrevious();
    }

    public void ShowNextFromShell()
    {
        ShowNext();
    }

    private async void OpenStandaloneWindow()
    {
        try
        {
            await _windowHostLauncher.LaunchAsync(IndependentWindowHostOptions.Media(
                L("媒体播放器", "Media Player"),
                _appPaths));
            ToastService.Show(L("媒体播放器独立窗口已打开", "Media player window opened"), ToastKind.Success);
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            ToastService.Show(L("媒体播放器独立窗口打开失败", "Failed to open media player window"), ToastKind.Error);
        }
    }

    private async void OpenFiles_Click()
    {
        var picker = new FileOpenPicker();
        var window = _ownerWindow ?? App.CurrentWindow;
        if (window is null)
        {
            return;
        }

        InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(window));
        foreach (var ext in new[] { ".mp4", ".mkv", ".webm", ".avi", ".mov", ".wmv", ".m4v", ".mp3", ".wav", ".flac", ".aac", ".m4a", ".ogg", ".wma", ".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp", ".tif", ".tiff" })
        {
            picker.FileTypeFilter.Add(ext);
        }

        var files = await picker.PickMultipleFilesAsync();
        if (files.Count == 0)
        {
            return;
        }

        foreach (var file in files)
        {
            _playlist.Add(new PlaylistItem(file, DetectKind(file.FileType)));
        }
        RenderPlaylist();

        if (_currentIndex < 0)
        {
            await PlayItemAsync(0);
        }
    }

    private async Task PlayItemAsync(int index)
    {
        if (_player is null || index < 0 || index >= _playlist.Count)
        {
            return;
        }

        _currentIndex = index;
        var item = _playlist[index];
        _nowPlayingText.Text = item.File.Name;
        _mediaInfoText.Text = $"{KindLabel(item.Kind)} · {index + 1}/{_playlist.Count}";
        _duration = TimeSpan.Zero;
        _positionSlider.Value = 0;
        _positionSlider.Maximum = 100;
        _timeText.Text = "00:00 / 00:00";
        _playbackCapabilityNote = null;
        DisposeAudioGraph();
        _player.Pause();
        _player.Source = null;
        _playerElement.Visibility = Visibility.Collapsed;
        _imageViewer.Visibility = Visibility.Collapsed;
        _audioVisualizer.Visibility = Visibility.Collapsed;
        _visualizerTimer.Stop();

        if (item.Kind == MediaKind.Image)
        {
            var bitmap = new BitmapImage();
            using var stream = await item.File.OpenReadAsync();
            await bitmap.SetSourceAsync(stream);
            _imageViewer.Source = bitmap;
            _imageViewer.Stretch = _mediaStretch;
            _imageViewer.Visibility = Visibility.Visible;
            SetPlayButtonGlyph(_slideshowSwitch.IsOn ? "\uE769" : "\uE768");
            UpdateCurrentMediaInfo();
            UpdateSlideshowState();
        }
        else if (item.Kind == MediaKind.Audio)
        {
            if (await StartAudioGraphAsync(item.File))
            {
                _audioVisualizer.Visibility = Visibility.Visible;
                _visualizerTimer.Start();
                _slideshowTimer.Stop();
            }
            else
            {
                _playbackCapabilityNote = L("增强链路不可用，已使用 100% 平台音量", "Boost path unavailable; platform volume is capped at 100%");
                PlayWithMediaPlayer(item.File, MediaKind.Audio);
            }
        }
        else
        {
            PlayWithMediaPlayer(item.File, MediaKind.Video);
            _slideshowTimer.Stop();
        }

        UpdateCurrentMediaInfo();
        RenderPlaylist();
        UpdateTransportState();
        RestartControlsTimer();
    }

    private void RenderPlaylist()
    {
        UpdatePlaylistSummary();
        _playlistPanel.Children.Clear();
        if (_playlist.Count == 0)
        {
            _playlistPanel.Children.Add(new Border
            {
                Padding = new Thickness(14),
                CornerRadius = new CornerRadius(8),
                Background = DarkBrush("#242424"),
                Child = LightText(L("暂无媒体。点击“打开媒体”添加文件。", "No media yet. Click \"Open media\" to add files."), 13)
            });
            return;
        }

        for (var index = 0; index < _playlist.Count; index++)
        {
            var item = _playlist[index];
            var selected = index == _currentIndex;
            var button = new Button
            {
                Padding = new Thickness(12),
                CornerRadius = new CornerRadius(8),
                Background = selected ? DarkBrush("#26384A") : DarkBrush("#242424"),
                BorderBrush = selected ? DarkBrush("#60B3FF") : DarkBrush("#333333"),
                BorderThickness = new Thickness(1),
                HorizontalContentAlignment = HorizontalAlignment.Stretch,
                Content = new StackPanel
                {
                    Spacing = 4,
                    Children =
                    {
                        LightText(item.File.Name, 13, FontWeights.SemiBold, maxLines: 2),
                        LightText($"{KindLabel(item.Kind)} · {index + 1}/{_playlist.Count}", 12)
                    }
                }
            };
            var captured = index;
            button.Click += async (_, _) => await PlayItemAsync(captured);
            _playlistPanel.Children.Add(button);
        }
    }

    private void ClearPlaylist()
    {
        _slideshowTimer.Stop();
        _visualizerTimer.Stop();
        DisposeAudioGraph();
        _playlist.Clear();
        _currentIndex = -1;
        _duration = TimeSpan.Zero;
        if (_player is not null)
        {
            _player.Pause();
            _player.Source = null;
        }

        _playerElement.Visibility = Visibility.Collapsed;
        _imageViewer.Visibility = Visibility.Collapsed;
        _audioVisualizer.Visibility = Visibility.Collapsed;
        _nowPlayingText.Text = L("尚未打开媒体", "No media opened");
        _mediaInfoText.Text = L("支持常见图片、音频、视频；解码能力跟随 Windows 系统组件。", "Supports common image, audio, and video formats using Windows system codecs.");
        _timeText.Text = "00:00 / 00:00";
        _positionSlider.Value = 0;
        _positionSlider.Maximum = 100;
        RenderPlaylist();
        UpdateTransportState();
    }

    private void ToggleMute()
    {
        var state = MediaVolumeModel.FromPercent(_volumeSlider.Value);
        if (_muted || state.IsMuted)
        {
            SetVolumePercent(_lastVolume <= 0 ? 80 : _lastVolume, updateLastVolume: false);
        }
        else
        {
            _lastVolume = state.Percent;
            SetVolumePercent(0, updateLastVolume: false);
        }
    }

    private void UpdateMuteButton()
    {
        if (_muteButton?.Content is not FontIcon icon)
        {
            return;
        }

        var muted = _muted || MediaVolumeModel.FromPercent(_volumeSlider.Value).IsMuted;
        icon.Glyph = muted ? "\uE74F" : "\uE767";
        ToolTipService.SetToolTip(_muteButton, muted ? L("取消静音", "Unmute") : L("静音", "Mute"));
    }

    private void ToggleFitMode()
    {
        _mediaStretch = _mediaStretch == Stretch.Uniform ? Stretch.UniformToFill : Stretch.Uniform;
        _playerElement.Stretch = _mediaStretch;
        _imageViewer.Stretch = _mediaStretch;
        UpdateFitButton();
    }

    private void UpdateFitButton()
    {
        if (_fitButton?.Content is not FontIcon icon)
        {
            return;
        }

        var fill = _mediaStretch == Stretch.UniformToFill;
        icon.Glyph = fill ? "\uE740" : "\uE9A6";
        ToolTipService.SetToolTip(_fitButton, fill ? L("填充窗口", "Fill window") : L("适应窗口", "Fit to window"));
    }

    private void UpdatePlaylistSummary()
    {
        _playlistCountText.Text = L($"{_playlist.Count} 项", $"{_playlist.Count} items");
        if (_clearPlaylistButton is not null)
        {
            _clearPlaylistButton.IsEnabled = _playlist.Count > 0;
        }
    }

    private void TogglePlayback()
    {
        if (_currentIndex < 0)
        {
            return;
        }

        var item = _playlist[_currentIndex];
        if (item.Kind == MediaKind.Image)
        {
            _slideshowSwitch.IsOn = !_slideshowSwitch.IsOn;
            UpdateSlideshowState();
            return;
        }

        if (IsUsingAudioGraph)
        {
            if (_audioGraphPlaying)
            {
                _audioInputNode?.Stop();
                _audioGraphPlaying = false;
            }
            else
            {
                _audioInputNode?.Start();
                _audioGraphPlaying = true;
                _audioPositionTimer.Start();
            }
        }
        else
        {
            if (_player is null)
            {
                return;
            }

            if (_player.PlaybackSession.PlaybackState == MediaPlaybackState.Playing)
            {
                _player.Pause();
            }
            else
            {
                _player.Play();
            }
        }

        UpdateTransportState();
    }

    private void StopPlayback()
    {
        _slideshowTimer.Stop();
        if (IsUsingAudioGraph)
        {
            _audioInputNode?.Stop();
            _audioInputNode?.Seek(TimeSpan.Zero);
            _audioGraphPlaying = false;
            UpdateAudioGraphPosition(TimeSpan.Zero);
        }
        else if (_player is not null)
        {
            _player.Pause();
            _player.PlaybackSession.Position = TimeSpan.Zero;
        }
        UpdateTransportState();
    }

    private void ShowPrevious()
    {
        if (_playlist.Count == 0)
        {
            return;
        }

        var next = _currentIndex <= 0 ? (_loopSwitch.IsOn ? _playlist.Count - 1 : 0) : _currentIndex - 1;
        _ = PlayItemAsync(next);
    }

    private void ShowNext()
    {
        if (_playlist.Count == 0)
        {
            return;
        }

        var next = _currentIndex + 1;
        if (next >= _playlist.Count)
        {
            if (!_loopSwitch.IsOn)
            {
                _slideshowTimer.Stop();
                return;
            }
            next = 0;
        }
        _ = PlayItemAsync(next);
    }

    private void UpdateSlideshowState()
    {
        if (_currentIndex < 0 || _playlist[_currentIndex].Kind != MediaKind.Image)
        {
            _slideshowTimer.Stop();
            return;
        }

        if (_slideshowSwitch.IsOn)
        {
            _slideshowTimer.Start();
            SetPlayButtonGlyph("\uE769");
        }
        else
        {
            _slideshowTimer.Stop();
            SetPlayButtonGlyph("\uE768");
        }
    }

    private void SeekBy(double seconds)
    {
        if (_currentIndex < 0 || _playlist[_currentIndex].Kind == MediaKind.Image)
        {
            return;
        }

        var current = IsUsingAudioGraph
            ? _audioInputNode?.Position.TotalSeconds ?? 0
            : _player?.PlaybackSession.Position.TotalSeconds ?? 0;
        var next = Math.Clamp(current + seconds, 0, Math.Max(1, _positionSlider.Maximum));
        SeekTo(TimeSpan.FromSeconds(next));
    }

    private void PlaybackSession_PositionChanged(MediaPlaybackSession sender, object args)
    {
        if (_draggingPosition)
        {
            return;
        }

        DispatcherQueue.TryEnqueue(() =>
        {
            _syncingPosition = true;
            _positionSlider.Value = Math.Clamp(sender.Position.TotalSeconds, _positionSlider.Minimum, _positionSlider.Maximum);
            _syncingPosition = false;
            UpdateTimeText(sender.Position);
        });
    }

    private void PlaybackSession_NaturalDurationChanged(MediaPlaybackSession sender, object args)
    {
        _duration = sender.NaturalDuration;
        DispatcherQueue.TryEnqueue(() =>
        {
            _positionSlider.Maximum = Math.Max(1, _duration.TotalSeconds);
            UpdateTimeText(sender.Position);
        });
    }

    private void PlaybackSession_PlaybackStateChanged(MediaPlaybackSession sender, object args)
    {
        DispatcherQueue.TryEnqueue(UpdateTransportState);
    }

    private void Player_MediaEnded(MediaPlayer sender, object args)
    {
        DispatcherQueue.TryEnqueue(ShowNext);
    }

    private void UpdateTransportState()
    {
        if (_currentIndex < 0)
        {
            SetPlayButtonGlyph("\uE768");
            return;
        }

        if (_currentIndex >= 0 && _playlist[_currentIndex].Kind == MediaKind.Image)
        {
            SetPlayButtonGlyph(_slideshowSwitch.IsOn ? "\uE769" : "\uE768");
            return;
        }

        if (IsUsingAudioGraph)
        {
            SetPlayButtonGlyph(_audioGraphPlaying ? "\uE769" : "\uE768");
            UpdateTimeText(_audioInputNode?.Position ?? TimeSpan.Zero);
            return;
        }

        if (_player is null)
        {
            return;
        }
        SetPlayButtonGlyph(_player.PlaybackSession.PlaybackState == MediaPlaybackState.Playing ? "\uE769" : "\uE768");
        UpdateTimeText(_player.PlaybackSession.Position);
    }

    private void SetPlayButtonGlyph(string glyph)
    {
        if (_playPauseButton.Content is FontIcon icon)
        {
            icon.Glyph = glyph;
        }
    }

    private void UpdateTimeText(TimeSpan current)
    {
        _timeText.Text = $"{FormatTime(current)} / {FormatTime(_duration)}";
    }

    private bool IsUsingAudioGraph => _audioGraph is not null && _audioInputNode is not null;

    private void SeekTo(TimeSpan position)
    {
        if (_currentIndex < 0 || _playlist[_currentIndex].Kind == MediaKind.Image)
        {
            return;
        }

        var next = TimeSpan.FromSeconds(Math.Clamp(position.TotalSeconds, 0, Math.Max(1, _positionSlider.Maximum)));
        if (IsUsingAudioGraph)
        {
            _audioInputNode?.Seek(next);
            UpdateAudioGraphPosition(next);
            return;
        }

        if (_player is not null)
        {
            _player.PlaybackSession.Position = next;
        }
    }

    private void SetVolumePercent(double percent, bool updateLastVolume)
    {
        _syncingVolume = true;
        try
        {
            _volumeSlider.Value = Math.Clamp(percent, MediaVolumeModel.MinPercent, MediaVolumeModel.MaxPercent);
        }
        finally
        {
            _syncingVolume = false;
        }

        ApplyVolume(updateLastVolume);
    }

    private void ApplyVolume(bool updateLastVolume = true)
    {
        var state = MediaVolumeModel.FromPercent(_volumeSlider.Value);
        _muted = state.IsMuted;
        if (updateLastVolume && !state.IsMuted)
        {
            _lastVolume = state.Percent;
        }

        _volumeText.Text = MediaVolumeModel.FormatPercent(state.Percent, AppLocalizer.CurrentLanguage);

        if (_player is not null)
        {
            _player.Volume = state.PlatformVolume;
        }

        if (_audioInputNode is not null)
        {
            _audioInputNode.OutgoingGain = state.AudioGain;
        }

        UpdateMuteButton();
        UpdateCurrentMediaInfo();
    }

    private void ApplyPlaybackRate(double rate)
    {
        var normalized = Math.Clamp(rate, 0.25, 3.0);
        if (_player is not null)
        {
            _player.PlaybackSession.PlaybackRate = normalized;
        }

        if (_audioInputNode is not null)
        {
            _audioInputNode.PlaybackSpeedFactor = normalized;
        }
    }

    private double CurrentPlaybackRate()
    {
        if (_speedBox.SelectedItem is ComboBoxItem item &&
            double.TryParse(item.Tag?.ToString(), out var rate))
        {
            return rate;
        }

        return 1.0;
    }

    private void PlayWithMediaPlayer(StorageFile file, MediaKind kind)
    {
        if (_player is null)
        {
            return;
        }

        _player.Source = MediaSource.CreateFromStorageFile(file);
        ApplyPlaybackRate(CurrentPlaybackRate());
        ApplyVolume(updateLastVolume: false);
        _player.Play();
        _playerElement.Stretch = _mediaStretch;
        _playerElement.Visibility = kind == MediaKind.Video ? Visibility.Visible : Visibility.Collapsed;
        _audioVisualizer.Visibility = kind == MediaKind.Audio ? Visibility.Visible : Visibility.Collapsed;
        if (kind == MediaKind.Audio)
        {
            _visualizerTimer.Start();
        }
    }

    private async Task<bool> StartAudioGraphAsync(StorageFile file)
    {
        try
        {
            DisposeAudioGraph();
            var graphSettings = new AudioGraphSettings(AudioRenderCategory.Media)
            {
                DesiredRenderDeviceAudioProcessing = AudioProcessing.Default,
                MaxPlaybackSpeedFactor = 3.0
            };

            var graphResult = await AudioGraph.CreateAsync(graphSettings);
            if (graphResult.Status != AudioGraphCreationStatus.Success || graphResult.Graph is null)
            {
                return false;
            }

            var graph = graphResult.Graph;
            var outputResult = await graph.CreateDeviceOutputNodeAsync();
            if (outputResult.Status != AudioDeviceNodeCreationStatus.Success || outputResult.DeviceOutputNode is null)
            {
                graph.Dispose();
                return false;
            }

            var inputResult = await graph.CreateFileInputNodeAsync(file);
            if (inputResult.Status != AudioFileNodeCreationStatus.Success || inputResult.FileInputNode is null)
            {
                outputResult.DeviceOutputNode.Dispose();
                graph.Dispose();
                return false;
            }

            _audioGraph = graph;
            _audioOutputNode = outputResult.DeviceOutputNode;
            _audioInputNode = inputResult.FileInputNode;
            _audioInputNode.FileCompleted += AudioInput_FileCompleted;
            _audioInputNode.AddOutgoingConnection(_audioOutputNode);
            _duration = _audioInputNode.Duration;
            _positionSlider.Maximum = Math.Max(1, _duration.TotalSeconds);
            ApplyPlaybackRate(CurrentPlaybackRate());
            ApplyVolume(updateLastVolume: false);
            _audioGraph.Start();
            _audioInputNode.Start();
            _audioGraphPlaying = true;
            _audioPositionTimer.Start();
            UpdateAudioGraphPosition(TimeSpan.Zero);
            return true;
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            DisposeAudioGraph();
            return false;
        }
    }

    private void DisposeAudioGraph()
    {
        _audioPositionTimer.Stop();
        _audioGraphPlaying = false;
        try
        {
            if (_audioInputNode is not null)
            {
                _audioInputNode.FileCompleted -= AudioInput_FileCompleted;
                _audioInputNode.Stop();
                _audioInputNode.Dispose();
            }
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
        }

        try
        {
            _audioOutputNode?.Stop();
            _audioOutputNode?.Dispose();
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
        }

        try
        {
            _audioGraph?.Stop();
            _audioGraph?.Dispose();
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
        }

        _audioInputNode = null;
        _audioOutputNode = null;
        _audioGraph = null;
    }

    private void AudioPositionTimer_Tick(object? sender, object e)
    {
        if (!IsUsingAudioGraph || _draggingPosition)
        {
            return;
        }

        UpdateAudioGraphPosition(_audioInputNode?.Position ?? TimeSpan.Zero);
    }

    private void UpdateAudioGraphPosition(TimeSpan position)
    {
        _syncingPosition = true;
        try
        {
            _positionSlider.Value = Math.Clamp(position.TotalSeconds, _positionSlider.Minimum, _positionSlider.Maximum);
        }
        finally
        {
            _syncingPosition = false;
        }

        UpdateTimeText(position);
    }

    private void AudioInput_FileCompleted(AudioFileInputNode sender, object args)
    {
        DispatcherQueue.TryEnqueue(ShowNext);
    }

    private void UpdateCurrentMediaInfo()
    {
        if (_currentIndex < 0 || _currentIndex >= _playlist.Count)
        {
            return;
        }

        var item = _playlist[_currentIndex];
        var volume = MediaVolumeModel.FromPercent(_volumeSlider.Value);
        var parts = new List<string>
        {
            KindLabel(item.Kind),
            $"{_currentIndex + 1}/{_playlist.Count}"
        };

        if (!string.IsNullOrWhiteSpace(_playbackCapabilityNote))
        {
            parts.Add(_playbackCapabilityNote);
        }

        if (volume.IsBoosted)
        {
            if (item.Kind == MediaKind.Audio && IsUsingAudioGraph)
            {
                parts.Add(L("音频增强已启用", "Audio boost enabled"));
            }
            else if (item.Kind == MediaKind.Video)
            {
                parts.Add(L("视频音量按 100% 输出", "Video output capped at 100%"));
            }
            else if (item.Kind == MediaKind.Audio)
            {
                parts.Add(L("增强不可用，按 100% 输出", "Boost unavailable; capped at 100%"));
            }
        }

        _mediaInfoText.Text = string.Join(" · ", parts);
    }

    private void Root_KeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.Key == Windows.System.VirtualKey.Escape && _fullscreen)
        {
            ExitFullscreen();
            e.Handled = true;
        }
        else if (e.Key == Windows.System.VirtualKey.Space)
        {
            TogglePlayback();
            e.Handled = true;
        }
        else if (e.Key == Windows.System.VirtualKey.F)
        {
            ToggleFullscreen();
            e.Handled = true;
        }
        else if (e.Key == Windows.System.VirtualKey.Left)
        {
            SeekBy(-10);
            e.Handled = true;
        }
        else if (e.Key == Windows.System.VirtualKey.Right)
        {
            SeekBy(10);
            e.Handled = true;
        }
    }

    private void Root_PointerMoved(object sender, PointerRoutedEventArgs e)
    {
        _controlsHost.Visibility = Visibility.Visible;
        RestartControlsTimer();
    }

    private void HideControlsTimer_Tick(object? sender, object e)
    {
        _hideControlsTimer.Stop();
        if (ShouldAutoHideControls())
        {
            _controlsHost.Visibility = Visibility.Collapsed;
        }
    }

    private void RestartControlsTimer()
    {
        _hideControlsTimer.Stop();
        _controlsHost.Visibility = Visibility.Visible;
        if (ShouldAutoHideControls())
        {
            _hideControlsTimer.Start();
        }
    }

    private bool ShouldAutoHideControls()
    {
        return _fullscreen ||
            (_currentIndex >= 0 && _playlist[_currentIndex].Kind is MediaKind.Image or MediaKind.Video);
    }

    private void ToggleFullscreen()
    {
        if (_fullscreen)
        {
            ExitFullscreen();
        }
        else
        {
            EnterFullscreen();
        }
    }

    private void EnterFullscreen()
    {
        _appWindow ??= GetCurrentAppWindow();
        _appWindow?.SetPresenter(AppWindowPresenterKind.FullScreen);
        _fullscreen = true;
        ApplyFullscreenLayout();
        RestartControlsTimer();
    }

    private void ExitFullscreen()
    {
        _appWindow ??= GetCurrentAppWindow();
        _appWindow?.SetPresenter(AppWindowPresenterKind.Default);
        _fullscreen = false;
        ApplyFullscreenLayout();
        _controlsHost.Visibility = Visibility.Visible;
    }

    private void ApplyFullscreenLayout()
    {
        if (_topBar is not null)
        {
            _topBar.Visibility = _fullscreen ? Visibility.Collapsed : Visibility.Visible;
        }

        _playlistHost.Visibility = _fullscreen ? Visibility.Collapsed : Visibility.Visible;
        _controlsHost.Margin = _fullscreen ? new Thickness(28) : new Thickness(24);
        _controlsHost.CornerRadius = new CornerRadius(_fullscreen ? 12 : 18);
        UpdateFullscreenButton();
        ArrangeLayout();
    }

    private void UpdateFullscreenButton()
    {
        if (_fullscreenButton is null)
        {
            return;
        }

        ToolTipService.SetToolTip(_fullscreenButton, _fullscreen ? L("退出全屏", "Exit fullscreen") : L("全屏", "Fullscreen"));
        if (_fullscreenButton.Content is FontIcon icon)
        {
            icon.Glyph = _fullscreen ? "\uE73F" : "\uE740";
        }
    }

    private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
    {
        if (!args.DidPresenterChange)
        {
            return;
        }

        _fullscreen = sender.Presenter.Kind == AppWindowPresenterKind.FullScreen;
        DispatcherQueue.TryEnqueue(() =>
        {
            _controlsHost.Visibility = Visibility.Visible;
            ApplyFullscreenLayout();
        });
    }

    private void ArrangeLayout()
    {
        if (_fullscreen)
        {
            ArrangeTransportLayout(ActualWidth is > 0 and < 760);
            _layout.ColumnDefinitions.Clear();
            _layout.RowDefinitions.Clear();
            _layout.ColumnDefinitions.Add(new ColumnDefinition());
            _layout.RowDefinitions.Add(new RowDefinition());
            Grid.SetRow(_stage, 0);
            Grid.SetColumn(_stage, 0);
            Grid.SetRow(_playlistHost, 0);
            Grid.SetColumn(_playlistHost, 0);
            return;
        }

        var narrow = ActualWidth is > 0 and < 980;
        _controlsHost.Margin = narrow ? new Thickness(14) : new Thickness(24);
        _controlsHost.Padding = narrow ? new Thickness(14) : new Thickness(18);
        _volumeSlider.Width = narrow ? 96 : 150;
        _speedBox.Width = narrow ? 96 : 112;
        ArrangeTransportLayout(narrow);
        _layout.ColumnDefinitions.Clear();
        _layout.RowDefinitions.Clear();
        if (narrow)
        {
            _layout.RowDefinitions.Add(new RowDefinition());
            _layout.RowDefinitions.Add(new RowDefinition { Height = new GridLength(230) });
            _layout.ColumnDefinitions.Add(new ColumnDefinition());
            Grid.SetRow(_stage, 0);
            Grid.SetColumn(_stage, 0);
            Grid.SetRow(_playlistHost, 1);
            Grid.SetColumn(_playlistHost, 0);
            _playlistHost.BorderThickness = new Thickness(0, 1, 0, 0);
        }
        else
        {
            _layout.ColumnDefinitions.Add(new ColumnDefinition());
            _layout.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(310) });
            _layout.RowDefinitions.Add(new RowDefinition());
            Grid.SetRow(_stage, 0);
            Grid.SetColumn(_stage, 0);
            Grid.SetRow(_playlistHost, 0);
            Grid.SetColumn(_playlistHost, 1);
            _playlistHost.BorderThickness = new Thickness(1, 0, 0, 0);
        }
    }

    private void ArrangeTransportLayout(bool compact)
    {
        if (_transportGrid is null ||
            _nowPlayingPanel is null ||
            _centerControlsPanel is null ||
            _rightControlsPanel is null)
        {
            return;
        }

        _transportGrid.ColumnDefinitions.Clear();
        _transportGrid.RowDefinitions.Clear();
        if (compact)
        {
            _transportGrid.ColumnDefinitions.Add(new ColumnDefinition());
            _transportGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _transportGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            _transportGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            Grid.SetRow(_nowPlayingPanel, 0);
            Grid.SetColumn(_nowPlayingPanel, 0);
            Grid.SetRow(_centerControlsPanel, 1);
            Grid.SetColumn(_centerControlsPanel, 0);
            Grid.SetRow(_rightControlsPanel, 2);
            Grid.SetColumn(_rightControlsPanel, 0);
            _centerControlsPanel.HorizontalAlignment = HorizontalAlignment.Center;
            _rightControlsPanel.HorizontalAlignment = HorizontalAlignment.Center;
            _rightControlsPanel.Spacing = 6;
            if (_speedLabel is not null)
            {
                _speedLabel.Visibility = Visibility.Collapsed;
            }
            return;
        }

        _transportGrid.ColumnDefinitions.Add(new ColumnDefinition());
        _transportGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        _transportGrid.ColumnDefinitions.Add(new ColumnDefinition());
        Grid.SetRow(_nowPlayingPanel, 0);
        Grid.SetColumn(_nowPlayingPanel, 0);
        Grid.SetRow(_centerControlsPanel, 0);
        Grid.SetColumn(_centerControlsPanel, 1);
        Grid.SetRow(_rightControlsPanel, 0);
        Grid.SetColumn(_rightControlsPanel, 2);
        _centerControlsPanel.HorizontalAlignment = HorizontalAlignment.Center;
        _rightControlsPanel.HorizontalAlignment = HorizontalAlignment.Right;
        _rightControlsPanel.Spacing = 8;
        if (_speedLabel is not null)
        {
            _speedLabel.Visibility = Visibility.Visible;
        }
    }

    private void RenderAudioBars(bool active)
    {
        foreach (var bar in _audioBars)
        {
            bar.Height = active ? Random.Shared.Next(24, 150) : 34;
            bar.Opacity = active ? Random.Shared.NextDouble() * 0.45 + 0.55 : 0.45;
        }
    }

    private AppWindow? GetCurrentAppWindow()
    {
        var window = _ownerWindow ?? App.CurrentWindow;
        if (window is null)
        {
            return null;
        }
        var windowId = Win32Interop.GetWindowIdFromWindow(WindowNative.GetWindowHandle(window));
        return AppWindow.GetFromWindowId(windowId);
    }

    private static MediaKind DetectKind(string fileType)
    {
        var type = fileType.ToLowerInvariant();
        if (type is ".png" or ".jpg" or ".jpeg" or ".bmp" or ".gif" or ".webp" or ".tif" or ".tiff")
        {
            return MediaKind.Image;
        }
        if (type is ".mp3" or ".wav" or ".flac" or ".aac" or ".m4a" or ".ogg" or ".wma")
        {
            return MediaKind.Audio;
        }
        return MediaKind.Video;
    }

    private static string KindLabel(MediaKind kind) => kind switch
    {
        MediaKind.Image => L("图片", "Image"),
        MediaKind.Audio => L("音频", "Audio"),
        _ => L("视频", "Video")
    };

    private static string FormatTime(TimeSpan value)
    {
        return value.TotalHours >= 1
            ? $"{(int)value.TotalHours:00}:{value.Minutes:00}:{value.Seconds:00}"
            : $"{(int)value.TotalMinutes:00}:{value.Seconds:00}";
    }

    private static T WithColumn<T>(T element, int column) where T : FrameworkElement
    {
        Grid.SetColumn(element, column);
        return element;
    }

    private static T WithRow<T>(T element, int row) where T : FrameworkElement
    {
        Grid.SetRow(element, row);
        return element;
    }

    private static SolidColorBrush DarkBrush(string hex) => ModernUi.Brush(hex);

    private static string L(string zh, string en) => AppLocalizer.T(zh, en);

    private static Button PlayerCommandButton(string text, string glyph, Action click, bool primary = false)
    {
        var button = new Button
        {
            MinWidth = 116,
            Height = 38,
            Padding = new Thickness(14, 0, 14, 0),
            CornerRadius = new CornerRadius(8),
            Background = primary ? DarkBrush("#2E6B9B") : DarkBrush("#242424"),
            BorderBrush = primary ? DarkBrush("#69BDF4") : DarkBrush("#3A3A3A"),
            BorderThickness = new Thickness(1),
            HorizontalContentAlignment = HorizontalAlignment.Center,
            Content = new StackPanel
            {
                Orientation = Orientation.Horizontal,
                Spacing = 8,
                VerticalAlignment = VerticalAlignment.Center,
                Children =
                {
                    new FontIcon { Glyph = glyph, FontSize = 15, Foreground = DarkBrush("#F5F5F5") },
                    LightText(text, 13, FontWeights.SemiBold, maxLines: 1)
                }
            }
        };
        button.Click += (_, _) => click();
        ToolTipService.SetToolTip(button, text);
        return button;
    }

    private static Button LightIconButton(string glyph, string tooltip, Action click)
    {
        var button = ModernUi.IconButton(glyph, tooltip, click);
        button.BorderBrush = DarkBrush("#333333");
        button.Background = DarkBrush("#242424");
        if (button.Content is FontIcon icon)
        {
            icon.Foreground = DarkBrush("#F5F5F5");
        }

        return button;
    }

    private static TextBlock LightText(string text, double size = 14, FontWeight? weight = null, int maxLines = 0)
    {
        var block = ModernUi.Text(text, size, weight, DarkBrush("#FFFFFF"), maxLines);
        block.LineHeight = Math.Max(size * 1.28, size + 4);
        return block;
    }

    private sealed record PlaylistItem(StorageFile File, MediaKind Kind);

    private enum MediaKind
    {
        Image,
        Audio,
        Video
    }
}
