import 'dart:async' show Stream, unawaited;
import '../../../media/ids.dart';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';

import '../../../focus/dpad_navigator.dart';
import '../../../focus/focusable_wrapper.dart';
import '../../../i18n/strings.g.dart';
import '../../../media/media_item.dart';
import '../../../media/media_item_labels.dart';
import '../../../media/media_item_types.dart';
import '../../../media/media_server_client.dart';
import '../../../mpv/mpv.dart';
import '../../../media/media_source_info.dart';
import '../../../providers/playback_state_provider.dart';
import '../../../services/settings_service.dart';
import '../../../services/download_storage_service.dart';
import '../../../utils/formatters.dart';
import '../../../utils/player_utils.dart';
import '../../../utils/provider_extensions.dart';
import '../../../utils/scroll_utils.dart';
import '../../app_icon.dart';
import '../../clickable_cursor.dart';
import '../../optimized_media_image.dart';
import '../../settings_builder.dart';
import 'media_selector_thumbnail.dart';

/// Horizontal scrollable strip of chapter/queue items shown on swipe-up.
class ContentStrip extends StatefulWidget {
  final Player player;
  final List<MediaChapter> chapters;
  final bool chaptersLoaded;
  final bool canControl;
  final String? serverId;
  final bool showQueueTab;
  final Function(MediaItem)? onQueueItemSelected;
  final Future<void> Function(Duration position)? onSeekRequested;
  final Function(Duration position)? onSeekCompleted;

  /// Whether to use dpad/focus-based navigation (TV mode).
  /// When true, no tab bar is shown — pages are navigated via UP/DOWN.
  final bool useFocusNavigation;

  /// Called when navigating UP from the top-most strip page (back to buttons).
  final VoidCallback? onNavigateUp;

  /// Called on any focus activity (to reset auto-hide timer).
  final VoidCallback? onFocusActivity;

  const ContentStrip({
    super.key,
    required this.player,
    required this.chapters,
    required this.chaptersLoaded,
    required this.canControl,
    this.serverId,
    this.showQueueTab = false,
    this.onQueueItemSelected,
    this.onSeekRequested,
    this.onSeekCompleted,
    this.useFocusNavigation = false,
    this.onNavigateUp,
    this.onFocusActivity,
  });

  @override
  State<ContentStrip> createState() => ContentStripState();
}

enum _StripTab { chapters, queue }

class ContentStripState extends State<ContentStrip> {
  late _StripTab _activeTab;
  final ScrollController _chapterScrollController = ScrollController();
  final ScrollController _queueScrollController = ScrollController();
  final Map<_StripTab, Object?> _lastAutoScrolled = {};
  final Map<int, GlobalKey> _chapterItemKeys = {};
  final Map<int, GlobalKey> _queueItemKeys = {};
  late Stream<int?> _chapterIndexStream;

  // Focus nodes for focus navigation mode
  final List<FocusNode> _chapterFocusNodes = [];
  final List<FocusNode> _queueFocusNodes = [];

  bool get _hasChapters => widget.chapters.isNotEmpty;
  bool get _hasQueue => widget.showQueueTab && widget.onQueueItemSelected != null;
  bool get _hasBothTabs => _hasChapters && _hasQueue;

  @override
  void initState() {
    super.initState();
    _activeTab = _hasChapters ? _StripTab.chapters : _StripTab.queue;
    _bindChapterIndexStream();
  }

