using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media;
using Windows.ApplicationModel.DataTransfer;
using YMhut.Box.Core.Logging;
using YMhut.Box.WinUI.Services;

namespace YMhut.Box.WinUI.Views;

public sealed class LogPage : Page
{
    private const int InitialLogTake = 30;
    private const int StreamLogBatchSize = 100;

    private readonly ILogService _logService = AppServices.GetRequiredService<ILogService>();
    private readonly ObservableCollection<LogEntryItem> _entryItems = [];
    private readonly ListView _entriesList = new()
    {
        SelectionMode = ListViewSelectionMode.None,
        IsItemClickEnabled = true,
        ShowsScrollingPlaceholders = true,
        HorizontalAlignment = HorizontalAlignment.Stretch,
        VerticalAlignment = VerticalAlignment.Stretch,
        Background = ModernUi.Brush("#00FFFFFF"),
        BorderThickness = new Thickness(0),
        Padding = new Thickness(0, 0, 2, 0)
    };
    private readonly Border _loadingIndicator = new()
    {
        Padding = new Thickness(16, 12, 16, 12),
        CornerRadius = new CornerRadius(8),
        Background = ModernUi.Surface,
        BorderBrush = ModernUi.Stroke,
        BorderThickness = new Thickness(1),
        Margin = new Thickness(0, 0, 16, 0),
        Visibility = Visibility.Collapsed
    };
    private readonly ProgressRing _loadingRing = new()
    {
        Width = 18,
        Height = 18,
        IsActive = false,
        VerticalAlignment = VerticalAlignment.Center
    };
    private readonly TextBlock _loadingText = ModernUi.Text(AppLocalizer.T("正在继续加载日志...", "Loading more logs..."), 13, FontWeights.SemiBold, ModernUi.TextSecondary);
    private readonly TextBox _searchBox = new()
    {
        PlaceholderText = AppLocalizer.T("搜索日志消息、分类或详情", "Search log message, category, or detail"),
        BorderThickness = new Thickness(0),
        Background = ModernUi.Brush("#00FFFFFF"),
        FontSize = 15,
        VerticalAlignment = VerticalAlignment.Center
    };
    private readonly TextBlock _summaryText = ModernUi.Text(AppLocalizer.T("正在加载日志...", "Loading logs..."), 14, FontWeights.SemiBold, ModernUi.TextSecondary);
    private readonly TextBlock _timelineDateText = ModernUi.Text("", 14, FontWeights.SemiBold, ModernUi.TextPrimary, maxLines: 1);
    private readonly Dictionary<string, Button> _filterButtons = [];
    private readonly CalendarDatePicker _datePicker = new()
    {
        Date = DateTimeOffset.Now,
        MinWidth = 172,
        Height = 42,
        PlaceholderText = AppLocalizer.T("选择日期", "Choose date")
    };
    private readonly List<LogEntry> _entries = [];
    private IReadOnlyList<LogEntry> _visibleEntries = [];
    private string _levelFilter = "";
    private CancellationTokenSource? _loadCancellation;
    private CancellationTokenSource? _searchDebounceCancellation;
    private UIElement? _emptyStateElement;
    private Task? _backgroundLoadTask;
    private readonly HashSet<string> _entryKeys = [];
    private int _loadVersion;
    private bool _allLoaded;

    public LogPage()
    {
        Background = ModernUi.AppBackground;
        ConfigureLogList();
        Content = BuildContent();
        Loaded += async (_, _) => await LoadLogsAsync();
        _logService.EntryWritten += LogService_EntryWritten;
        Unloaded += (_, _) =>
        {
            _logService.EntryWritten -= LogService_EntryWritten;
            CancelActiveLoad();
            CancelQueuedLoads();
        };
    }

    private void ConfigureLogList()
    {
        _entriesList.ItemsSource = _entryItems;
        _entriesList.ItemTemplate = CreateLogItemTemplate();
        _entriesList.ItemContainerStyle = CreateLogItemContainerStyle();
        _entriesList.ItemClick += async (_, args) =>
        {
            if (args.ClickedItem is LogEntryItem item)
            {
                await ShowLogDetailAsync(item.Entry);
            }
        };
    }

