// lib/widgets/collection/app_bar/collection_app_bar.dart

import 'dart:async';
import 'dart:math';

import 'package:aves/app_mode.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/container/set_and.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/query.dart';
import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/query.dart';
import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/collection/filter_bar.dart';
import 'package:aves/widgets/collection/query_bar.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/rate_button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/tag_button.dart';
import 'package:aves/widgets/common/action_controls/togglers/favourite.dart';
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/basic/popup/container.dart';
import 'package:aves/widgets/common/basic/popup/expansion_panel.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_app_bar.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:aves/widgets/dialogs/tile_view_dialog.dart';
import 'package:aves/widgets/search/collection_search_delegate.dart';
import 'package:aves/widgets/viewer/controls/notifications.dart';
import 'package:aves_model/aves_model.dart';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';

// Remote
import 'package:aves/widgets/collection/remote_status_button.dart';

// Cast (modulare)
import 'cast/cast_icon_button.dart';
import 'cast/cast_bar_inline.dart';

import 'package:aves/widgets/cast/cast_controller.dart';

import 'package:aves/widgets/activity/activity_files_page.dart';

class CollectionAppBar extends StatefulWidget {
  final ValueNotifier<double> appBarHeightNotifier;
  final ScrollController scrollController;
  final CollectionLens collection;

  const CollectionAppBar({
    super.key,
    required this.appBarHeightNotifier,
    required this.scrollController,
    required this.collection,
  });

  @override
  State<CollectionAppBar> createState() => _CollectionAppBarState();
}