  @override
  void didUpdateWidget(ContentStrip oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!identical(oldWidget.player, widget.player) || !identical(oldWidget.chapters, widget.chapters)) {
      _bindChapterIndexStream();
    }
    _normalizeActiveTab();
  }

  void _normalizeActiveTab() {
    if (_activeTab == _StripTab.chapters && !_hasChapters && _hasQueue) {
      _activeTab = _StripTab.queue;
      _lastAutoScrolled.remove(_StripTab.queue);
    } else if (_activeTab == _StripTab.queue && !_hasQueue && _hasChapters) {
      _activeTab = _StripTab.chapters;
      _lastAutoScrolled.remove(_StripTab.chapters);
    }
  }

  void _bindChapterIndexStream() {
    if (!_hasChapters) {
      _chapterIndexStream = const Stream<int?>.empty();
      return;
    }

    _chapterIndexStream = widget.player.streams.position
        .map((position) => MediaChapter.indexAtPosition(position, widget.chapters))
        .distinct();
  }

  @override
  void dispose() {
    _chapterScrollController.dispose();
    _queueScrollController.dispose();
    for (final node in _chapterFocusNodes) {
      node.dispose();
    }
    for (final node in _queueFocusNodes) {
      node.dispose();
    }
    super.dispose();
  }

  /// Request focus on the current chapter or queue item (called by parent when strip appears).
  void requestInitialFocus() {
    if (_activeTab == _StripTab.chapters && _chapterFocusNodes.isNotEmpty) {
      final currentIndex = MediaChapter.indexAtPosition(widget.player.state.position, widget.chapters);
      final idx = (currentIndex ?? 0).clamp(0, _chapterFocusNodes.length - 1);
      _chapterFocusNodes[idx].requestFocus();
      _scrollToFocusedNode(_chapterFocusNodes[idx]);
    } else if (_activeTab == _StripTab.queue && _queueFocusNodes.isNotEmpty) {
      final currentIndex = _getCurrentQueueIndex();
      final idx = (currentIndex ?? 0).clamp(0, _queueFocusNodes.length - 1);
      _queueFocusNodes[idx].requestFocus();
      _scrollToFocusedNode(_queueFocusNodes[idx]);
    }
  }

  Future<void> _handleChapterTap(Duration position) async {
    if (!widget.canControl) return;
    final clamped = clampSeekPosition(widget.player, position);
    await (widget.onSeekRequested ?? widget.player.seek)(clamped);
    if (mounted) {
      widget.onSeekCompleted?.call(clamped);
    }
  }

  int? _getCurrentQueueIndex() {
    try {
      final playbackState = context.read<PlaybackStateProvider>();
      final items = playbackState.loadedItems;
      final currentItemID = playbackState.currentPlayQueueItemID;
      final idx = items.indexWhere((item) => playbackState.playQueueItemIdFor(item) == currentItemID);
      return idx >= 0 ? idx : null;
    } catch (_) {
      return null;
    }
  }

  void _ensureFocusNodes(List<FocusNode> nodes, int count, String prefix) {
    while (nodes.length < count) {
      nodes.add(FocusNode(debugLabel: '$prefix${nodes.length}'));
    }
    while (nodes.length > count) {
      nodes.removeLast().dispose();
    }
  }

  void _scrollToFocusedNode(FocusNode node) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      final context = node.context;
      if (context == null) return;
      Scrollable.ensureVisible(
        context,
        alignment: 0.5,
        duration: const Duration(milliseconds: 150),
        curve: Curves.easeOut,
      );
    });
  }

  void _selectTab(_StripTab tab) {
    setState(() {
      _activeTab = tab;
      _lastAutoScrolled.remove(tab);
    });
  }

  KeyEventResult _handleFocusItemKeyEvent(KeyEvent event, int index, int totalItems, _StripTab page) {
    if (!event.isActionable) return KeyEventResult.ignored;

    final key = event.logicalKey;

    if (key == LogicalKeyboardKey.arrowLeft || key == LogicalKeyboardKey.arrowRight) {
      final nodes = page == _StripTab.chapters ? _chapterFocusNodes : _queueFocusNodes;
      final target = index + (key == LogicalKeyboardKey.arrowLeft ? -1 : 1);
      if (target >= 0 && target < totalItems) {
        nodes[target].requestFocus();
        _scrollToFocusedNode(nodes[target]);
        widget.onFocusActivity?.call();
      }
      return KeyEventResult.handled;
    }

    if (key == LogicalKeyboardKey.arrowUp) {
      if (page == _StripTab.queue && _hasChapters) {
        // Switch to chapters page and focus current chapter
        setState(() {
          _activeTab = _StripTab.chapters;
          _lastAutoScrolled.remove(_StripTab.chapters);
        });
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted && _chapterFocusNodes.isNotEmpty) {
            final currentIndex = MediaChapter.indexAtPosition(widget.player.state.position, widget.chapters);
            final idx = (currentIndex ?? 0).clamp(0, _chapterFocusNodes.length - 1);
            _chapterFocusNodes[idx].requestFocus();
            _scrollToFocusedNode(_chapterFocusNodes[idx]);
          }
        });
        widget.onFocusActivity?.call();
      } else {
        // chapters page (or queue without chapters) → go back to buttons
        widget.onNavigateUp?.call();
      }
      return KeyEventResult.handled;
    }

    if (key == LogicalKeyboardKey.arrowDown) {
      if (page == _StripTab.chapters && _hasQueue) {
        // Switch to queue page and focus current queue item
        setState(() {
          _activeTab = _StripTab.queue;
          _lastAutoScrolled.remove(_StripTab.queue);
        });
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted && _queueFocusNodes.isNotEmpty) {
            final idx = (_getCurrentQueueIndex() ?? 0).clamp(0, _queueFocusNodes.length - 1);
            _queueFocusNodes[idx].requestFocus();
            _scrollToFocusedNode(_queueFocusNodes[idx]);
          }
        });
        widget.onFocusActivity?.call();
      }
      // On queue page or chapters-only, consume to prevent bubbling
      return KeyEventResult.handled;
    }

    return KeyEventResult.ignored;
  }

  MediaServerClient? _tryGetClient(BuildContext context, ServerId? serverId) {
    return context.tryGetMediaClientForServer(serverId);
  }

  double _itemWidth(bool isTablet) => isTablet ? 212.0 : 132.0; // thumb + 12 padding

  GlobalKey _itemKeyFor(Map<int, GlobalKey> keys, int index) {
    return keys.putIfAbsent(index, GlobalKey.new);
  }

  void _trimItemKeys(Map<int, GlobalKey> keys, int count) {
    keys.removeWhere((index, _) => index >= count);
  }

  void _autoScrollTo(
    ScrollController controller,
    Map<int, GlobalKey> keys,
    int index, {
    required bool isTablet,
    required bool Function() isCurrent,
    int attempt = 0,
  }) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted || !isCurrent()) return;

      if (controller.positions.length != 1 || !controller.position.hasContentDimensions) {
        if (attempt < 3) {
          _autoScrollTo(controller, keys, index, isTablet: isTablet, isCurrent: isCurrent, attempt: attempt + 1);
        }
        return;
      }

      scrollListToIndex(
        controller,
        index,
        itemExtent: _itemWidth(isTablet),
        leadingPadding: widget.useFocusNavigation ? 12 : 4,
        animate: false,
      );
      scrollKeyedChildToHorizontalCenter(
        controller,
        _itemKeyFor(keys, index),
        animate: false,
        maxAttempts: 4,
        isCurrent: isCurrent,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    if (!_hasChapters && !_hasQueue) return const SizedBox.shrink();

    final isTablet = MediaQuery.sizeOf(context).shortestSide >= 600;
    final stripHeight = isTablet ? 170.0 : 106.0;
    // Add extra height for focus decoration when in focus navigation mode
    final effectiveStripHeight = widget.useFocusNavigation ? stripHeight + 16.0 : stripHeight;

    return SafeArea(
      top: false,
      child: Padding(
        padding: .symmetric(horizontal: widget.useFocusNavigation ? 0 : 16),
        child: Column(
          mainAxisSize: .min,
          children: [
            // Tab bar only shown in touch mode when both tabs exist
            if (_hasBothTabs && !widget.useFocusNavigation) _buildTabBar(),
            // In focus mode, show a small label for the current page
            if (widget.useFocusNavigation)
              Padding(
                padding: const EdgeInsets.only(bottom: 4),
                child: Text(
                  _activeTab == _StripTab.chapters ? t.videoControls.chapters : t.videoControls.queue,
                  style: const TextStyle(color: Colors.white70, fontSize: 12, fontWeight: .w500),
                ),
              ),
            if (!widget.useFocusNavigation) const SizedBox(height: 8),
            SizedBox(
              height: effectiveStripHeight,
              child: _activeTab == _StripTab.chapters ? _buildChapterStrip(isTablet) : _buildQueueStrip(isTablet),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildTabBar() {
    return Row(
      mainAxisAlignment: .center,
      children: [
        _buildTabLabel(t.videoControls.chapters, _StripTab.chapters),
        const SizedBox(width: 24),
        _buildTabLabel(t.videoControls.queue, _StripTab.queue),
      ],
    );
  }

  Widget _buildTabLabel(String label, _StripTab tab) {
    final isActive = _activeTab == tab;
    return ClickableCursor(
      child: GestureDetector(
        onTap: () => _selectTab(tab),
        child: Column(
          mainAxisSize: .min,
          children: [
            Text(
              label,
              style: TextStyle(
                color: isActive ? Colors.white : Colors.white54,
                fontSize: 13,
                fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
              ),
            ),
            const SizedBox(height: 4),
            Container(height: 2, width: 40, color: isActive ? Colors.white : Colors.transparent),
          ],
        ),
      ),
    );
  }

  /// Horizontal list of strip items: auto-scrolls to [autoScrollIndex] whenever
  /// [autoScrollToken] changes, and wraps items for focus navigation.
  Widget _buildStrip({
    required _StripTab tab,
    required ScrollController controller,
    required Map<int, GlobalKey> keys,
    required List<FocusNode> nodes,
    required String focusPrefix,
    required int itemCount,
    required bool isTablet,
    required int? autoScrollIndex,
    required Object? autoScrollToken,
    required (Widget, VoidCallback?) Function(BuildContext context, int index, Key key) itemBuilder,
  }) {
    _trimItemKeys(keys, itemCount);

    if (autoScrollIndex != null && _lastAutoScrolled[tab] != autoScrollToken) {
      _lastAutoScrolled[tab] = autoScrollToken;
      _autoScrollTo(
        controller,
        keys,
        autoScrollIndex,
        isTablet: isTablet,
        isCurrent: () => _lastAutoScrolled[tab] == autoScrollToken,
      );
    }

    if (widget.useFocusNavigation) {
      _ensureFocusNodes(nodes, itemCount, focusPrefix);
    }

    return ListView.builder(
      controller: controller,
      scrollDirection: Axis.horizontal,
      clipBehavior: widget.useFocusNavigation ? Clip.none : Clip.hardEdge,
      itemCount: itemCount,
      padding: .symmetric(horizontal: widget.useFocusNavigation ? 12 : 4),
      itemBuilder: (context, index) {
        final (item, onTap) = itemBuilder(context, index, _itemKeyFor(keys, index));

        if (!widget.useFocusNavigation) return item;

        return Align(
          alignment: .topCenter,
          child: FocusableWrapper(
            focusNode: nodes[index],
            onSelect: onTap,
            onKeyEvent: (_, event) => _handleFocusItemKeyEvent(event, index, itemCount, tab),
            onFocusChange: (hasFocus) {
              if (hasFocus) widget.onFocusActivity?.call();
            },
            borderRadius: 6,
            autoScroll: false,
            useBackgroundFocus: true,
            child: item,
          ),
        );
      },
    );
  }

  Widget _buildChapterStrip(bool isTablet) {
    return StreamBuilder<int?>(
      stream: _chapterIndexStream,
      initialData: MediaChapter.indexAtPosition(widget.player.state.position, widget.chapters),
      builder: (context, chapterSnapshot) {
        final currentChapterIndex = chapterSnapshot.data;

        return _buildStrip(
          tab: _StripTab.chapters,
          controller: _chapterScrollController,
          keys: _chapterItemKeys,
          nodes: _chapterFocusNodes,
          focusPrefix: 'ChapterFocus',
          itemCount: widget.chapters.length,
          isTablet: isTablet,
          autoScrollIndex: currentChapterIndex,
          autoScrollToken: currentChapterIndex,
          itemBuilder: (context, index, itemKey) {
            final chapter = widget.chapters[index];

            final localThumbPath = widget.serverId != null && chapter.thumb != null
                ? DownloadStorageService.instance.getArtworkPathSync(ServerId(widget.serverId!), chapter.thumb!)
                : null;

            final VoidCallback? onTap = widget.canControl
                ? () => unawaited(_handleChapterTap(chapter.startTime))
                : null;

            return (
              _buildStripItem(
                key: itemKey,
                isCurrent: currentChapterIndex == index,
                isTablet: isTablet,
                thumbnail: chapter.thumb != null
                    ? _buildStripThumbnail(
                        client: _tryGetClient(context, serverIdOrNull(widget.serverId)),
                        imagePath: chapter.thumb,
                        localFilePath: localThumbPath,
                        isTablet: isTablet,
                      )
                    : null,
                title: chapter.label,
                subtitle: formatDurationTimestamp(chapter.startTime),
                onTap: onTap,
              ),
              onTap,
            );
          },
        );
      },
    );
  }

  Widget _buildQueueStrip(bool isTablet) {
    return SettingValueBuilder<bool>(
      pref: SettingsService.hideSpoilers,
      builder: (context, hideSpoilers, _) => Consumer<PlaybackStateProvider>(
        builder: (context, playbackState, _) {
          final items = playbackState.loadedItems;
          final currentItemID = playbackState.currentPlayQueueItemID;
          final currentIndex = currentItemID == null
              ? -1
              : items.indexWhere((item) => playbackState.playQueueItemIdFor(item) == currentItemID);

          return _buildStrip(
            tab: _StripTab.queue,
            controller: _queueScrollController,
            keys: _queueItemKeys,
            nodes: _queueFocusNodes,
            focusPrefix: 'QueueFocus',
            itemCount: items.length,
            isTablet: isTablet,
            autoScrollIndex: currentIndex >= 0 ? currentIndex : null,
            autoScrollToken: (currentItemID, currentIndex),
            itemBuilder: (context, index, itemKey) {
              final item = items[index];

              final client = item.serverId != null
                  ? context.tryGetMediaClientForServer(serverIdOrNull(item.serverId))
                  : null;

              void onTap() => widget.onQueueItemSelected?.call(item);

              return (
                _buildStripItem(
                  key: itemKey,
                  isCurrent: playbackState.playQueueItemIdFor(item) == currentItemID,
                  isTablet: isTablet,
                  thumbnail: item.thumbPath != null
                      ? _buildStripThumbnail(client: client, imagePath: item.thumbPath, isTablet: isTablet)
                      : null,
                  blurThumbnail: hideSpoilers && item.shouldHideSpoiler,
                  title: item.title ?? '',
                  subtitle: formatQueueItemSubtitle(item),
                  onTap: onTap,
                ),
                onTap,
              );
            },
          );
        },
      ),
    );
  }

  Widget _buildStripThumbnail({
    required MediaServerClient? client,
    required String? imagePath,
    required bool isTablet,
    String? localFilePath,
  }) {
    return OptimizedMediaImage.thumb(
      client: client,
      imagePath: imagePath,
      localFilePath: localFilePath,
      width: isTablet ? 200.0 : 120.0,
      height: isTablet ? 112.0 : 68.0,
      fit: BoxFit.cover,
      errorWidget: (_, _, _) => const AppIcon(Symbols.image_rounded, fill: 1, color: Colors.white54, size: 34),
    );
  }

  Widget _buildStripItem({
    Key? key,
    required bool isCurrent,
    required Widget? thumbnail,
    required String title,
    required String subtitle,
    required VoidCallback? onTap,
    bool blurThumbnail = false,
    bool isTablet = false,
  }) {
    final itemWidth = isTablet ? 200.0 : 120.0;
    final thumbHeight = isTablet ? 112.0 : 68.0;
    final titleFontSize = isTablet ? 13.0 : 11.0;
    final subtitleFontSize = isTablet ? 12.0 : 10.0;

    final verticalMargin = widget.useFocusNavigation ? 4.0 : 0.0;
    return ClickableCursor(
      child: GestureDetector(
        onTap: onTap,
        child: Container(
          key: key,
          width: itemWidth,
          margin: .symmetric(horizontal: 6, vertical: verticalMargin),
          child: Column(
            mainAxisSize: .min,
            crossAxisAlignment: .start,
            children: [
              MediaSelectorThumbnail(
                width: itemWidth,
                height: thumbHeight,
                thumbnail: thumbnail,
                isCurrent: isCurrent,
                borderColor: Colors.white,
                radius: 6,
                blurThumbnail: blurThumbnail,
              ),
              const SizedBox(height: 4),
              Text(
                title,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: titleFontSize,
                  fontWeight: isCurrent ? FontWeight.w600 : FontWeight.normal,
                ),
                maxLines: 1,
                overflow: .ellipsis,
              ),
              Text(
                subtitle,
                style: TextStyle(
                  color: isCurrent ? Colors.white70 : Colors.white60,
                  fontSize: subtitleFontSize,
                  fontWeight: isCurrent ? FontWeight.w500 : FontWeight.normal,
                ),
                maxLines: 1,
                overflow: .ellipsis,
              ),
            ],
          ),
        ),
      ),
    );
  }
}
