import 'package:flutter/material.dart';
import 'package:plezy/widgets/app_icon.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../focus/focusable_wrapper.dart';
import '../i18n/strings.g.dart';
import '../media/media_item.dart';
import '../media/media_item_types.dart';
import '../models/download_models.dart';
import '../utils/dialogs.dart';
import '../utils/global_key_utils.dart';
import 'download_status_icon.dart';

/// Represents a node in the download tree
class DownloadTreeNode {
  final String key;
  final String title;
  final DownloadNodeType type;
  final double progress; // 0.0-1.0
  final DownloadStatus status;
  final List<DownloadTreeNode> children;
  final MediaItem? metadata;
  final DownloadProgress? downloadProgress;

  const DownloadTreeNode({
    required this.key,
    required this.title,
    required this.type,
    this.progress = 0.0,
    required this.status,
    this.children = const [],
    this.metadata,
    this.downloadProgress,
  });

  /// Check if this node has children
  bool get hasChildren => children.isNotEmpty;

  /// Get the number of completed children
  int get completedChildrenCount {
    return children.where((child) => child.status == DownloadStatus.completed).length;
  }
}

/// Type of node in the download tree
enum DownloadNodeType { show, season, episode, movie }

/// Hierarchical tree view for downloads
/// Groups TV shows by show -> season -> episode
/// Movies appear at top level
class DownloadTreeView extends StatefulWidget {
  final Map<String, DownloadProgress> downloads;
  final Map<String, MediaItem> metadata;
  final void Function(String globalKey)? onPause;
  final void Function(String globalKey)? onResume;
  final void Function(String globalKey)? onRetry;
  final void Function(String globalKey)? onCancel;
  final void Function(String globalKey)? onDelete;
  final VoidCallback? onNavigateLeft;
  final VoidCallback? onBack;
  final bool suppressAutoFocus;

  const DownloadTreeView({
    super.key,
    required this.downloads,
    required this.metadata,
    this.onPause,
    this.onResume,
    this.onRetry,
    this.onCancel,
    this.onDelete,
    this.onNavigateLeft,
    this.onBack,
    this.suppressAutoFocus = false,
  });

  @override
  State<DownloadTreeView> createState() => _DownloadTreeViewState();
}

class _DownloadTreeViewState extends State<DownloadTreeView> {
  final Set<String> _expandedNodes = {};
  final FocusNode _firstItemFocusNode = FocusNode(debugLabel: 'DownloadTreeView_firstItem');