class _CollectionAppBarState extends State<CollectionAppBar>
    with RouteAware, SingleTickerProviderStateMixin, WidgetsBindingObserver {
  final Set<StreamSubscription> _subscriptions = {};
  final EntrySetActionDelegate _actionDelegate = EntrySetActionDelegate();
  late AnimationController _browseToSelectAnimation;
  final ValueNotifier<bool> _isSelectingNotifier = ValueNotifier(false);
  final FocusNode _queryBarFocusNode = FocusNode();
  late final Listenable _queryFocusRequestNotifier;
  double _statusBarHeight = 0;

  static const double _castBarHeight = 48;

  CollectionLens get collection => widget.collection;

  bool get isTrash => collection.filters.contains(TrashFilter.instance);

  CollectionSource get source => collection.source;

  Set<CollectionFilter> get visibleFilters => collection.filters
      .where((v) => !(v is QueryFilter && v.live) && v is! TrashFilter)
      .toSet();

  bool get showFilterBar => visibleFilters.isNotEmpty;

  void _onBackendChanged() {
    if (!mounted) return;
    setState(() {});
  }


  static const _sortOptions = [
    EntrySortFactor.date,
    EntrySortFactor.size,
    EntrySortFactor.name,
    EntrySortFactor.rating,
    EntrySortFactor.duration,
    EntrySortFactor.path,
  ];

  static const _sectionOptions = [
    EntrySectionFactor.album,
    EntrySectionFactor.month,
    EntrySectionFactor.day,
    EntrySectionFactor.none,
  ];

  static const _layoutOptions = [
    TileLayout.mosaic,
    TileLayout.grid,
    TileLayout.list,
  ];

  static const _trashSelectionQuickActions = [
    EntrySetAction.delete,
    EntrySetAction.restore,
  ];

  @override
  void initState() {
    super.initState();
    CastController.instance.backendNotifier.addListener(_onBackendChanged);


    final query = context.read<Query>();
    _subscriptions.add(query.enabledStream.listen((e) => _updateAppBarHeight()));
    _queryFocusRequestNotifier = query.focusRequestNotifier;
    _queryFocusRequestNotifier.addListener(_onQueryFocusRequest);
    _queryBarFocusNode.addListener(_onQueryBarFocusChanged);

    _browseToSelectAnimation = AnimationController(
      duration: context.read<DurationsData>().iconAnimation,
      vsync: this,
    );

    _isSelectingNotifier.addListener(_onActivityChanged);

    // Importante: ricostruisce la AppBar quando appare/scompare la cast bar
    CastController.instance.isCastingNotifier.addListener(_onCastVisibilityChanged);

    _registerWidget(widget);
    WidgetsBinding.instance.addObserver(this);

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateStatusBarHeight();
      _onFilterChanged();
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final route = ModalRoute.of(context);
    if (route is PageRoute) {
      AvesApp.pageRouteObserver.subscribe(this, route);
    }
  }

  @override
  void didUpdateWidget(covariant CollectionAppBar oldWidget) {
    super.didUpdateWidget(oldWidget);
    _unregisterWidget(oldWidget);
    _registerWidget(widget);
  }

  @override
  void dispose() {
    _unregisterWidget(widget);

    CastController.instance.isCastingNotifier.removeListener(_onCastVisibilityChanged);
    CastController.instance.backendNotifier.removeListener(_onBackendChanged);


    _queryBarFocusNode.dispose();
    _queryFocusRequestNotifier.removeListener(_onQueryFocusRequest);
    _queryBarFocusNode.removeListener(_onQueryBarFocusChanged);
    _isSelectingNotifier.dispose();
    _browseToSelectAnimation.dispose();

    _subscriptions
      ..forEach((sub) => sub.cancel())
      ..clear();

    WidgetsBinding.instance.removeObserver(this);
    AvesApp.pageRouteObserver.unsubscribe(this);
    super.dispose();
  }

  void _registerWidget(CollectionAppBar widget) {
    widget.collection.filterChangeNotifier.addListener(_onFilterChanged);
  }

  void _unregisterWidget(CollectionAppBar widget) {
    widget.collection.filterChangeNotifier.removeListener(_onFilterChanged);
  }

  void _onCastVisibilityChanged() {
    if (!mounted) return;
    setState(() {});
    _updateAppBarHeight();
  }

  void _onQueryFocusRequest() {
    if (_queryFocusRequestNotifier is ValueNotifier<bool> &&
        (_queryFocusRequestNotifier as ValueNotifier<bool>).value) {
      _queryBarFocusNode.requestFocus();
    }
  }

  void _onQueryBarFocusChanged() {
    if (_queryBarFocusNode.hasFocus) {
      _scrollToTop();
    }
    _updateAppBarHeight();
  }

  void _onActivityChanged() {
    if (!mounted) return;

    if (context.read<Selection<AvesEntry>>().isSelecting) {
      _browseToSelectAnimation.forward();
    } else {
      _browseToSelectAnimation.reverse();
    }
    _updateAppBarHeight();
  }

  void _onFilterChanged() {
    _updateAppBarHeight();

    final filters = collection.filters;
    if (filters.isNotEmpty) {
      final selection = context.read<Selection<AvesEntry>>();
      if (selection.isSelecting) {
        final toRemove = selection.selectedItems
            .where((entry) => !filters.every((f) => f.test(entry)))
            .toSet();
        selection.removeFromSelection(toRemove);
      }
    }
  }

  void _scrollToTop() => widget.scrollController.jumpTo(0);

  void _updateStatusBarHeight() {
    if (!mounted) return;
    final padding = MediaQuery.paddingOf(context);
    if (_statusBarHeight != padding.top) {
      setState(() => _statusBarHeight = padding.top);
      _updateAppBarHeight();
    }
  }

  // FIX PRINCIPALE: altezza reale completa della AppBar
  void _updateAppBarHeight() {
    widget.appBarHeightNotifier.value =
        _statusBarHeight +
        AvesAppBar.appBarHeightForContentHeight(appBarContentHeight);
  }

  @override
  void didPushNext() {
    _queryBarFocusNode.unfocus();
  }

  @override
  void didChangeMetrics() {
    WidgetsBinding.instance.addPostFrameCallback((_) => _updateStatusBarHeight());
  }

  @override
  Widget build(BuildContext context) {
    final appMode = context.watch<ValueNotifier<AppMode>>().value;
    final selection = context.watch<Selection<AvesEntry>>();
    final isSelecting = selection.isSelecting;
    _isSelectingNotifier.value = isSelecting;

    return NotificationListener<ScrollNotification>(
      onNotification: (notification) => false,
      child: AnimatedBuilder(
        animation: collection.filterChangeNotifier,
        builder: (context, child) {
          final canRemoveFilters = appMode != AppMode.pickFilteredMediaInternal;

          return Selector<Query, bool>(
            selector: (context, query) => query.enabled,
            builder: (context, queryEnabled, child) {
              return Selector<Settings, List<EntrySetAction>>(
                selector: (context, s) => s.collectionBrowsingQuickActions,
                builder: (context, _, child) {
                  final useTvLayout = settings.useTvLayout;
                  final onFilterTap =
                      canRemoveFilters ? collection.removeFilter : null;

                  return AvesAppBar(
                    contentHeight: appBarContentHeight,
                    pinned: context.select<Selection<AvesEntry>, bool>(
                      (selection) => selection.isSelecting,
                    ),
                    leading: _buildAppBarLeading(
                      hasDrawer: appMode.canNavigate,
                      isSelecting: isSelecting,
                    ),
                    title: _buildAppBarTitle(isSelecting),
                    actions: (context, maxWidth) => _buildAppBarActions(
                      context: context,
                      selection: selection,
                      maxWidth: maxWidth,
                      useTvLayout: useTvLayout,
                    ),
                    bottom: PreferredSize(
                      preferredSize: Size.fromHeight(
                        _computeBottomHeight(context, queryEnabled),
                      ),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          if (CastController.instance.isCastingNotifier.value)
                            const CastBarInline(),

                          if (useTvLayout)
                            SizedBox(
                              height: CaptionedButton.getTelevisionButtonHeight(context),
                              child: ListView(
                                padding: const EdgeInsets.symmetric(horizontal: 8),
                                scrollDirection: Axis.horizontal,
                                children: _buildActions(
                                  context,
                                  selection,
                                  double.infinity,
                                ),
                              ),
                            ),

                          if (showFilterBar)
                            NotificationListener(
                              onNotification: (notification) {
                                if (notification is SelectFilterNotification) {
                                  collection.addFilters({notification.filter});
                                  return true;
                                } else if (notification is DecomposeFilterNotification) {
                                  final filter = notification.filter;
                                  if (filter is DynamicAlbumFilter) {
                                    final innerFilter = filter.filter;
                                    final newFilters =
                                        innerFilter is SetAndFilter
                                            ? innerFilter.innerFilters
                                            : {innerFilter};
                                    collection.addFilters(newFilters);
                                    collection.removeFilter(filter);
                                    return true;
                                  }
                                }
                                return false;
                              },
                              child: FilterBar(
                                filters: visibleFilters,
                                onTap: onFilterTap,
                                onRemove: onFilterTap,
                              ),
                            ),

                          if (queryEnabled)
                            EntryQueryBar(
                              queryNotifier:
                                  context.select<Query, ValueNotifier<String>>(
                                (query) => query.queryNotifier,
                              ),
                              focusNode: _queryBarFocusNode,
                            ),
                        ],
                      ),
                    ),
                    transitionKey: isSelecting,
                  );
                },
              );
            },
          );
        },
      ),
    );
  }

  double _computeBottomHeight(BuildContext context, bool queryEnabled) {
    double height = 0;

    if (CastController.instance.isCastingNotifier.value) {
      height += _castBarHeight;
    }

    if (settings.useTvLayout) {
      height += CaptionedButton.getTelevisionButtonHeight(context);
    }

    if (showFilterBar) {
      height += FilterBar.preferredHeight;
    }

    if (queryEnabled) {
      final textScaler = MediaQuery.textScalerOf(context);
      height += EntryQueryBar.getPreferredHeight(textScaler);
    }

    return height;
  }

  // -------------------------------------------------------------
  // ACTIONS
  // -------------------------------------------------------------
  List<Widget> _buildAppBarActions({
    required BuildContext context,
    required Selection<AvesEntry> selection,
    required double maxWidth,
    required bool useTvLayout,
  }) {
    final statusButton = Padding(
      padding: const EdgeInsets.symmetric(horizontal: 6),
      child: RemoteStatusButton(source: source),
    );

    if (useTvLayout) {
      return [
        statusButton,
        const CastIconButton(),
      ];
    }


/*return [
  statusButton,

  SizedBox(
    width: 48,
    height: kToolbarHeight,
    child: Center(
      child: Stack(
        alignment: Alignment.center,
        children: [
          // LETTERA SOPRA, IN ALTO A DESTRA, STESSO COLORE DELL’ICONA
          Positioned(
            top: 2,
            right: 6,
            child: ValueListenableBuilder(
              valueListenable: CastController.instance.backendNotifier,
              builder: (_, __, ___) {
                return ValueListenableBuilder(
                  valueListenable: CastController.instance.castIconState,
                  builder: (_, __, ___) {
                    return Text(
                      CastController.instance.backendLetter,
                      style: TextStyle(
                        fontSize: 11,
                        fontWeight: FontWeight.bold,
                        color: CastController.instance.iconColor, // ← ECCO QUI
                        //color: Colors.red,
                        height: 1.0,
                      ),
                    );
                  },
                );
              },
            ),
          ),

          const CastIconButton(),
        ],
      ),
    ),
  ),

  ..._buildActions(context, selection, maxWidth),
];
*/
return [
  statusButton,

  SizedBox(
    width: 48,
    height: kToolbarHeight,
    child: Center(
      child: Stack(
        alignment: Alignment.center,
        children: [
          // SCRITTA IN BASSO, CENTRATA, STESSO COLORE DELL’ICONA
          Positioned(
            bottom: 0, // leggermente sotto l’icona
            child: ValueListenableBuilder(

valueListenable: CastController.instance.backendNotifier,
builder: (_, __, ___) {
  return ValueListenableBuilder(
    valueListenable: CastController.instance.castIconState,
    builder: (_, __, ___) {
      return Text(
        CastController.instance.backendNotifier.value.name,
        style: TextStyle(
          fontSize: 10,
          fontWeight: FontWeight.w600,
          color: CastController.instance.castIconState.value == CastIconState.connected
              ? Colors.blueAccent
              : Colors.black,
          height: 1.0,
        ),
      );
    },
  );
}

            ),
          ),

          const CastIconButton(),
        ],
      ),
    ),
  ),

  ..._buildActions(context, selection, maxWidth),
];

  }

  double get appBarContentHeight {
    final textScaler = MediaQuery.textScalerOf(context);
    double height = textScaler.scale(kToolbarHeight);

    if (settings.useTvLayout) {
      height += CaptionedButton.getTelevisionButtonHeight(context);
    }
    if (showFilterBar) {
      height += FilterBar.preferredHeight;
    }
    if (context.read<Query>().enabled) {
      height += EntryQueryBar.getPreferredHeight(textScaler);
    }

    if (CastController.instance.isCastingNotifier.value) {
      height += _castBarHeight;
    }

    return height;
  }

  Widget? _buildAppBarLeading({
    required bool hasDrawer,
    required bool isSelecting,
  }) {
    if (settings.useTvLayout) return null;

    if (!hasDrawer) {
      return const CloseButton();
    }

    VoidCallback? onPressed;
    String? tooltip;

    if (isSelecting) {
      onPressed = () => context.read<Selection<AvesEntry>>().browse();
      tooltip = MaterialLocalizations.of(context).backButtonTooltip;
    } else {
      onPressed = Scaffold.of(context).openDrawer;
      tooltip = MaterialLocalizations.of(context).openAppDrawerTooltip;
    }

    return IconButton(
      key: const Key('appbar-leading-button'),
      icon: AnimatedIcon(
        icon: AnimatedIcons.menu_arrow,
        progress: _browseToSelectAnimation,
      ),
      onPressed: onPressed,
      tooltip: tooltip,
    );
  }

  Widget _buildAppBarTitle(bool isSelecting) {
    final l10n = context.l10n;

    if (isSelecting) {
      return Selector<Selection<AvesEntry>?, int>(
        selector: (context, selection) => selection?.selectedItems.length ?? 0,
        builder: (context, count, child) => Text(
          count == 0 ? l10n.collectionSelectPageTitle : l10n.itemCount(count),
          softWrap: false,
          overflow: TextOverflow.fade,
          maxLines: 1,
        ),
      );
    } else {
      final appMode = context.watch<ValueNotifier<AppMode>>().value;

      Widget title = Text(
        appMode.isPickingMedia
            ? l10n.collectionPickPageTitle
            : (isTrash ? l10n.binPageTitle : l10n.collectionPageTitle),
        softWrap: false,
        overflow: TextOverflow.fade,
        maxLines: 1,
      );

      if (appMode == AppMode.main) {
        title = SourceStateAwareAppBarTitle(
          title: title,
          source: source,
        );
      }

      return InteractiveAppBarTitle(
        onTap: appMode.canNavigate ? _goToSearch : null,
        child: title,
      );
    }
  }

  void _goToSearch() {
    Navigator.maybeOf(context)?.push(
      SearchPageRoute(
        delegate: CollectionSearchDelegate(
          searchFieldLabel: context.l10n.searchCollectionFieldHint,
          searchFieldStyle: Themes.searchFieldStyle(context),
          source: collection.source,
          parentCollection: collection,
        ),
      ),
    );
  }

  List<Widget> _buildActions(
      BuildContext context, Selection<AvesEntry> selection, double maxWidth) {
    final appMode = context.watch<ValueNotifier<AppMode>>().value;
    final isSelecting = selection.isSelecting;
    final selectedItemCount = selection.selectedItems.length;

    bool isVisible(EntrySetAction action) => _actionDelegate.isVisible(
          action,
          appMode: appMode,
          isSelecting: isSelecting,
          itemCount: collection.entryCount,
          selectedItemCount: selectedItemCount,
          isTrash: isTrash,
        );

    bool canApply(EntrySetAction action) => _actionDelegate.canApply(
          action,
          isSelecting: isSelecting,
          collection: collection,
          selectedItemCount: selectedItemCount,
        );

    return settings.useTvLayout
        ? _buildTelevisionActions(
            context: context,
            appMode: appMode,
            selection: selection,
            isVisible: isVisible,
            canApply: canApply,
          )
        : _buildMobileActions(
            context: context,
            appMode: appMode,
            selection: selection,
            maxWidth: maxWidth,
            isVisible: isVisible,
            canApply: canApply,
          );
  }

  List<Widget> _buildTelevisionActions({
    required BuildContext context,
    required AppMode appMode,
    required Selection<AvesEntry> selection,
    required bool Function(EntrySetAction action) isVisible,
    required bool Function(EntrySetAction action) canApply,
  }) {
    final isSelecting = selection.isSelecting;

    return [
      ...EntrySetActions.general,
      ...isSelecting
          ? EntrySetActions.pageSelection
          : EntrySetActions.pageBrowsing,
    ].nonNulls.where(isVisible).map((action) {
      final enabled = canApply(action);
      return CaptionedButton(
        iconButtonBuilder: (context, focusNode) => _buildButtonIcon(
          context,
          action,
          enabled: enabled,
          selection: selection,
          focusNode: focusNode,
        ),
        captionText: _buildButtonCaption(
          context,
          action,
          enabled: enabled,
        ),
        onPressed: enabled ? () => _onActionSelected(action) : null,
      );
    }).toList();
  }

  static double _iconButtonWidth(BuildContext context) {
    const defaultPadding = EdgeInsets.all(8);
    const defaultIconSize = 24.0;
    return defaultPadding.horizontal +
        MediaQuery.textScalerOf(context).scale(defaultIconSize);
  }

  List<Widget> _buildMobileActions({
    required BuildContext context,
    required AppMode appMode,
    required Selection<AvesEntry> selection,
    required double maxWidth,
    required bool Function(EntrySetAction action) isVisible,
    required bool Function(EntrySetAction action) canApply,
  }) {
    final availableCount = (maxWidth / _iconButtonWidth(context)).floor();

    final isSelecting = selection.isSelecting;
    final selectedItemCount = selection.selectedItems.length;
    final hasSelection = selectedItemCount > 0;

    final browsingQuickActions = settings.collectionBrowsingQuickActions;
    final selectionQuickActions =
        isTrash ? _trashSelectionQuickActions : settings.collectionSelectionQuickActions;

    final quickActions = (isSelecting ? selectionQuickActions : browsingQuickActions)
        .take(max(0, availableCount - 1))
        .toList();

    final quickActionButtons = quickActions
        .where(isVisible)
        .map((action) => _buildButtonIcon(
              context,
              action,
              enabled: canApply(action),
              selection: selection,
            ));

    final animations =
        context.select<Settings, AccessibilityAnimations>((v) => v.accessibilityAnimations);

    return [
      ...quickActionButtons,
      PopupMenuButton<EntrySetAction>(
        key: const Key('appbar-menu-button'),
        itemBuilder: (context) {
          bool isValidForMenu(EntrySetAction? v) =>
              v == null || (!quickActions.contains(v) && isVisible(v));

          final generalMenuItems = EntrySetActions.general
              .where(isValidForMenu)
              .map((action) =>
                  _toMenuItem(action, enabled: canApply(action), selection: selection));

          final allContextualActions =
              isSelecting ? EntrySetActions.pageSelection : EntrySetActions.pageBrowsing;

          final contextualMenuActions =
              allContextualActions.where(isValidForMenu).fold<List<EntrySetAction?>>(
            [],
            (prev, v) {
              if (v == null && (prev.isEmpty || prev.last == null)) return prev;
              return [...prev, v];
            },
          );

          if (contextualMenuActions.isNotEmpty && contextualMenuActions.last == null) {
            contextualMenuActions.removeLast();
          }

          final contextualMenuItems = <PopupMenuEntry<EntrySetAction>>[
            ...contextualMenuActions.map((action) {
              if (action == null) return const PopupMenuDivider();
              return _toMenuItem(
                action,
                enabled: canApply(action),
                selection: selection,
              );
            }),
            if (isSelecting &&
                !settings.isReadOnly &&
                appMode == AppMode.main &&
                !isTrash)
              PopupMenuExpansionPanel<EntrySetAction>(
                enabled: hasSelection,
                value: 'edit',
                icon: AIcons.edit,
                title: context.l10n.collectionActionEdit,
                items: [
                  _buildRotateAndFlipMenuItems(context, canApply: canApply),
                  ...EntrySetActions.edit
                      .where((v) => isVisible(v) && !quickActions.contains(v))
                      .map((action) => _toMenuItem(
                            action,
                            enabled: canApply(action),
                            selection: selection,
                          )),
                ],
              ),
          ];

          return [
            ...generalMenuItems,
            if (contextualMenuItems.isNotEmpty) ...[
              const PopupMenuDivider(),
              ...contextualMenuItems,
            ],
          ];
        },
        onSelected: (action) async {
          await Future.delayed(animations.popUpAnimationDelay * timeDilation);
          await _onActionSelected(action);
        },
        popUpAnimationStyle: animations.popUpAnimationStyle,
      ),
    ];
  }

  Set<AvesEntry> _getExpandedSelectedItems(Selection<AvesEntry> selection) {
    return selection.selectedItems
        .expand((entry) => entry.stackedEntries ?? {entry})
        .toSet();
  }

  Key _getActionKey(EntrySetAction action) => Key('menu-${action.name}');

  Widget _buildButtonIcon(
    BuildContext context,
    EntrySetAction action, {
    required bool enabled,
    FocusNode? focusNode,
    required Selection<AvesEntry> selection,
  }) {
    final blurred = settings.enableBlurEffect;
    final onPressed = enabled ? () => _onActionSelected(action) : null;

    switch (action) {
      case EntrySetAction.toggleTitleSearch:
        return Selector<Query?, bool>(
          selector: (context, query) => query?.enabled ?? false,
          builder: (context, queryEnabled, child) {
            return TitleSearchToggler(
              queryEnabled: queryEnabled,
              onPressed: onPressed,
              focusNode: focusNode,
            );
          },
        );

      case EntrySetAction.copy:
        return MoveButton(
          copy: true,
          blurred: blurred,
          onChooserValue: (album) =>
              _actionDelegate.quickMove(context, album, copy: true),
          onPressed: onPressed,
        );

      case EntrySetAction.move:
        return MoveButton(
          copy: false,
          blurred: blurred,
          onChooserValue: (album) =>
              _actionDelegate.quickMove(context, album, copy: false),
          onPressed: onPressed,
        );

      case EntrySetAction.editRating:
        return RateButton(
          blurred: blurred,
          onChooserValue: (rating) => _actionDelegate.quickRate(context, rating),
          focusNode: focusNode,
          onPressed: onPressed,
        );

      case EntrySetAction.editTags:
        return TagButton(
          blurred: blurred,
          onChooserValue: (filter) => _actionDelegate.quickTag(context, filter),
          focusNode: focusNode,
          onPressed: onPressed,
        );

      case EntrySetAction.toggleFavourite:
        return FavouriteToggler(
          entries: _getExpandedSelectedItems(selection),
          focusNode: focusNode,
          onPressed: onPressed,
        );

      default:
        return IconButton(
          key: _getActionKey(action),
          icon: action.getIcon(),
          onPressed: onPressed,
          focusNode: focusNode,
          tooltip: action.getText(context),
        );
    }
  }

  Widget _buildButtonCaption(
    BuildContext context,
    EntrySetAction action, {
    required bool enabled,
  }) {
    switch (action) {
      case EntrySetAction.toggleTitleSearch:
        return TitleSearchTogglerCaption(enabled: enabled);
      default:
        return CaptionedButtonText(
          text: action.getText(context),
          enabled: enabled,
        );
    }
  }

  PopupMenuItem<EntrySetAction> _toMenuItem(
    EntrySetAction action, {
    required bool enabled,
    required Selection<AvesEntry> selection,
  }) {
    late Widget child;

    switch (action) {
      case EntrySetAction.toggleTitleSearch:
        child = TitleSearchToggler(
          queryEnabled: context.read<Query>().enabled,
          isMenuItem: true,
        );
        break;

      case EntrySetAction.toggleFavourite:
        child = FavouriteToggler(
          entries: _getExpandedSelectedItems(selection),
          isMenuItem: true,
        );
        break;

      default:
        child = MenuRow(
          text: action.getText(context),
          icon: action.getIcon(),
        );
    }

    return PopupMenuItem(
      key: _getActionKey(action),
      value: action,
      enabled: enabled,
      child: child,
    );
  }

  PopupMenuEntry<EntrySetAction> _buildRotateAndFlipMenuItems(
    BuildContext context, {
    required bool Function(EntrySetAction action) canApply,
  }) {
    Widget buildDivider() => const SizedBox(
          height: 16,
          child: VerticalDivider(width: 1, thickness: 1),
        );

    Widget buildItem(EntrySetAction action) => Expanded(
          child: Material(
            color: Colors.transparent,
            shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(8)),
            ),
            clipBehavior: Clip.antiAlias,
            child: PopupMenuItem(
              value: action,
              enabled: canApply(action),
              child: Tooltip(
                message: action.getText(context),
                child: Center(child: action.getIcon()),
              ),
            ),
          ),
        );

    return PopupMenuItemContainer(
      child: Row(
        children: [
          buildDivider(),
          buildItem(EntrySetAction.rotateCCW),
          buildDivider(),
          buildItem(EntrySetAction.rotateCW),
          buildDivider(),
          buildItem(EntrySetAction.flip),
          buildDivider(),
        ],
      ),
    );
  }

