import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:forui/forui.dart' as forui;

import '../../../core/contracts/library.dart';
import '../../../providers/di_providers.dart';
import '../../../providers/session_provider.dart';
import '../../../providers/version_priority_provider.dart';
import '../../../shared/constants/breakpoints.dart';
import '../../../shared/utils/cross_server_search_key.dart';
import '../../../shared/widgets/all_versions_sheet.dart';
import '../../../shared/widgets/app_loading_ring.dart';
import '../../../shared/widgets/frosted_panel.dart';
import '../../../shared/widgets/hover_scrollable_row.dart';
import '../../../shared/widgets/version_filter.dart';
import '../../../shared/widgets/version_grouping.dart';
import '../../../shared/widgets/version_source_card.dart';
import 'multi_source_ordering.dart';
import 'multi_source_scroll.dart';


typedef MultiSourceTap =
    Future<void> Function(
      String serverId,
      String itemId, {
      String? mediaSourceId,
      CrossServerSourceCard? card,
    });


typedef LocalSourceTap = void Function(EmbyRawMediaSource src, int index);

class MultiSourceSection extends ConsumerStatefulWidget {
  final CrossServerSearchReq req;
  final MultiSourceTap? onSourceTap;
  final bool embedded;


  final List<EmbyRawMediaSource> localSources;


  final String? currentLocalSourceId;


  final String? currentServerName;


  final LocalSourceTap? onLocalSourceTap;


  final String? excludedServerIdOverride;


  final String title;


  final String? selectedCrossServerKey;


  final bool showLoadingWhenEmpty;


  final bool externalLoading;


  final String loadingText;

  const MultiSourceSection({
    super.key,
    required this.req,
    this.onSourceTap,
    this.embedded = false,
    this.localSources = const [],
    this.currentLocalSourceId,
    this.currentServerName,
    this.onLocalSourceTap,
    this.excludedServerIdOverride,
    this.title = '多源聚合',
    this.selectedCrossServerKey,
    this.showLoadingWhenEmpty = false,
    this.externalLoading = false,
    this.loadingText = '正在查找可用版本…',
  });

  @override
  ConsumerState<MultiSourceSection> createState() => _MultiSourceSectionState();
}

String _bucketOf(CrossServerSourceCard card) {
  return versionBucketOf(card.quality?.resolutionLabel);
}

String _bucketOfLocal(EmbyRawMediaSource src) {
  return versionBucketOf(MediaQualityInfo.fromMediaSource(src).resolutionLabel);
}

class _MultiSourceSectionState extends ConsumerState<MultiSourceSection> {
  static final double _cardWidth = Platform.isAndroid ? 170.0 : 220.0;
  static const double _cardGap = 12.0;

  final ScrollController _scrollController = ScrollController();
  String? _selectedBucket;

  String? _autoScrolledKey;

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

  @override
  Widget build(BuildContext context) {
    final session = ref.watch(activeSessionProvider);
    if (session == null) return const SizedBox.shrink();

    final req = widget.req;
    final key = (
      activeServerId: session.serverId,
      anchorType: req.anchorType,
      providerIdQuery: encodeProviderIdsForKey(req.providerIds),
      seriesProviderIdQuery: encodeProviderIdsForKey(
        req.seriesProviderIds ?? const {},
      ),
      parentIndexNumber: req.parentIndexNumber ?? -1,
      indexNumber: req.indexNumber ?? -1,
      itemName: req.itemName,
      excludedServerId: widget.excludedServerIdOverride ?? session.serverId,
    );

    final asyncResult = ref.watch(crossServerSearchDataProvider(key));

    return _renderContent(
      asyncResult,
      widget.excludedServerIdOverride ?? session.serverId,
    );
  }