  @override
  void dispose() {
    _firstItemFocusNode.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(DownloadTreeView oldWidget) {
    super.didUpdateWidget(oldWidget);
    // When suppressAutoFocus changes from true to false, focus the first item
    if (oldWidget.suppressAutoFocus && !widget.suppressAutoFocus) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted && _firstItemFocusNode.canRequestFocus) {
          _firstItemFocusNode.requestFocus();
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final tree = _buildTree();
    final flattenedNodes = _flattenTree(tree);

    if (flattenedNodes.isEmpty) {
      return Center(child: Text(t.downloads.noDownloadsTree));
    }

    return ListView.builder(
      padding: EdgeInsets.zero,
      itemCount: flattenedNodes.length,
      itemBuilder: (context, index) {
        final item = flattenedNodes[index];
        return _buildTreeItem(item.node, item.depth, isFirst: index == 0);
      },
    );
  }

  /// Build the download tree from flat download list
  List<DownloadTreeNode> _buildTree() {
    final Map<String, List<MapEntry<String, DownloadProgress>>> showGroups = {};
    final List<DownloadTreeNode> movies = [];

    // Group downloads
    for (final entry in widget.downloads.entries) {
      final globalKey = entry.key;
      final download = entry.value;
      final meta = widget.metadata[globalKey];

      if (meta == null) continue;

      if (meta.isEpisode) {
        // Group episodes by show
        final showKey = meta.grandparentId ?? 'unknown';
        showGroups.putIfAbsent(showKey, () => []);
        showGroups[showKey]!.add(entry);
      } else if (meta.isMovie) {
        // Movies go at top level
        movies.add(
          DownloadTreeNode(
            key: globalKey,
            title: meta.displayTitle,
            type: DownloadNodeType.movie,
            progress: download.progressPercent,
            status: download.status,
            metadata: meta,
            downloadProgress: download,
          ),
        );
      }
    }

    // Build show nodes
    final List<DownloadTreeNode> shows = [];
    for (final showEntry in showGroups.entries) {
      final showKey = showEntry.key;
      final episodes = showEntry.value;

      if (episodes.isEmpty) continue;

      // Get show metadata from first episode
      final firstEpisode = widget.metadata[episodes.first.key];
      final showTitle = firstEpisode?.grandparentTitle ?? 'Unknown Show';

      // Group episodes by season
      final Map<String, List<MapEntry<String, DownloadProgress>>> seasonGroups = {};
      for (final episode in episodes) {
        final meta = widget.metadata[episode.key];
        if (meta == null) continue;

        final seasonKey = meta.parentId ?? 'unknown';
        seasonGroups.putIfAbsent(seasonKey, () => []);
        seasonGroups[seasonKey]!.add(episode);
      }

      // Build season nodes
      final List<DownloadTreeNode> seasons = [];
      for (final seasonEntry in seasonGroups.entries) {
        final seasonKey = seasonEntry.key;
        final seasonEpisodes = seasonEntry.value;

        if (seasonEpisodes.isEmpty) continue;

        // Get season metadata from first episode
        final firstEpisode = widget.metadata[seasonEpisodes.first.key];
        final seasonTitle = firstEpisode?.parentTitle ?? 'Unknown Season';
        final seasonNumber = firstEpisode?.parentIndex;

        // Build episode nodes
        final List<DownloadTreeNode> episodeNodes = [];
        for (final episodeEntry in seasonEpisodes) {
          final globalKey = episodeEntry.key;
          final download = episodeEntry.value;
          final meta = widget.metadata[globalKey];

          if (meta == null) continue;

          final episodeNumber = meta.index;
          final episodeTitle = episodeNumber != null ? 'Episode $episodeNumber - ${meta.title!}' : meta.title!;

          episodeNodes.add(
            DownloadTreeNode(
              key: globalKey,
              title: episodeTitle,
              type: DownloadNodeType.episode,
              progress: download.progressPercent,
              status: download.status,
              metadata: meta,
              downloadProgress: download,
            ),
          );
        }

        // Sort episodes by episode number only (not by status)
        episodeNodes.sort((a, b) {
          final aIndex = a.metadata?.index ?? 0;
          final bIndex = b.metadata?.index ?? 0;
          return aIndex.compareTo(bIndex);
        });

        // Calculate aggregate season progress
        final seasonProgress = episodeNodes.isEmpty
            ? 0.0
            : episodeNodes.map((e) => e.progress).reduce((a, b) => a + b) / episodeNodes.length;
        final seasonStatus = _determineAggregateStatus(episodeNodes.map((e) => e.status).toList());

        final displayTitle = seasonNumber != null ? 'Season $seasonNumber' : seasonTitle;

        seasons.add(
          DownloadTreeNode(
            key: '$showKey:$seasonKey',
            title: displayTitle,
            type: DownloadNodeType.season,
            progress: seasonProgress,
            status: seasonStatus,
            children: episodeNodes,
          ),
        );
      }

      seasons.removeWhere((s) => s.children.isEmpty);

      // Sort seasons by season number
      seasons.sort((a, b) {
        final aSeasonNum = widget.metadata[a.children.first.key]?.parentIndex ?? 0;
        final bSeasonNum = widget.metadata[b.children.first.key]?.parentIndex ?? 0;
        return aSeasonNum.compareTo(bSeasonNum);
      });

      // Calculate aggregate show progress
      final showProgress = seasons.isEmpty
          ? 0.0
          : seasons.map((s) => s.progress).reduce((a, b) => a + b) / seasons.length;
      final showStatus = _determineAggregateStatus(seasons.map((s) => s.status).toList());

      shows.add(
        DownloadTreeNode(
          key: showKey,
          title: showTitle,
          type: DownloadNodeType.show,
          progress: showProgress,
          status: showStatus,
          children: seasons,
        ),
      );
    }

    // Sort shows and movies by status and title
    _sortNodesByStatusAndTitle(shows);
    _sortNodesByStatusAndTitle(movies);

    // Combine movies and shows
    return [...movies, ...shows];
  }

  /// Determine aggregate status from child statuses
  /// Priority: downloading > queued > paused > completed > failed
  DownloadStatus _determineAggregateStatus(List<DownloadStatus> statuses) {
    if (statuses.isEmpty) return DownloadStatus.queued;

    if (statuses.any((s) => s == DownloadStatus.downloading)) {
      return DownloadStatus.downloading;
    }
    if (statuses.any((s) => s == DownloadStatus.queued)) {
      return DownloadStatus.queued;
    }
    if (statuses.any((s) => s == DownloadStatus.paused)) {
      return DownloadStatus.paused;
    }
    if (statuses.any((s) => s == DownloadStatus.failed)) {
      return DownloadStatus.failed;
    }
    return DownloadStatus.completed;
  }

  /// Compare statuses for sorting (downloading first, then queued, etc.)
  int _compareByStatus(DownloadStatus a, DownloadStatus b) {
    const statusOrder = {
      DownloadStatus.downloading: 0,
      DownloadStatus.queued: 1,
      DownloadStatus.paused: 2,
      DownloadStatus.completed: 3,
      DownloadStatus.failed: 4,
      DownloadStatus.cancelled: 5,
    };
    return (statusOrder[a] ?? 99).compareTo(statusOrder[b] ?? 99);
  }

  /// Sort nodes by status (downloading first) then by title
  void _sortNodesByStatusAndTitle(List<DownloadTreeNode> nodes) {
    nodes.sort((a, b) {
      final statusCompare = _compareByStatus(a.status, b.status);
      if (statusCompare != 0) return statusCompare;
      return a.title.compareTo(b.title);
    });
  }

  /// Flatten the tree into a list of visible nodes with their depths
  List<_FlatNode> _flattenTree(List<DownloadTreeNode> nodes, [int depth = 0]) {
    final List<_FlatNode> result = [];

    for (final node in nodes) {
      result.add(_FlatNode(node: node, depth: depth));

      // Add children if node is expanded
      if (_expandedNodes.contains(node.key) && node.hasChildren) {
        result.addAll(_flattenTree(node.children, depth + 1));
      }
    }

    return result;
  }

  /// Toggle node expansion
  void _toggleExpansion(String key) {
    setState(() {
      if (_expandedNodes.contains(key)) {
        _expandedNodes.remove(key);
      } else {
        _expandedNodes.add(key);
      }
    });
  }

  /// Build a tree item widget
  Widget _buildTreeItem(DownloadTreeNode node, int depth, {bool isFirst = false}) {
    return _DownloadTreeItem(
      node: node,
      depth: depth,
      isExpanded: _expandedNodes.contains(node.key),
      onToggleExpansion: () => _toggleExpansion(node.key),
      onPause: widget.onPause,
      onResume: widget.onResume,
      onRetry: widget.onRetry,
      onCancel: widget.onCancel,
      onDelete: widget.onDelete,
      onNavigateLeft: widget.onNavigateLeft,
      onBack: widget.onBack,
      rowFocusNode: isFirst ? _firstItemFocusNode : null,
      autofocus: isFirst && !widget.suppressAutoFocus,
      pauseAllChildren: _pauseAllChildren,
      resumeAllChildren: _resumeAllChildren,
      deleteAllChildren: _deleteAllChildren,
    );
  }

  /// Pause all active (downloading and queued) children of a container node
  void _pauseAllChildren(DownloadTreeNode node) {
    final keys = _getActiveChildKeys(node);
    for (final key in keys) {
      widget.onPause?.call(key);
    }
  }

  /// Resume all paused children of a container node
  void _resumeAllChildren(DownloadTreeNode node) {
    final keys = _getPausedChildKeys(node);
    for (final key in keys) {
      widget.onResume?.call(key);
    }
  }

  /// Get all active (downloading or queued) child keys from a container node
  List<String> _getActiveChildKeys(DownloadTreeNode node) {
    final List<String> keys = [];
    for (final child in node.children) {
      if (child.hasChildren) {
        keys.addAll(_getActiveChildKeys(child));
      } else if (child.status == DownloadStatus.downloading || child.status == DownloadStatus.queued) {
        keys.add(child.key);
      }
    }
    return keys;
  }

  /// Get all paused child keys from a container node
  List<String> _getPausedChildKeys(DownloadTreeNode node) {
    final List<String> keys = [];
    for (final child in node.children) {
      if (child.hasChildren) {
        keys.addAll(_getPausedChildKeys(child));
      } else if (child.status == DownloadStatus.paused) {
        keys.add(child.key);
      }
    }
    return keys;
  }

  /// Delete all children of a container node via the container's globalKey
  /// so deleteDownload's transitive show/season path cleans up all maps.
  void _deleteAllChildren(DownloadTreeNode node) {
    final containerKey = resolveDownloadContainerGlobalKey(node, widget.metadata);
    if (containerKey != null) {
      widget.onDelete?.call(containerKey);
      return;
    }

    // Container globalKey unresolvable; fall back to per-leaf delete.
    for (final key in _getAllChildKeys(node)) {
      widget.onDelete?.call(key);
    }
  }

  /// Get all leaf node keys from a container node
  List<String> _getAllChildKeys(DownloadTreeNode node) {
    final List<String> keys = [];

    for (final child in node.children) {
      if (child.hasChildren) {
        keys.addAll(_getAllChildKeys(child));
      } else {
        keys.add(child.key);
      }
    }

    return keys;
  }
}

/// Tree-node keys for shows/seasons aren't provider globalKeys; reconstruct
/// from any leaf episode's serverId + grandparentId/parentId.
@visibleForTesting
String? resolveDownloadContainerGlobalKey(DownloadTreeNode node, Map<String, MediaItem> metadata) {
  final firstLeafKey = _firstLeafKey(node);
  if (firstLeafKey == null) return null;
  final firstLeafMeta = metadata[firstLeafKey];
  final serverId = firstLeafMeta?.serverId;
  if (serverId == null) return null;
  switch (node.type) {
    case DownloadNodeType.show:
      final showRatingKey = firstLeafMeta!.grandparentId;
      if (showRatingKey == null) return null;
      return buildGlobalKey(serverId, showRatingKey);
    case DownloadNodeType.season:
      final seasonRatingKey = firstLeafMeta!.parentId;
      if (seasonRatingKey == null) return null;
      return buildGlobalKey(serverId, seasonRatingKey);
    case DownloadNodeType.episode:
    case DownloadNodeType.movie:
      return null;
  }
}

String? _firstLeafKey(DownloadTreeNode node) {
  for (final child in node.children) {
    if (child.hasChildren) {
      final result = _firstLeafKey(child);
      if (result != null) return result;
    } else {
      return child.key;
    }
  }
  return null;
}

/// Helper class to store a node with its depth in the flattened tree
class _FlatNode {
  final DownloadTreeNode node;
  final int depth;