    private static DataTemplate CreateLogItemTemplate()
    {
        return (DataTemplate)XamlReader.Load("""
            <DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                <Grid ColumnSpacing="12" HorizontalAlignment="Stretch" Margin="0,0,16,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="74" />
                        <ColumnDefinition Width="38" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding TimeText}"
                               FontSize="13"
                               FontWeight="SemiBold"
                               Foreground="{Binding TextSecondaryBrush}"
                               HorizontalAlignment="Right"
                               VerticalAlignment="Top"
                               Margin="0,13,0,0" />
                    <Grid Grid.Column="1">
                        <Border Width="2"
                                Background="{Binding StrokeBrush}"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Stretch" />
                        <Border Width="34"
                                Height="34"
                                CornerRadius="8"
                                Background="{Binding LevelBackgroundBrush}"
                                BorderBrush="{Binding StrokeBrush}"
                                BorderThickness="1"
                                VerticalAlignment="Top"
                                Margin="0,5,0,0">
                            <FontIcon Glyph="{Binding LevelGlyph}"
                                      FontSize="15"
                                      Foreground="{Binding LevelBrush}"
                                      HorizontalAlignment="Center"
                                      VerticalAlignment="Center" />
                        </Border>
                    </Grid>
                    <Border Grid.Column="2"
                            Padding="14,12,14,12"
                            CornerRadius="8"
                            Background="{Binding CardBackgroundBrush}"
                            BorderBrush="{Binding StrokeBrush}"
                            BorderThickness="1"
                            HorizontalAlignment="Stretch"
                            Margin="0,0,0,10">
                        <Grid ColumnSpacing="12">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <StackPanel Spacing="6">
                                <TextBlock Text="{Binding Message}"
                                           FontSize="15"
                                           FontWeight="SemiBold"
                                           Foreground="{Binding TextPrimaryBrush}"
                                           TextWrapping="Wrap"
                                           TextTrimming="CharacterEllipsis"
                                           MaxLines="2" />
                                <TextBlock Text="{Binding DetailPreview}"
                                           FontSize="12"
                                           Foreground="{Binding TextSecondaryBrush}"
                                           TextWrapping="Wrap"
                                           TextTrimming="CharacterEllipsis"
                                           MaxLines="1" />
                                <StackPanel Orientation="Horizontal" Spacing="8">
                                    <Border Padding="8,2,8,3"
                                            CornerRadius="6"
                                            Background="{Binding BadgeBackgroundBrush}">
                                        <TextBlock Text="{Binding LevelText}"
                                                   FontSize="12"
                                                   FontWeight="SemiBold"
                                                   Foreground="{Binding LevelBrush}"
                                                   TextTrimming="CharacterEllipsis"
                                                   MaxLines="1" />
                                    </Border>
                                    <Border Padding="8,2,8,3"
                                            CornerRadius="6"
                                            Background="{Binding BadgeBackgroundBrush}">
                                        <TextBlock Text="{Binding Category}"
                                                   FontSize="12"
                                                   FontWeight="SemiBold"
                                                   Foreground="{Binding TextSecondaryBrush}"
                                                   TextTrimming="CharacterEllipsis"
                                                   MaxLines="1" />
                                    </Border>
                                </StackPanel>
                            </StackPanel>
                            <FontIcon Grid.Column="1"
                                      Glyph="&#xE8A7;"
                                      FontSize="13"
                                      Foreground="{Binding TextSecondaryBrush}"
                                      VerticalAlignment="Center"
                                      Margin="8,0,0,0" />
                        </Grid>
                    </Border>
                </Grid>
            </DataTemplate>
            """);
    }

    private static Style CreateLogItemContainerStyle()
    {
        return (Style)XamlReader.Load("""
            <Style xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                <Setter Property="Padding" Value="0" />
                <Setter Property="Margin" Value="0" />
                <Setter Property="MinHeight" Value="0" />
            </Style>
            """);
    }

    private UIElement BuildContent()
    {
        ConfigureLoadingIndicator();
        _searchBox.TextChanged += (_, _) => QueueSearchLoad();
        _datePicker.DateChanged += (_, _) => _ = LoadLogsAsync();

        var root = new Grid
        {
            Padding = new Thickness(32, 28, 32, 32),
            RowSpacing = 16
        };
        root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        root.RowDefinitions.Add(new RowDefinition());

        var header = BuildHeader();
        Grid.SetRow(header, 0);
        root.Children.Add(header);

        var board = BuildBoard();
        Grid.SetRow(board, 1);
        root.Children.Add(board);
        return root;
    }