  Widget _renderContent(
    AsyncValue<List<CrossServerSourceCard>> asyncResult,
    String currentServerId,
  ) {
    final cards = asyncResult.maybeWhen(
      data: (list) => list,
      orElse: () => asyncResult.value ?? const <CrossServerSourceCard>[],
    );

    final priorities =
        ref.watch(versionPriorityProvider).value?.activePriorities ??
        const <VersionPriority>[];

    final entries = <MultiSourceVersionEntry>[];
    final localSources = widget.localSources;
    for (var i = 0; i < localSources.length; i++) {
      final src = localSources[i];
      final isCurrent =
          widget.currentLocalSourceId != null &&
          src.Id == widget.currentLocalSourceId;
      entries.add(
        MultiSourceVersionEntry(
          bucket: _bucketOfLocal(src),
          data: _localCardData(src, i, currentServerId),
          source: src,
          isLocal: true,
          isActive: isCurrent,
          localIndex: i,
          serverName: widget.currentServerName ?? '本服务器',
          serverId: '',
          itemId: '',
          mediaSourceId: src.Id ?? '',
        ),
      );
    }
    for (final c in cards) {
      final isSelected =
          widget.selectedCrossServerKey != null &&
          widget.selectedCrossServerKey == '${c.serverId}|${c.mediaSourceId}';
      entries.add(
        MultiSourceVersionEntry(
          bucket: _bucketOf(c),
          data: _crossServerCardData(c),
          source: c.mediaSource,
          isLocal: false,
          isActive: isSelected,
          localIndex: -1,
          serverName: c.serverName,
          serverId: c.serverId,
          itemId: c.landingItemId,
          mediaSourceId: c.mediaSourceId,
        ),
      );
    }

    sortMultiSourceEntries(entries, priorities);
    final allGroups = groupVersionEntries([
      for (final entry in entries) entry.data,
    ]);

    final bucketsPresent = <String>{};
    for (final e in entries) {
      bucketsPresent.add(e.bucket);
    }
    final buckets = kVersionBucketOrder
        .where(bucketsPresent.contains)
        .toList(growable: false);

    final canonicalSelected =
        _selectedBucket != null && bucketsPresent.contains(_selectedBucket)
        ? _selectedBucket!
        : defaultSelectedBucketForEntries(entries);

    final filteredGroups = canonicalSelected == null
        ? allGroups
        : allGroups
              .where(
                (group) =>
                    versionBucketOf(group.representative.resolutionLabel) ==
                    canonicalSelected,
              )
              .toList(growable: false);

    final hasMergedGroup = allGroups.any((group) => group.members.length > 1);
    final showAllVersionsButton =
        filteredGroups.length > inlineVersionLimit() || hasMergedGroup;

    final width = MediaQuery.sizeOf(context).width;
    final compact = width < Breakpoints.detail;
    final hPad = compact ? 16.0 : 32.0;

    if (entries.isEmpty) {
      final loading = widget.externalLoading || asyncResult.isLoading;
      if (widget.showLoadingWhenEmpty && loading) {
        return _wrapContent(
          _buildLoadingContent(widget.embedded ? 0.0 : hPad),
          hPad,
        );
      }
      return const SizedBox.shrink();
    }

    final content = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        _buildHeader(
          buckets,
          canonicalSelected,
          allGroups,
          entries.length,
          showAllVersionsButton,
          widget.embedded ? 0.0 : hPad,
        ),
        const SizedBox(height: 16),
        _buildCardList(
          filteredGroups,
          canonicalSelected,
          widget.embedded ? 0.0 : hPad,
        ),
      ],
    );

    return _wrapContent(content, hPad);
  }

  Widget _wrapContent(Widget content, double hPad) {
    if (widget.embedded) return content;
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: hPad),
      child: FrostedPanel(
        radius: 16,
        padding: EdgeInsets.all(hPad),
        blurSigma: 15,
        border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
        color: Colors.black.withValues(alpha: 0.3),
        child: content,
      ),
    );
  }

  Widget _buildLoadingContent(double hPad) {
    final compact = MediaQuery.sizeOf(context).width < Breakpoints.compact;
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: EdgeInsets.symmetric(horizontal: hPad),
          child: Row(
            children: [
              Text(
                widget.title,
                style: TextStyle(
                  fontSize: compact ? 17 : 20,
                  fontWeight: FontWeight.w600,
                  color: Colors.white.withValues(alpha: 0.9),
                ),
              ),
              const Spacer(),
              const AppLoadingRing(size: 16, color: Colors.white),
              const SizedBox(width: 8),
              Flexible(
                child: Text(
                  widget.loadingText,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    fontSize: 13,
                    color: Colors.white.withValues(alpha: 0.62),
                  ),
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 16),
        SizedBox(height: Platform.isAndroid ? 100 : 120),
      ],
    );
  }

  Widget _buildHeader(
    List<String> buckets,
    String? selectedBucket,
    List<VersionSourceGroup> allGroups,
    int sourceCount,
    bool showAllVersionsButton,
    double hPad,
  ) {
    final compact = MediaQuery.sizeOf(context).width < Breakpoints.compact;
    final titleAndAction = Wrap(
      spacing: 10,
      runSpacing: 8,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: [
        Text(
          widget.title,
          style: TextStyle(
            fontSize: compact ? 17 : 20,
            fontWeight: FontWeight.w600,
            color: Colors.white.withValues(alpha: 0.9),
          ),
        ),
        if (showAllVersionsButton)
          forui.FButton(
            variant: forui.FButtonVariant.secondary,
            size: forui.FButtonSizeVariant.xs,
            mainAxisSize: MainAxisSize.min,
            onPress: () =>
                showAllVersionsSheet(context: context, groups: allGroups),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text('全部 $sourceCount 个版本'),
                const SizedBox(width: 2),
                const Icon(Icons.chevron_right_rounded, size: 16),
              ],
            ),
          ),
      ],
    );
    final filterChips = Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        for (
          var bucketIndex = 0;
          bucketIndex < buckets.length;
          bucketIndex++
        ) ...[
          if (bucketIndex > 0) const SizedBox(width: 8),
          VersionFilterChip(
            label: buckets[bucketIndex],
            selected: selectedBucket == buckets[bucketIndex],
            showDot: true,
            onTap: () => setState(() => _selectedBucket = buckets[bucketIndex]),
          ),
        ],
      ],
    );

    return Padding(
      padding: EdgeInsets.symmetric(horizontal: hPad),
      child: compact
          ? Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                titleAndAction,
                if (buckets.isNotEmpty) ...[
                  const SizedBox(height: 12),
                  SingleChildScrollView(
                    scrollDirection: Axis.horizontal,
                    child: filterChips,
                  ),
                ],
              ],
            )
          : Row(
              children: [
                titleAndAction,
                if (buckets.isNotEmpty) ...[
                  const SizedBox(width: 16),
                  Expanded(
                    child: Align(
                      alignment: Alignment.centerRight,
                      child: SingleChildScrollView(
                        scrollDirection: Axis.horizontal,
                        reverse: true,
                        child: filterChips,
                      ),
                    ),
                  ),
                ],
              ],
            ),
    );
  }

  Widget _buildCardList(
    List<VersionSourceGroup> groups,
    String? bucket,
    double hPad,
  ) {
    if (groups.isEmpty) return const SizedBox.shrink();

    final inlineLimit = inlineVersionLimit();
    final inlineGroups = groups.take(inlineLimit).toList(growable: true);
    final activeGroupIndex = groups.indexWhere(
      (group) => group.hasActiveMember,
    );
    if (activeGroupIndex >= inlineLimit && inlineGroups.isNotEmpty) {
      inlineGroups[inlineGroups.length - 1] = groups[activeGroupIndex];
    }

    _maybeScrollActiveIntoView(inlineGroups, bucket, hPad);
    final itemCount = inlineGroups.length;

    return SizedBox(
      height: Platform.isAndroid ? 100 : 120,
      child: HoverScrollableRow(
        controller: _scrollController,
        builder: (_, controller) => ListView.builder(
          controller: controller,
          scrollDirection: Axis.horizontal,
          padding: EdgeInsets.fromLTRB(hPad, 0, hPad, 0),
          itemCount: itemCount,
          itemBuilder: (_, i) => Padding(
            padding: EdgeInsets.only(right: i < itemCount - 1 ? 12 : 0),
            child: VersionSourceCard(
              data: inlineGroups[i].displayData,
              onMergedServersTap: inlineGroups[i].members.length > 1
                  ? () => showAllVersionsSheet(
                      context: context,
                      groups: [inlineGroups[i]],
                      title: '选择服务器',
                    )
                  : null,
            ),
          ),
        ),
      ),
    );
  }

  void _maybeScrollActiveIntoView(
    List<VersionSourceGroup> groups,
    String? bucket,
    double leadingPad,
  ) {
    final activeGroupIndex = groups.indexWhere(
      (group) => group.hasActiveMember,
    );
    final activeSourceKey = activeGroupIndex < 0
        ? '<none>'
        : groups[activeGroupIndex].activeMember?.sourceKey ?? '<none>';
    final key = '${bucket ?? '<all>'}|$activeSourceKey';
    if (key == _autoScrolledKey) return;
    _autoScrolledKey = key;

    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted || !_scrollController.hasClients) return;
      final position = _scrollController.position;
      _scrollController.jumpTo(
        multiSourceScrollOffset(
          activeIndex: activeGroupIndex,
          viewportWidth: position.viewportDimension,
          leadingPad: leadingPad,
          maxScrollExtent: position.maxScrollExtent,
          cardWidth: _cardWidth,
          cardGap: _cardGap,
        ),
      );
    });
  }

  VersionSourceCardData _localCardData(
    EmbyRawMediaSource src,
    int index,
    String currentServerId,
  ) {
    final tap = widget.onLocalSourceTap;
    final isCurrent =
        widget.currentLocalSourceId != null &&
        src.Id == widget.currentLocalSourceId;
    return VersionSourceCardData.fromLocalSource(
      src,
      serverName: widget.currentServerName ?? '本服务器',
      serverKey: currentServerId,
      isCurrent: isCurrent,
      tooltip: isCurrent ? null : '切换到该版本',
      onTap: (tap == null || isCurrent) ? null : () async => tap(src, index),
    );
  }

  VersionSourceCardData _crossServerCardData(CrossServerSourceCard card) {
    final selectedKey = widget.selectedCrossServerKey;
    final isSelected =
        selectedKey != null &&
        selectedKey == '${card.serverId}|${card.mediaSourceId}';
    final data = VersionSourceCardData.fromCrossServer(
      card,
      selected: isSelected,
      onTap: _tapHandlerFor(card),
    );
    return data;
  }

  Future<void> Function()? _tapHandlerFor(CrossServerSourceCard card) {
    final tap = widget.onSourceTap;
    if (tap == null) return null;
    return () => tap(
      card.serverId,
      card.landingItemId,
      mediaSourceId: card.mediaSourceId,
      card: card,
    );
  }
}