  const _FlatNode({required this.node, required this.depth});
}

/// A single tree item with focusable row content and action buttons
class _DownloadTreeItem extends StatefulWidget {
  final DownloadTreeNode node;
  final int depth;
  final bool isExpanded;
  final VoidCallback onToggleExpansion;
  final void Function(String globalKey)? onPause;
  final void Function(String globalKey)? onResume;
  final void Function(String globalKey)? onRetry;
  final void Function(String globalKey)? onCancel;
  final void Function(String globalKey)? onDelete;
  final VoidCallback? onNavigateLeft;
  final VoidCallback? onBack;
  final FocusNode? rowFocusNode;
  final bool autofocus;
  final void Function(DownloadTreeNode) pauseAllChildren;
  final void Function(DownloadTreeNode) resumeAllChildren;
  final void Function(DownloadTreeNode) deleteAllChildren;

  const _DownloadTreeItem({
    required this.node,
    required this.depth,
    required this.isExpanded,
    required this.onToggleExpansion,
    this.onPause,
    this.onResume,
    this.onRetry,
    this.onCancel,
    this.onDelete,
    this.onNavigateLeft,
    this.onBack,
    this.rowFocusNode,
    this.autofocus = false,
    required this.pauseAllChildren,
    required this.resumeAllChildren,
    required this.deleteAllChildren,
  });