Future<void> _onActionSelected(EntrySetAction action) async {
  switch (action) {
    case EntrySetAction.configureView:
      await _configureView();
      break;

    case EntrySetAction.select:
      context.read<Selection<AvesEntry>>().select();
      break;

    case EntrySetAction.selectAll:
      context.read<Selection<AvesEntry>>().addToSelection(collection.sortedEntries);
      break;

    case EntrySetAction.selectNone:
      context.read<Selection<AvesEntry>>().clearSelection();
      break;

    case EntrySetAction.openGpx:
      await Navigator.of(context).push(
        MaterialPageRoute(
          builder: (_) => const ActivityFilesPage(),
        ),
      );
      break;

    default:
      _actionDelegate.onActionSelected(context, action);
  }
}

  Future<void> _configureView() async {
    final initialValue = (
      settings.collectionSortFactor,
      settings.collectionSectionFactor,
      settings.getTileLayout(CollectionPage.routeName),
      settings.collectionSortReverse,
    );

    final extentController = context.read<TileExtentController>();
    final value =
        await showDialog<(EntrySortFactor?, EntrySectionFactor?, TileLayout?, bool)>(
      context: context,
      builder: (context) {
        return TileViewDialog<EntrySortFactor, EntrySectionFactor, TileLayout>(
          initialValue: initialValue,
          sortOptions: _sortOptions
              .map((v) => TileViewDialogOption(
                    value: v,
                    title: v.getName(context),
                    icon: v.icon,
                  ))
              .toList(),
          sectionOptions: _sectionOptions
              .map((v) => TileViewDialogOption(
                    value: v,
                    title: v.getName(context),
                    icon: v.icon,
                  ))
              .toList(),
          layoutOptions: _layoutOptions
              .map((v) => TileViewDialogOption(
                    value: v,
                    title: v.getName(context),
                    icon: v.icon,
                  ))
              .toList(),
          sortOrder: (factor, reverse) => factor.getOrderName(context, reverse),
          canSection: (s, g, l) => s == EntrySortFactor.date,
          tileExtentController: extentController,
        );
      },
      routeSettings: const RouteSettings(name: TileViewDialog.routeName),
    );

    await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);

    if (value != null && initialValue != value) {
      settings.collectionSortFactor = value.$1!;
      settings.collectionSectionFactor = value.$2!;
      settings.setTileLayout(CollectionPage.routeName, value.$3!);
      settings.collectionSortReverse = value.$4;
    }
  }
}