    private FrameworkElement BuildHeader()
    {
        var titleArea = new StackPanel
        {
            Spacing = 4,
            Children =
            {
                ModernUi.Text(AppLocalizer.T("日志", "Logs"), 28, FontWeights.SemiBold),
                ModernUi.Text(AppLocalizer.T("按日期、级别和关键词查看用户操作与服务触发记录。", "View user actions and service events by date, level, and keyword."), 14, foreground: ModernUi.TextSecondary, maxLines: 2)
            }
        };

        var search = new Border
        {
            MinWidth = 180,
            MaxWidth = 520,
            Height = 42,
            HorizontalAlignment = HorizontalAlignment.Stretch,
            CornerRadius = new CornerRadius(8),
            Background = ModernUi.Surface,
            BorderBrush = ModernUi.Stroke,
            BorderThickness = new Thickness(1),
            Child = new Grid
            {
                Padding = new Thickness(13, 0, 13, 0),
                ColumnSpacing = 10,
                ColumnDefinitions =
                {
                    new ColumnDefinition { Width = GridLength.Auto },
                    new ColumnDefinition()
                },
                Children =
                {
                    new FontIcon { Glyph = "\uE721", FontSize = 15, Foreground = ModernUi.TextSecondary, VerticalAlignment = VerticalAlignment.Center },
                    _searchBox
                }
            }
        };
        Grid.SetColumn(_searchBox, 1);

        var actions = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8, VerticalAlignment = VerticalAlignment.Center };
        actions.Children.Add(ModernUi.IconButton("\uE8C8", AppLocalizer.T("复制当前全部日志", "Copy visible logs"), CopyVisibleLogs));
        actions.Children.Add(ModernUi.IconButton("\uE72C", AppLocalizer.T("刷新日志", "Refresh logs"), async () => await LoadLogsAsync()));
        actions.Children.Add(ModernUi.IconButton("\uE74D", AppLocalizer.T("删除日志", "Delete logs"), async () => await ConfirmClearAsync()));

        var right = new Grid
        {
            ColumnSpacing = 10,
            RowSpacing = 8,
            VerticalAlignment = VerticalAlignment.Bottom,
            MinWidth = 0
        };
        right.Children.Add(_datePicker);
        right.Children.Add(search);
        right.Children.Add(actions);
        right.SizeChanged += (_, e) => ArrangeHeaderActions(right, search, actions, e.NewSize.Width);
        ArrangeHeaderActions(right, search, actions, 900);