  @override
  State<_DownloadTreeItem> createState() => _DownloadTreeItemState();
}

class _DownloadTreeItemState extends State<_DownloadTreeItem> {
  /// Treat downloading items with no progress/speed as effectively queued
  /// (they're waiting in background_downloader's HoldingQueue).
  DownloadStatus get _effectiveStatus {
    if (widget.node.status == DownloadStatus.downloading &&
        widget.node.progress == 0 &&
        (widget.node.downloadProgress?.speed ?? 0) == 0) {
      return DownloadStatus.queued;
    }
    return widget.node.status;
  }

  // Focus node for row content (only created if not provided externally)
  FocusNode? _ownedRowFocusNode;
  // Focus nodes for action buttons (up to 3 buttons max)
  final List<FocusNode> _buttonFocusNodes = [];

  FocusNode get _rowFocusNode => widget.rowFocusNode ?? _ownedRowFocusNode!;

  @override
  void initState() {
    super.initState();
    _initRowFocusNode();
    _initButtonFocusNodes();
  }

  @override
  void didUpdateWidget(_DownloadTreeItem oldWidget) {
    super.didUpdateWidget(oldWidget);
    // Reinitialize focus nodes if action count changed
    if (_getActionCount() != _buttonFocusNodes.length) {
      _disposeButtonFocusNodes();
      _initButtonFocusNodes();
    }
  }

  void _initRowFocusNode() {
    if (widget.rowFocusNode == null) {
      _ownedRowFocusNode = FocusNode(debugLabel: 'download_row_${widget.node.key}');
    }
  }

  void _initButtonFocusNodes() {
    final actionCount = _getActionCount();
    for (int i = 0; i < actionCount; i++) {
      _buttonFocusNodes.add(FocusNode(debugLabel: 'download_action_$i'));
    }
  }

  void _disposeButtonFocusNodes() {
    for (final node in _buttonFocusNodes) {
      node.dispose();
    }
    _buttonFocusNodes.clear();
  }

  @override
  void dispose() {
    _ownedRowFocusNode?.dispose();
    _disposeButtonFocusNodes();
    super.dispose();
  }

  int _getActionCount() {
    final isContainer = widget.node.type == DownloadNodeType.show || widget.node.type == DownloadNodeType.season;
    if (isContainer) {
      return _getContainerActionCount();
    }
    return _getItemActionCount();
  }

  int _getItemActionCount() {
    int count = 0;
    final status = widget.node.status;
    if (status == DownloadStatus.downloading && widget.onPause != null) count++;
    if (status == DownloadStatus.paused && widget.onResume != null) count++;
    if ((status == DownloadStatus.downloading || status == DownloadStatus.queued) && widget.onCancel != null) count++;
    if (status == DownloadStatus.failed && widget.onRetry != null) count++;
    if ((status == DownloadStatus.completed || status == DownloadStatus.failed || status == DownloadStatus.cancelled) &&
        widget.onDelete != null) {
      count++;
    }
    return count;
  }

  int _getContainerActionCount() {
    int count = 0;
    final status = widget.node.status;
    if ((status == DownloadStatus.downloading || status == DownloadStatus.queued) && widget.onPause != null) count++;
    if (status == DownloadStatus.paused && widget.onResume != null) count++;
    if (widget.onDelete != null) count++;
    return count;
  }

  void _focusFirstButton() {
    if (_buttonFocusNodes.isNotEmpty) {
      _buttonFocusNodes.first.requestFocus();
    }
  }