        var grid = new Grid { ColumnSpacing = 20 };
        grid.ColumnDefinitions.Add(new ColumnDefinition());
        grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.82, GridUnitType.Star) });
        grid.Children.Add(titleArea);
        Grid.SetColumn(right, 1);
        grid.Children.Add(right);
        grid.SizeChanged += (_, e) =>
        {
            var narrow = e.NewSize.Width < 760;
            grid.ColumnDefinitions.Clear();
            grid.RowDefinitions.Clear();
            grid.ColumnDefinitions.Add(new ColumnDefinition());
            if (narrow)
            {
                grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
                grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
                Grid.SetColumn(right, 0);
                Grid.SetRow(right, 1);
            }
            else
            {
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.82, GridUnitType.Star) });
                Grid.SetColumn(right, 1);
                Grid.SetRow(right, 0);
            }
        };
        return grid;
    }

    private void ArrangeHeaderActions(Grid right, Border search, StackPanel actions, double width)
    {
        if (width <= 0)
        {
            width = 900;
        }

        right.ColumnDefinitions.Clear();
        right.RowDefinitions.Clear();
        right.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        right.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        right.ColumnDefinitions.Add(new ColumnDefinition());
        right.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

        var compact = width < 620;
        var tiny = width < 460;
        _datePicker.MinWidth = tiny ? 108 : compact ? 128 : 180;
        _datePicker.Width = tiny ? 108 : compact ? 128 : double.NaN;
        search.MinWidth = tiny ? 96 : compact ? 136 : 220;
        search.MaxWidth = compact ? Math.Max(96, width - _datePicker.MinWidth - 148) : double.PositiveInfinity;

        Grid.SetColumn(_datePicker, 0);
        Grid.SetRow(_datePicker, 0);
        Grid.SetColumn(search, 1);
        Grid.SetRow(search, 0);
        Grid.SetColumn(actions, 2);
        Grid.SetRow(actions, 0);
        actions.HorizontalAlignment = HorizontalAlignment.Right;
        actions.VerticalAlignment = VerticalAlignment.Center;
    }

    private FrameworkElement BuildBoard()
    {
        var root = new Grid { RowSpacing = 14 };
        root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        root.RowDefinitions.Add(new RowDefinition());

        var filterRow = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8 };
        AddFilterButton(filterRow, "", AppLocalizer.T("全部", "All"));
        AddFilterButton(filterRow, "Information", AppLocalizer.T("信息", "Info"));
        AddFilterButton(filterRow, "Warning", AppLocalizer.T("警告", "Warning"));
        AddFilterButton(filterRow, "Error", AppLocalizer.T("错误", "Error"));

        var top = new Grid { Margin = new Thickness(0, 0, 0, 14) };
        top.ColumnDefinitions.Add(new ColumnDefinition());
        top.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        top.Children.Add(filterRow);
        _summaryText.VerticalAlignment = VerticalAlignment.Center;
        Grid.SetColumn(_summaryText, 1);
        top.Children.Add(_summaryText);
        root.Children.Add(top);

        var datePill = new Border
        {
            HorizontalAlignment = HorizontalAlignment.Left,
            Padding = new Thickness(12, 7, 12, 7),
            CornerRadius = new CornerRadius(8),
            Background = ModernUi.AccentSoft,
            BorderBrush = ModernUi.Stroke,
            BorderThickness = new Thickness(1),
            Child = new StackPanel
            {
                Orientation = Orientation.Horizontal,
                Spacing = 8,
                Children =
                {
                    new FontIcon { Glyph = "\uE787", FontSize = 14, Foreground = ModernUi.Accent, VerticalAlignment = VerticalAlignment.Center },
                    _timelineDateText
                }
            }
        };
        Grid.SetRow(datePill, 1);
        root.Children.Add(datePill);

        Grid.SetRow(_entriesList, 2);
        root.Children.Add(_entriesList);
        return root;
    }

    private void AddFilterButton(StackPanel row, string level, string label)
    {
        var dot = level switch
        {
            "Information" => ModernUi.Info,
            "Warning" => ModernUi.Bronze,
            "Error" => ModernUi.Danger,
            _ => ModernUi.TextSecondary
        };

        var button = new Button
        {
            Padding = new Thickness(12, 7, 12, 7),
            CornerRadius = new CornerRadius(6),
            BorderThickness = new Thickness(1),
            Content = new StackPanel
            {
                Orientation = Orientation.Horizontal,
                Spacing = 8,
                Children =
                {
                    new Border { Width = 8, Height = 8, CornerRadius = new CornerRadius(4), Background = dot, VerticalAlignment = VerticalAlignment.Center },
                    ModernUi.Text(label, 14, FontWeights.SemiBold)
                }
            }
        };
        button.Click += (_, _) =>
        {
            _levelFilter = level;
            UpdateFilterButtons();
            _ = LoadLogsAsync();
        };
        _filterButtons[level] = button;
        row.Children.Add(button);
        UpdateFilterButtons();
    }

    private void ConfigureLoadingIndicator()
    {
        _loadingIndicator.Child = new StackPanel
        {
            Orientation = Orientation.Horizontal,
            Spacing = 10,
            HorizontalAlignment = HorizontalAlignment.Center,
            Children =
            {
                _loadingRing,
                _loadingText
            }
        };
    }

    private void LogService_EntryWritten(object? sender, LogEntry e)
    {
        DispatcherQueue.TryEnqueue(() => HandleWrittenEntry(e));
    }

    private void HandleWrittenEntry(LogEntry entry)
    {
        var selectedDate = DateOnly.FromDateTime((_datePicker.Date ?? DateTimeOffset.Now).LocalDateTime);
        if (DateOnly.FromDateTime(entry.Timestamp.LocalDateTime) != selectedDate || !MatchesCurrentFilter(entry))
        {
            return;
        }

        if (!_allLoaded)
        {
            _summaryText.Text = AppLocalizer.T(
                $"{selectedDate:yyyy-MM-dd} · 后台加载中，新日志将在刷新后合并",
                $"{selectedDate:yyyy-MM-dd} · Background loading; new logs will be merged on refresh");
            return;
        }

        var key = EntryKey(entry);
        if (!_entryKeys.Add(key))
        {
            return;
        }

        RemoveTransientRows();
        _entries.Insert(0, entry);
        _entryItems.Insert(0, new LogEntryItem(entry));
        _visibleEntries = _entries.ToArray();
        UpdateLoadingState(isLoading: false, selectedDate);
    }

    private void QueueSearchLoad()
    {
        _searchDebounceCancellation?.Cancel();
        _searchDebounceCancellation?.Dispose();
        var cancellation = new CancellationTokenSource();
        _searchDebounceCancellation = cancellation;
        _ = RunSearchLoadAsync(cancellation);
    }

    private async Task RunSearchLoadAsync(CancellationTokenSource cancellation)
    {
        try
        {
            await Task.Delay(250, cancellation.Token);
            if (ReferenceEquals(cancellation, _searchDebounceCancellation) && !cancellation.IsCancellationRequested)
            {
                await LoadLogsAsync();
            }
        }
        catch (OperationCanceledException)
        {
        }
    }

    private async Task LoadLogsAsync()
    {
        CancelActiveLoad();
        _backgroundLoadTask = null;
        var loadVersion = Interlocked.Increment(ref _loadVersion);
        var cancellation = new CancellationTokenSource();
        _loadCancellation = cancellation;
        var selectedDate = DateOnly.FromDateTime((_datePicker.Date ?? DateTimeOffset.Now).LocalDateTime);
        var level = _levelFilter;
        var query = _searchBox.Text;
        _entries.Clear();
        _visibleEntries = [];
        _allLoaded = false;
        BeginLogRender(selectedDate);

        try
        {
            var firstPage = await ReadLogPageInBackgroundAsync(
                selectedDate,
                level,
                query,
                skip: 0,
                take: InitialLogTake,
                cancellation.Token);
            if (!IsCurrentLoad(loadVersion, cancellation))
            {
                return;
            }

            AppendLogEntries(firstPage);
            _allLoaded = firstPage.Count < InitialLogTake;
            UpdateLoadingState(!_allLoaded, selectedDate);
            if (_allLoaded)
            {
                return;
            }

            _backgroundLoadTask = LoadRemainingLogsAsync(selectedDate, level, query, loadVersion, cancellation.Token);
        }
        catch (OperationCanceledException)
        {
        }
        catch (Exception exception)
        {
            if (IsCurrentLoad(loadVersion, cancellation))
            {
                _allLoaded = true;
                RenderLoadError(exception);
            }
        }
    }

    private async Task LoadRemainingLogsAsync(DateOnly selectedDate, string level, string query, int loadVersion, CancellationToken cancellationToken)
    {
        try
        {
            var skip = _entries.Count;
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var batch = await ReadLogPageInBackgroundAsync(
                    selectedDate,
                    level,
                    query,
                    skip,
                    StreamLogBatchSize,
                    cancellationToken);
                if (!IsCurrentLoad(loadVersion, _loadCancellation))
                {
                    return;
                }

                if (batch.Count == 0)
                {
                    _allLoaded = true;
                    UpdateLoadingState(isLoading: false, selectedDate);
                    return;
                }

                AppendLogEntries(batch);
                skip += batch.Count;
                _allLoaded = batch.Count < StreamLogBatchSize;
                UpdateLoadingState(!_allLoaded, selectedDate);
                if (_allLoaded)
                {
                    return;
                }

                await Task.Delay(50, cancellationToken);
            }
        }
        catch (OperationCanceledException)
        {
        }
        catch (Exception exception)
        {
            if (IsCurrentLoad(loadVersion, _loadCancellation))
            {
                _allLoaded = true;
                RenderLoadError(exception);
            }
        }
    }

    private Task<IReadOnlyList<LogEntry>> ReadLogPageInBackgroundAsync(
        DateOnly selectedDate,
        string level,
        string query,
        int skip,
        int take,
        CancellationToken cancellationToken)
    {
        return Task.Run(
            async () => await _logService.ReadByDatePageAsync(
                selectedDate,
                level,
                query,
                skip,
                take,
                cancellationToken).ConfigureAwait(false),
            cancellationToken);
    }

    private bool IsCurrentLoad(int loadVersion, CancellationTokenSource? cancellation)
    {
        return loadVersion == _loadVersion &&
            ReferenceEquals(cancellation, _loadCancellation) &&
            cancellation is not null &&
            !cancellation.IsCancellationRequested;
    }

    private void CancelActiveLoad()
    {
        var cancellation = _loadCancellation;
        if (cancellation is null)
        {
            return;
        }

        _loadCancellation = null;
        cancellation.Cancel();
        cancellation.Dispose();
    }

    private void CancelQueuedLoads()
    {
        _searchDebounceCancellation?.Cancel();
        _searchDebounceCancellation?.Dispose();
        _searchDebounceCancellation = null;
    }

    private async Task ConfirmClearAsync()
    {
        var scope = new ComboBox
        {
            Header = AppLocalizer.T("删除范围", "Delete scope"),
            MinWidth = 260
        };
        scope.Items.Add(new ComboBoxItem { Content = AppLocalizer.T("今天", "Today"), Tag = "today", IsSelected = true });
        scope.Items.Add(new ComboBoxItem { Content = AppLocalizer.T("当前筛选", "Current filter"), Tag = "filtered" });
        scope.Items.Add(new ComboBoxItem { Content = AppLocalizer.T("全部", "All"), Tag = "all" });

        var dialog = new ContentDialog
        {
            Title = AppLocalizer.T("删除日志？", "Delete logs?"),
            Content = new StackPanel
            {
                Spacing = 12,
                Children =
                {
                    ModernUi.Text(AppLocalizer.T("请选择要清理的日志范围。此操作会写入本地 SQLite 日志库，无法撤销。", "Choose which logs to clear. This operation affects the local SQLite log store and cannot be undone."), 14, foreground: ModernUi.TextSecondary),
                    scope
                }
            },
            PrimaryButtonText = AppLocalizer.T("删除", "Delete"),
            CloseButtonText = AppLocalizer.T("取消", "Cancel"),
            DefaultButton = ContentDialogButton.Close,
            XamlRoot = XamlRoot
        };
        var result = await dialog.ShowAsync();
        if (result != ContentDialogResult.Primary)
        {
            return;
        }

        var selected = scope.SelectedItem is ComboBoxItem item ? (string)item.Tag : "today";
        switch (selected)
        {
            case "all":
                await _logService.ClearAllAsync();
                break;
            case "filtered":
                await _logService.ClearFilteredTodayAsync(_levelFilter, _searchBox.Text);
                break;
            default:
                await _logService.ClearTodayAsync();
                break;
        }

        await LoadLogsAsync();
        ToastService.Show(selected switch
        {
            "all" => AppLocalizer.T("全部日志已清理", "All logs cleared"),
            "filtered" => AppLocalizer.T("当前筛选日志已清理", "Filtered logs cleared"),
            _ => AppLocalizer.T("今天的日志已清理", "Today's logs cleared")
        }, ToastKind.Success);
    }

    private void UpdateFilterButtons()
    {
        foreach (var pair in _filterButtons)
        {
            var selected = string.Equals(pair.Key, _levelFilter, StringComparison.OrdinalIgnoreCase);
            pair.Value.Background = selected ? ModernUi.AccentSoft : ModernUi.Surface;
            pair.Value.BorderBrush = selected ? ModernUi.Accent : ModernUi.Stroke;
        }
    }

    private void BeginLogRender(DateOnly selectedDate)
    {
        _entryItems.Clear();
        _entryKeys.Clear();
        _emptyStateElement = null;
        _entriesList.Header = null;
        _timelineDateText.Text = FormatTimelineDate(selectedDate);
        _summaryText.Text = AppLocalizer.T($"{selectedDate:yyyy-MM-dd} · 正在加载前 {InitialLogTake} 条日志...", $"{selectedDate:yyyy-MM-dd} · Loading first {InitialLogTake} logs...");
        ShowLoadingIndicator(AppLocalizer.T("正在加载首屏日志...", "Loading initial logs..."));
    }

    private void AppendLogEntries(IReadOnlyList<LogEntry> entries)
    {
        if (entries.Count == 0)
        {
            _visibleEntries = _entries.ToArray();
            return;
        }

        RemoveTransientRows();
        foreach (var entry in entries)
        {
            if (!_entryKeys.Add(EntryKey(entry)))
            {
                continue;
            }

            _entries.Add(entry);
            _entryItems.Add(new LogEntryItem(entry));
        }

        _visibleEntries = _entries.ToArray();
    }

    private void UpdateLoadingState(bool isLoading, DateOnly selectedDate)
    {
        _visibleEntries = _entries.ToArray();
        _summaryText.Text = isLoading
            ? AppLocalizer.T($"{selectedDate:yyyy-MM-dd} · 已加载 {_entries.Count} 条，继续加载中...", $"{selectedDate:yyyy-MM-dd} · {_entries.Count} loaded, loading more...")
            : AppLocalizer.T($"{selectedDate:yyyy-MM-dd} · 已加载全部 {_entries.Count} 条日志", $"{selectedDate:yyyy-MM-dd} · All {_entries.Count} logs loaded");

        if (_entries.Count == 0)
        {
            RemoveTransientRows();
            if (isLoading)
            {
                ShowLoadingIndicator(AppLocalizer.T("正在读取所选日期日志...", "Reading logs for the selected date..."));
            }
            else
            {
                ShowEmptyState(AppLocalizer.T("所选日期暂无匹配日志。", "No matching logs for the selected date."));
            }
            return;
        }

        if (isLoading)
        {
            ShowLoadingIndicator(AppLocalizer.T("正在继续加载当天剩余日志...", "Loading the rest of today's logs..."));
        }
        else
        {
            HideLoadingIndicator();
        }
    }

    private void RenderLoadError(Exception exception)
    {
        var safe = AppLocalizer.SanitizeSensitiveText(exception.Message, 220);
        _summaryText.Text = AppLocalizer.T("日志加载失败", "Failed to load logs");
        _entryItems.Clear();
        HideLoadingIndicator();
        _entriesList.Header = ModernUi.Card(new StackPanel
        {
            Spacing = 10,
            Children =
            {
                ModernUi.Text(AppLocalizer.T("日志加载失败", "Failed to load logs"), 18, FontWeights.SemiBold, ModernUi.Danger),
                ModernUi.Text(safe, 13, foreground: ModernUi.TextSecondary),
                ModernUi.PillButton(AppLocalizer.T("重试", "Retry"), "\uE72C", async () => await LoadLogsAsync(), primary: true)
            }
        }, new Thickness(16), margin: new Thickness(0, 0, 16, 0), radius: 8);
    }

    private void ShowLoadingIndicator(string text)
    {
        RemoveEmptyState();
        _loadingText.Text = text;
        _loadingIndicator.Visibility = Visibility.Visible;
        _loadingRing.IsActive = true;
        _entriesList.Footer = _loadingIndicator;
    }

    private void HideLoadingIndicator()
    {
        _loadingRing.IsActive = false;
        _loadingIndicator.Visibility = Visibility.Collapsed;
        if (ReferenceEquals(_entriesList.Footer, _loadingIndicator))
        {
            _entriesList.Footer = null;
        }
    }

    private void ShowEmptyState(string message)
    {
        RemoveEmptyState();
        _emptyStateElement = new Border
        {
            Padding = new Thickness(22),
            CornerRadius = new CornerRadius(8),
            Background = ModernUi.Surface,
            BorderBrush = ModernUi.Stroke,
            BorderThickness = new Thickness(1),
            Margin = new Thickness(0, 0, 16, 0),
            Child = ModernUi.Text(message, 16, FontWeights.SemiBold, ModernUi.TextSecondary)
        };
        _entriesList.Header = _emptyStateElement;
    }

    private void RemoveTransientRows()
    {
        HideLoadingIndicator();
        RemoveEmptyState();
    }

    private void RemoveEmptyState()
    {
        if (_emptyStateElement is null)
        {
            return;
        }

        if (ReferenceEquals(_entriesList.Header, _emptyStateElement))
        {
            _entriesList.Header = null;
        }

        _emptyStateElement = null;
    }

    private async Task ShowLogDetailAsync(LogEntry entry)
    {
        var rows = new StackPanel { Spacing = 10 };
        foreach (var row in new[]
        {
            (AppLocalizer.T("时间", "Time"), entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff")),
            (AppLocalizer.T("级别", "Level"), DisplayLevel(entry.Level)),
            (AppLocalizer.T("分类", "Category"), DisplayCategory(entry.Category)),
            (AppLocalizer.T("消息", "Message"), DisplayMessage(entry.Message)),
            (AppLocalizer.T("详情", "Detail"), DisplayDetail(entry.Detail))
        }.Where(row => !string.IsNullOrWhiteSpace(row.Item2)))
        {
            rows.Children.Add(new StackPanel
            {
                Spacing = 4,
                Children =
                {
                    ModernUi.Text(row.Item1, 13, FontWeights.SemiBold, ModernUi.TextSecondary),
                    ModernUi.Text(row.Item2, 14)
                }
            });
        }

        var dialog = new ContentDialog
        {
            Title = AppLocalizer.T("日志详情", "Log detail"),
            Content = ModernUi.GutterScroll(rows, 520),
            CloseButtonText = AppLocalizer.T("关闭", "Close"),
            XamlRoot = XamlRoot
        };
        await dialog.ShowAsync();
    }

    private void CopyVisibleLogs()
    {
        if (_visibleEntries.Count == 0)
        {
            ToastService.Show(AppLocalizer.T("当前没有可复制的日志", "No logs to copy"), ToastKind.Info);
            return;
        }

        var text = string.Join(Environment.NewLine, _visibleEntries.Select(entry =>
            $"{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{DisplayLevel(entry.Level)}] {DisplayCategory(entry.Category)} - {DisplayMessage(entry.Message)} {DisplayDetail(entry.Detail)}".Trim()));
        var package = new DataPackage();
        package.SetText(text);
        Clipboard.SetContent(package);
        ToastService.Show(AppLocalizer.T("当前日志已复制", "Visible logs copied"), ToastKind.Success);
    }

    private bool MatchesCurrentFilter(LogEntry entry)
    {
        if (!string.IsNullOrWhiteSpace(_levelFilter) &&
            !string.Equals(entry.Level, _levelFilter, StringComparison.OrdinalIgnoreCase))
        {
            return false;
        }

        var query = _searchBox.Text?.Trim();
        return string.IsNullOrWhiteSpace(query) ||
            entry.Message.Contains(query, StringComparison.OrdinalIgnoreCase) ||
            entry.Category.Contains(query, StringComparison.OrdinalIgnoreCase) ||
            (entry.Detail?.Contains(query, StringComparison.OrdinalIgnoreCase) ?? false);
    }

    private static string EntryKey(LogEntry entry)
    {
        return string.Join('\u001F',
            entry.Timestamp.ToString("O"),
            entry.Level,
            entry.Category,
            entry.Message,
            entry.Detail ?? string.Empty);
    }

    private static string FormatTimelineDate(DateOnly date)
    {
        var value = date.ToDateTime(TimeOnly.MinValue);
        return AppLocalizer.IsEnglish
            ? $"{value:yyyy-MM-dd} · {value:dddd}"
            : $"{value:yyyy-MM-dd} · {value:dddd}";
    }

    private static string Sanitize(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        return Regex.Replace(value, @"https?://[^\s]+", AppLocalizer.T("[已隐藏链接]", "[hidden link]"), RegexOptions.IgnoreCase);
    }

    private static string DisplayLevel(string? level) =>
        LogDisplayLocalizer.Level(level, AppLocalizer.CurrentLanguage);

    private static string DisplayCategory(string? category) =>
        Sanitize(LogDisplayLocalizer.Category(category, AppLocalizer.CurrentLanguage));

    private static string DisplayMessage(string? message) =>
        Sanitize(LogDisplayLocalizer.Message(message, AppLocalizer.CurrentLanguage));

    private static string DisplayDetail(string? detail) =>
        Sanitize(LogDisplayLocalizer.Detail(detail, AppLocalizer.CurrentLanguage));

    public sealed class LogEntryItem
    {
        public LogEntryItem(LogEntry entry)
        {
            Entry = entry;
            Message = DisplayMessage(entry.Message);
            Category = DisplayCategory(entry.Category);
            DetailPreview = string.IsNullOrWhiteSpace(entry.Detail) ? DisplayCategory(entry.Category) : DisplayDetail(entry.Detail);
            TimeText = entry.Timestamp.ToString("HH:mm:ss");
            LevelText = DisplayLevel(entry.Level);
            LevelBrush = entry.Level switch
            {
                "Warning" => ModernUi.Bronze,
                "Error" => ModernUi.Danger,
                _ => ModernUi.Info
            };
            LevelGlyph = entry.Level switch
            {
                "Warning" => "\uE7BA",
                "Error" => "\uEA39",
                _ => "\uE946"
            };
        }

        public LogEntry Entry { get; }

        public string Message { get; }

        public string Category { get; }

        public string DetailPreview { get; }

        public string TimeText { get; }

        public string LevelText { get; }

        public string LevelGlyph { get; }

        public Brush LevelBrush { get; }

        public Brush LevelBackgroundBrush => ModernUi.SurfaceAlt;

        public Brush BadgeBackgroundBrush => ModernUi.SurfaceAlt;

        public Brush CardBackgroundBrush => ModernUi.Surface;

        public Brush StrokeBrush => ModernUi.Stroke;

        public Brush TextPrimaryBrush => ModernUi.TextPrimary;

        public Brush TextSecondaryBrush => ModernUi.TextSecondary;
    }
}