  void _focusRow() {
    _rowFocusNode.requestFocus();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final canExpand = widget.node.hasChildren;
    final hasActions = _buttonFocusNodes.isNotEmpty;

    return Padding(
      padding: EdgeInsets.only(left: widget.depth * 16.0),
      child: FocusableWrapper(
        focusNode: _rowFocusNode,
        autofocus: widget.autofocus,
        onSelect: canExpand ? widget.onToggleExpansion : null,
        onNavigateLeft: widget.onNavigateLeft,
        onNavigateRight: hasActions ? _focusFirstButton : null,
        onBack: widget.onBack,
        borderRadius: 8.0,
        disableScale: true,
        useBackgroundFocus: true,
        child: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTap: canExpand ? widget.onToggleExpansion : null,
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
            child: Row(
              children: [
                // Row content
                Expanded(child: _buildRowContent(theme, canExpand)),

                // Action buttons
                if (hasActions) _buildActions(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildRowContent(ThemeData theme, bool canExpand) {
    return Row(
      children: [
        // Expand/collapse icon
        if (canExpand)
          AppIcon(widget.isExpanded ? Symbols.expand_more_rounded : Symbols.chevron_right_rounded, fill: 1, size: 20)
        else
          const SizedBox(width: 20),

        const SizedBox(width: 8),

        // Status icon
        _buildStatusIcon(_effectiveStatus),

        const SizedBox(width: 12),

        // Title and info
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                widget.node.title,
                style: theme.textTheme.bodyMedium?.copyWith(
                  fontWeight: canExpand ? FontWeight.w600 : FontWeight.normal,
                ),
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),

              if (canExpand) ...[
                const SizedBox(height: 4),
                Text(
                  _getNodeSummary(),
                  style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurface.withValues(alpha: 0.6)),
                ),
              ],

              // Progress bar for active downloads
              if (_effectiveStatus == DownloadStatus.downloading) ...[
                const SizedBox(height: 8),
                LinearProgressIndicator(
                  value: widget.node.progress,
                  backgroundColor: theme.colorScheme.surfaceContainerHighest,
                ),
                if (widget.node.downloadProgress != null) ...[
                  const SizedBox(height: 4),
                  Text(
                    '${(widget.node.progress * 100).toStringAsFixed(1)}% - ${widget.node.downloadProgress!.speedFormatted}',
                    style: theme.textTheme.bodySmall?.copyWith(
                      color: theme.colorScheme.onSurface.withValues(alpha: 0.6),
                    ),
                  ),
                ],
              ],

              // Queued label
              if (_effectiveStatus == DownloadStatus.queued) ...[
                const SizedBox(height: 4),
                Text(
                  t.downloads.downloadQueued,
                  style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurface.withValues(alpha: 0.5)),
                ),
              ],

              // Error message for failed downloads
              if (_effectiveStatus == DownloadStatus.failed && widget.node.downloadProgress?.errorMessage != null) ...[
                const SizedBox(height: 4),
                Text(
                  widget.node.downloadProgress!.errorMessage!,
                  style: theme.textTheme.bodySmall?.copyWith(color: Colors.red.withValues(alpha: 0.8)),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
              ],
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildStatusIcon(DownloadStatus status) {
    return DownloadStatusIcon(status: status, size: 20);
  }

  String _getNodeSummary() {
    final total = widget.node.children.length;
    final completed = widget.node.completedChildrenCount;
    return '$completed/$total completed';
  }

  Widget _buildActions() {
    final isContainer = widget.node.type == DownloadNodeType.show || widget.node.type == DownloadNodeType.season;

    final actions = isContainer ? _buildContainerActions() : _buildItemActions();

    return Row(mainAxisSize: MainAxisSize.min, children: actions);
  }

  List<Widget> _buildItemActions() {
    final globalKey = widget.node.key;
    final status = widget.node.status;
    final actions = <Widget>[];
    int buttonIndex = 0;

    // Pause button for downloading items
    if (status == DownloadStatus.downloading && widget.onPause != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.pause_rounded,
          tooltip: t.common.pause,
          onPressed: () => widget.onPause!(globalKey),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Resume button for paused items
    if (status == DownloadStatus.paused && widget.onResume != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.play_arrow_rounded,
          tooltip: t.common.resume,
          onPressed: () => widget.onResume!(globalKey),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Cancel button for downloading/queued items
    if ((status == DownloadStatus.downloading || status == DownloadStatus.queued) && widget.onCancel != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.close_rounded,
          tooltip: t.common.cancel,
          onPressed: () => widget.onCancel!(globalKey),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Retry button for failed items
    if (status == DownloadStatus.failed && widget.onRetry != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.refresh_rounded,
          tooltip: t.downloads.retryDownload,
          onPressed: () => widget.onRetry!(globalKey),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Delete button for completed/failed/cancelled items
    if ((status == DownloadStatus.completed || status == DownloadStatus.failed || status == DownloadStatus.cancelled) &&
        widget.onDelete != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.delete_rounded,
          tooltip: t.common.delete,
          onPressed: () async {
            final confirmed = await showDeleteConfirmation(
              context,
              title: t.downloads.deleteDownload,
              message: t.downloads.deleteConfirm(title: widget.node.title),
            );
            if (confirmed) widget.onDelete!(globalKey);
          },
          buttonIndex: buttonIndex++,
        ),
      );
    }

    return actions;
  }

  List<Widget> _buildContainerActions() {
    final status = widget.node.status;
    final actions = <Widget>[];
    int buttonIndex = 0;

    // Pause all button
    if ((status == DownloadStatus.downloading || status == DownloadStatus.queued) && widget.onPause != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.pause_rounded,
          tooltip: t.downloads.pauseAll,
          onPressed: () => widget.pauseAllChildren(widget.node),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Resume all button
    if (status == DownloadStatus.paused && widget.onResume != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.play_arrow_rounded,
          tooltip: t.downloads.resumeAll,
          onPressed: () => widget.resumeAllChildren(widget.node),
          buttonIndex: buttonIndex++,
        ),
      );
    }

    // Delete all button
    if (widget.onDelete != null) {
      actions.add(
        _buildActionButton(
          icon: Symbols.delete_sweep_rounded,
          tooltip: t.downloads.deleteAll,
          onPressed: () async {
            final confirmed = await showDeleteConfirmation(
              context,
              title: t.downloads.deleteDownload,
              message: t.downloads.deleteConfirm(title: widget.node.title),
            );
            if (confirmed) widget.deleteAllChildren(widget.node);
          },
          buttonIndex: buttonIndex++,
        ),
      );
    }

    return actions;
  }

  Widget _buildActionButton({
    required IconData icon,
    required String tooltip,
    required VoidCallback onPressed,
    required int buttonIndex,
  }) {
    // Guard against race condition where action count changed between didUpdateWidget and build
    if (buttonIndex >= _buttonFocusNodes.length) {
      return Tooltip(
        message: tooltip,
        child: GestureDetector(
          onTap: onPressed,
          child: Padding(padding: const EdgeInsets.all(8.0), child: AppIcon(icon, fill: 1, size: 20)),
        ),
      );
    }

    final isFirst = buttonIndex == 0;
    final isLast = buttonIndex == _buttonFocusNodes.length - 1;

    return FocusableWrapper(
      focusNode: _buttonFocusNodes[buttonIndex],
      onSelect: onPressed,
      onNavigateLeft: isFirst ? _focusRow : () => _buttonFocusNodes[buttonIndex - 1].requestFocus(),
      onNavigateRight: isLast ? null : () => _buttonFocusNodes[buttonIndex + 1].requestFocus(),
      onBack: widget.onBack,
      borderRadius: 20.0,
      disableScale: true,
      useBackgroundFocus: true,
      autoScroll: false,
      child: Tooltip(
        message: tooltip,
        child: GestureDetector(
          onTap: onPressed,
          child: Padding(padding: const EdgeInsets.all(8.0), child: AppIcon(icon, fill: 1, size: 20)),
        ),
      ),
    );
  }
}
