import 'dart:async';

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

import '../../core/contracts/script_widget.dart';
import '../../core/domain/usecases/script_widget/script_widget_service.dart';
import '../../providers/di_providers.dart';
import '../../shared/constants/layout_constants.dart';
import '../../shared/theme/app_theme.dart';
import '../../shared/utils/user_error_formatter.dart';
import '../../shared/widgets/app_card.dart';
import '../../shared/widgets/app_error_state.dart';
import '../../shared/widgets/app_loading_ring.dart';
import '../../shared/widgets/app_sliver_header.dart';
import '../../shared/widgets/empty_state.dart';
import '../../shared/widgets/error_media_row.dart';
import '../../shared/widgets/hover_scrollable_row.dart';
import '../../shared/widgets/media_poster_card.dart';
import '../../shared/widgets/media_row_metrics.dart';
import '../../shared/widgets/page_background.dart';
import '../../shared/widgets/script_video_card.dart';
import '../../shared/widgets/section_header.dart';
import '../../shared/widgets/skeleton_media_row.dart';
import 'script_widget_parameter_form.dart';

class ModuleBrowsePage extends ConsumerStatefulWidget {
  final String widgetId;

  const ModuleBrowsePage({super.key, required this.widgetId});

  @override
  ConsumerState<ModuleBrowsePage> createState() => _ModuleBrowsePageState();
}

class _ModuleBrowsePageState extends ConsumerState<ModuleBrowsePage> {
  final TextEditingController _searchController = TextEditingController();
  final Map<String, Map<String, Object?>> _moduleParameters =
      <String, Map<String, Object?>>{};

  InstalledScriptWidget? _installedWidget;
  List<ScriptWidgetContentSection> _searchSections =
      const <ScriptWidgetContentSection>[];
  bool _searchMode = false;
  bool _filtersExpanded = false;
  bool _loadingSearch = false;
  String? _searchError;

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

  List<ScriptWidgetModule> _contentModules(ScriptWidgetManifest manifest) {
    return manifest.modules
        .where(
          (module) =>
              module.type != 'danmu' &&
              module.type != 'stream' &&
              module.id != 'loadResource',
        )
        .toList(growable: false);
  }

  Map<String, Object?> _parametersFor(ScriptWidgetModule module) {
    return _moduleParameters.putIfAbsent(
      _moduleKey(module),
      () => buildScriptWidgetParameterDefaults(module.params),
    );
  }

  List<ScriptWidgetParam> _searchParameters(ScriptWidgetModule? searchModule) {
    if (searchModule == null) return const <ScriptWidgetParam>[];
    return searchModule.params
        .where((parameter) {
          final normalizedName = parameter.name.toLowerCase();
          final isKeywordParameter = const <String>{
            'keyword',
            'query',
            'search',
            'title',
          }.contains(normalizedName);
          final isInternalParameter = const <String>{
            'constant',
            'page',
            'offset',
          }.contains(parameter.type);
          return !isKeywordParameter && !isInternalParameter;
        })
        .toList(growable: false);
  }

  Future<void> _search() async {
    final searchModule = _installedWidget?.manifest.search;
    if (searchModule == null) return;
    final keyword = _searchController.text.trim();
    if (keyword.isEmpty) return;

    final searchParameters = <String, Object?>{
      ...buildScriptWidgetParameterDefaults(searchModule.params),
      ..._parametersFor(searchModule),
    };
    final keywordParameter = searchModule.params
        .where(
          (parameter) => const <String>{
            'keyword',
            'query',
            'search',
            'title',
          }.contains(parameter.name.toLowerCase()),
        )
        .firstOrNull;
    searchParameters[keywordParameter?.name ?? 'keyword'] = keyword;

    setState(() {
      _loadingSearch = true;
      _searchError = null;
    });
    try {
      final service = await ref.read(scriptWidgetServiceProvider.future);
      final items = await service.search(widget.widgetId, searchParameters);
      if (!mounted) return;
      setState(() {
        _searchSections = <ScriptWidgetContentSection>[
          ScriptWidgetContentSection(title: '搜索结果', items: items),
        ];
      });
    } catch (error) {
      if (!mounted) return;
      setState(() {
        _searchError = formatUserError(error);
        _searchSections = const <ScriptWidgetContentSection>[];
      });
    } finally {
      if (mounted) {
        setState(() => _loadingSearch = false);
      }
    }
  }

  ScriptWidgetParam? get _searchPagingParameter {
    final searchModule = _installedWidget?.manifest.search;
    return searchModule?.params
        .where(
          (parameter) => parameter.type == 'page' || parameter.type == 'offset',
        )
        .firstOrNull;
  }

  Future<void> _changeSearchPage(int direction) async {
    final searchModule = _installedWidget?.manifest.search;
    final pagingParameter = _searchPagingParameter;
    if (searchModule == null || pagingParameter == null) return;

    final parameters = _parametersFor(searchModule);
    final minimumValue = pagingParameter.type == 'page' ? 1 : 0;
    final currentValue =
        int.tryParse(parameters[pagingParameter.name]?.toString() ?? '') ??
        minimumValue;
    final countParameter = searchModule.params
        .where((parameter) => parameter.type == 'count')
        .firstOrNull;
    final pageStep = pagingParameter.type == 'offset'
        ? int.tryParse(parameters[countParameter?.name]?.toString() ?? '') ?? 20
        : 1;
    parameters[pagingParameter.name] = (currentValue + direction * pageStep)
        .clamp(minimumValue, 1 << 31);
    await _search();
  }

  bool get _canGoBackSearchPage {
    final searchModule = _installedWidget?.manifest.search;
    final pagingParameter = _searchPagingParameter;
    if (searchModule == null || pagingParameter == null) return false;
    final minimumValue = pagingParameter.type == 'page' ? 1 : 0;
    final currentValue =
        int.tryParse(
          _parametersFor(searchModule)[pagingParameter.name]?.toString() ?? '',
        ) ??
        minimumValue;
    return currentValue > minimumValue;
  }

  String get _currentSearchPageLabel {
    final searchModule = _installedWidget?.manifest.search;
    final pagingParameter = _searchPagingParameter;
    if (searchModule == null || pagingParameter == null) return '第 1 页';
    final parameters = _parametersFor(searchModule);
    final minimumValue = pagingParameter.type == 'page' ? 1 : 0;
    final currentValue =
        int.tryParse(parameters[pagingParameter.name]?.toString() ?? '') ??
        minimumValue;
    if (pagingParameter.type == 'page') return '第 $currentValue 页';

    final countParameter = searchModule.params
        .where((parameter) => parameter.type == 'count')
        .firstOrNull;
    final pageSize =
        int.tryParse(parameters[countParameter?.name]?.toString() ?? '') ?? 20;
    return '第 ${(currentValue ~/ pageSize) + 1} 页';
  }

  @override
  Widget build(BuildContext context) {
    final installedWidget = ref.watch(
      installedScriptWidgetProvider(widget.widgetId),
    );
    return Scaffold(
      backgroundColor: Colors.transparent,
      extendBodyBehindAppBar: true,
      body: Stack(
        children: [
          const Positioned.fill(child: PageBackground()),
          installedWidget.when(
            loading: () => const Center(child: AppLoadingRing()),
            error: (error, _) => Center(
              child: AppErrorState(
                message: formatUserError(error),
                onRetry: () => ref.invalidate(
                  installedScriptWidgetProvider(widget.widgetId),
                ),
              ),
            ),
            data: (widgetData) {
              _installedWidget = widgetData;
              return _buildContent(widgetData);
            },
          ),
        ],
      ),
    );
  }

  Widget _buildContent(InstalledScriptWidget installedWidget) {
    final manifest = installedWidget.manifest;
    final contentModules = _contentModules(manifest);
    final searchParameters = _searchParameters(manifest.search);
    return CustomScrollView(
      physics: const AlwaysScrollableScrollPhysics(
        parent: BouncingScrollPhysics(),
      ),
      slivers: [
        AppSliverHeader(
          title: manifest.title,
          bottom: _searchMode
              ? PreferredSize(
                  preferredSize: const Size.fromHeight(60),
                  child: _SearchControlBar(
                    searchController: _searchController,
                    searchHint: manifest.search?.description,
                    filtersExpanded: _filtersExpanded,
                    hasFilters: searchParameters.isNotEmpty,
                    onSearch: _search,
                    onToggleFilters: () {
                      setState(() => _filtersExpanded = !_filtersExpanded);
                    },
                  ),
                )
              : null,
          actions: [
            if (manifest.search != null)
              IconButton(
                onPressed: () {
                  setState(() {
                    _searchMode = !_searchMode;
                    _filtersExpanded = false;
                  });
                },
                tooltip: _searchMode ? '关闭搜索' : '搜索',
                icon: Icon(
                  _searchMode ? Icons.close_rounded : Icons.search_rounded,
                ),
              ),
          ],
        ),
        if (manifest.description.trim().isNotEmpty)
          SliverPadding(
            padding: const EdgeInsets.fromLTRB(20, 14, 20, 6),
            sliver: SliverToBoxAdapter(
              child: _ModuleIdentityCard(installedWidget: installedWidget),
            ),
          ),
        if (_searchMode)
          SliverPadding(
            padding: const EdgeInsets.fromLTRB(20, 12, 20, 56),
            sliver: SliverList(
              delegate: SliverChildListDelegate.fixed([
                _buildSearchFilterPanel(manifest),
                if (_filtersExpanded && searchParameters.isNotEmpty)
                  const SizedBox(height: 18),
                _buildSearchResults(),
              ]),
            ),
          )
        else if (contentModules.isEmpty)
          const SliverFillRemaining(
            hasScrollBody: false,
            child: EmptyState(
              icon: Icons.video_library_outlined,
              title: '没有内容功能',
              message: '该模块没有可浏览的内容功能。',
            ),
          )
        else
          SliverList(
            delegate: SliverChildListDelegate.fixed([
              for (final module in contentModules)
                _ModulePreviewSection(
                  widgetId: widget.widgetId,
                  module: module,
                ),
              const SizedBox(height: 56),
            ]),
          ),
      ],
    );
  }

  Widget _buildSearchFilterPanel(ScriptWidgetManifest manifest) {
    final searchModule = manifest.search;
    final parameters = _searchParameters(searchModule);
    return AnimatedSize(
      duration: const Duration(milliseconds: 180),
      curve: Curves.easeOutCubic,
      child: !_filtersExpanded || searchModule == null || parameters.isEmpty
          ? const SizedBox.shrink()
          : AppCard(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  if (searchModule.description.trim().isNotEmpty) ...[
                    Text(
                      searchModule.description,
                      style: Theme.of(context).textTheme.bodySmall?.copyWith(
                        color: context.appTokens.mutedForeground,
                      ),
                    ),
                    const SizedBox(height: 14),
                  ],
                  ScriptWidgetParameterForm(
                    parameters: parameters,
                    initialValues: _parametersFor(searchModule),
                    onChanged: (values) {
                      _parametersFor(searchModule).addAll(values);
                    },
                  ),
                  const SizedBox(height: 14),
                  Align(
                    alignment: Alignment.centerRight,
                    child: FButton(
                      onPress: _search,
                      child: const Text('应用并搜索'),
                    ),
                  ),
                ],
              ),
            ),
    );
  }

  Widget _buildSearchResults() {
    if (_loadingSearch) {
      return const Padding(
        padding: EdgeInsets.symmetric(vertical: 64),
        child: Center(child: AppLoadingRing()),
      );
    }
    if (_searchError case final searchError?) {
      return AppErrorState(
        compact: true,
        message: searchError,
        onRetry: _search,
      );
    }
    if (_searchSections.every((section) => section.items.isEmpty)) {
      return const EmptyState(
        compact: true,
        icon: Icons.search_rounded,
        title: '输入关键词开始搜索',
        message: '搜索结果会显示在这里。',
      );
    }

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        for (final section in _searchSections) ...[
          _ModuleSearchSection(section: section, widgetId: widget.widgetId),
          const SizedBox(height: 28),
        ],
        if (_searchPagingParameter != null)
          _PaginationBar(
            canGoBack: _canGoBackSearchPage,
            pageLabel: _currentSearchPageLabel,
            onPrevious: () => unawaited(_changeSearchPage(-1)),
            onNext: () => unawaited(_changeSearchPage(1)),
          ),
      ],
    );
  }
}

String _moduleKey(ScriptWidgetModule module) {
  return module.id.isEmpty ? module.functionName : module.id;
}

class _ModulePreviewSection extends ConsumerWidget {
  final String widgetId;
  final ScriptWidgetModule module;

  const _ModulePreviewSection({required this.widgetId, required this.module});


  ScriptWidgetParam? get _primaryEnumerationParam {
    return module.params
        .where(
          (parameter) =>
              parameter.type == 'enumeration' &&
              parameter.enumOptions.length >= 2,
        )
        .firstOrNull;
  }

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final previewKey = (widgetId: widgetId, moduleKey: _moduleKey(module));
    final sections = ref.watch(scriptWidgetModulePreviewProvider(previewKey));
    final enumerationParam = _primaryEnumerationParam;
    final categoryRow = enumerationParam == null
        ? const SizedBox.shrink()
        : _ModuleCategoryRow(
            widgetId: widgetId,
            moduleKey: _moduleKey(module),
            moduleTitle: module.title,
            parameter: enumerationParam,
          );
    return sections.when(
      loading: () => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [categoryRow, SkeletonMediaRow(title: module.title)],
      ),
      error: (error, _) => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          categoryRow,
          ErrorMediaRow(
            title: module.title,
            message: formatUserError(error),
            onRetry: () =>
                ref.invalidate(scriptWidgetModulePreviewProvider(previewKey)),
          ),
        ],
      ),
      data: (loadedSections) {
        final visibleSections = loadedSections
            .where((section) => section.items.isNotEmpty)
            .toList(growable: false);
        if (visibleSections.isEmpty && enumerationParam == null) {
          return const SizedBox.shrink();
        }
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            categoryRow,
            for (final section in visibleSections)
              _ModulePreviewRow(
                widgetId: widgetId,
                moduleKey: _moduleKey(module),
                section: section,
              ),
          ],
        );
      },
    );
  }
}


class _ModuleCategoryRow extends StatelessWidget {
  final String widgetId;
  final String moduleKey;
  final String moduleTitle;
  final ScriptWidgetParam parameter;

  const _ModuleCategoryRow({
    required this.widgetId,
    required this.moduleKey,
    required this.moduleTitle,
    required this.parameter,
  });

  static const double _cardHeight = 74;
  static const double _cardWidth = 168;

  @override
  Widget build(BuildContext context) {
    final options = parameter.enumOptions;
    final headerLabel = parameter.title.trim().isEmpty
        ? '$moduleTitle · 类型'
        : '$moduleTitle · ${parameter.title.trim()}';
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(24, 12, 24, 4),
          child: SectionHeader(label: headerLabel, count: options.length),
        ),
        SizedBox(
          height: _cardHeight,
          child: HoverScrollableRow(
            builder: (_, scrollController) => ListView.separated(
              controller: scrollController,
              scrollDirection: Axis.horizontal,
              padding: const EdgeInsets.symmetric(horizontal: 24),
              itemCount: options.length,
              separatorBuilder: (_, _) => const SizedBox(width: 12),
              itemBuilder: (context, index) {
                final option = options[index];
                return _CategoryCard(
                  label: option.title.isEmpty ? option.value : option.title,
                  width: _cardWidth,
                  onTap: () => _openCategory(context, option),
                );
              },
            ),
          ),
        ),
      ],
    );
  }

  void _openCategory(BuildContext context, ScriptWidgetOption option) {
    final targetUri = Uri(
      path:
          '/modules/${Uri.encodeComponent(widgetId)}/module/'
          '${Uri.encodeComponent(moduleKey)}',
      queryParameters: <String, String>{parameter.name: option.value},
    );
    context.push(targetUri.toString());
  }
}

class _CategoryCard extends StatefulWidget {
  final String label;
  final double width;
  final VoidCallback onTap;

  const _CategoryCard({
    required this.label,
    required this.width,
    required this.onTap,
  });

  @override
  State<_CategoryCard> createState() => _CategoryCardState();
}

class _CategoryCardState extends State<_CategoryCard> {
  bool _isHovered = false;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final tokens = context.appTokens;
    return SizedBox(
      width: widget.width,
      child: AppCard(
        filled: true,
        enableHover: false,
        radius: tokens.radiusCard,
        padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
        onTap: widget.onTap,
        onHoverChanged: (isHovered) {
          if (_isHovered == isHovered) return;
          setState(() => _isHovered = isHovered);
        },
        child: Row(
          children: [
            Icon(
              Icons.style_outlined,
              size: 20,
              color: theme.colorScheme.primary.withValues(
                alpha: _isHovered ? 0.9 : 0.65,
              ),
            ),
            const SizedBox(width: 10),
            Expanded(
              child: Text(
                widget.label,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
                style: theme.textTheme.bodyMedium?.copyWith(
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _ModulePreviewRow extends StatelessWidget {
  final String widgetId;
  final String moduleKey;
  final ScriptWidgetContentSection section;

  const _ModulePreviewRow({
    required this.widgetId,
    required this.moduleKey,
    required this.section,
  });

  @override
  Widget build(BuildContext context) {
    final statusItems = section.items
        .where((item) => item.type.toLowerCase() == 'text')
        .toList(growable: false);
    final contentItems = section.items
        .where((item) => item.type.toLowerCase() != 'text')
        .take(20)
        .toList(growable: false);
    final compact = MediaRowMetrics.isCompact(context);
    final cardWidth = MediaRowMetrics.cardWidth(
      MediaCardVariant.poster,
      compact: compact,
    );
    final rowHeight = MediaRowMetrics.rowHeight(
      MediaCardVariant.poster,
      compact: compact,
    );
    final allRoute =
        '/modules/${Uri.encodeComponent(widgetId)}/module/'
        '${Uri.encodeComponent(moduleKey)}';

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (contentItems.isNotEmpty)
          Padding(
            padding: const EdgeInsets.fromLTRB(24, 12, 24, 4),
            child: SectionHeader(
              label: section.title,
              count: contentItems.length,
              trailing: TextButton(
                onPressed: () => context.push(allRoute),
                child: const Text('查看全部'),
              ),
            ),
          ),
        for (final statusItem in statusItems)
          Padding(
            padding: const EdgeInsets.fromLTRB(24, 4, 24, 12),
            child: _ModuleStatusCard(item: statusItem),
          ),
        if (contentItems.isNotEmpty)
          SizedBox(
            height: rowHeight,
            child: HoverScrollableRow(
              builder: (_, scrollController) => ListView.separated(
                controller: scrollController,
                scrollDirection: Axis.horizontal,
                padding: const EdgeInsets.symmetric(horizontal: 24),
                itemCount: contentItems.length,
                separatorBuilder: (_, _) => const SizedBox(width: 14),
                itemBuilder: (context, index) {
                  final item = contentItems[index];
                  return ScriptVideoCard(
                    item: item,
                    width: cardWidth,
                    onTap: () => context.push(
                      '/modules/${Uri.encodeComponent(widgetId)}/detail',
                      extra: item,
                    ),
                  );
                },
              ),
            ),
          ),
      ],
    );
  }
}

class _ModuleSearchSection extends StatelessWidget {
  final ScriptWidgetContentSection section;
  final String widgetId;

  const _ModuleSearchSection({required this.section, required this.widgetId});

  @override
  Widget build(BuildContext context) {
    final statusItems = section.items
        .where((item) => item.type.toLowerCase() == 'text')
        .toList(growable: false);
    final contentItems = section.items
        .where((item) => item.type.toLowerCase() != 'text')
        .toList(growable: false);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SectionHeader(label: section.title, count: contentItems.length),
        for (final statusItem in statusItems) ...[
          _ModuleStatusCard(item: statusItem),
          const SizedBox(height: 12),
        ],
        if (contentItems.isNotEmpty)
          GridView.builder(
            shrinkWrap: true,
            physics: const NeverScrollableScrollPhysics(),
            itemCount: contentItems.length,
            gridDelegate: mediaGridDelegate,
            itemBuilder: (context, index) {
              final item = contentItems[index];
              return ScriptVideoCard(
                item: item,
                width: double.infinity,
                onTap: () => context.push(
                  '/modules/${Uri.encodeComponent(widgetId)}/detail',
                  extra: item,
                ),
              );
            },
          ),
      ],
    );
  }
}

class _ModuleIdentityCard extends StatelessWidget {
  final InstalledScriptWidget installedWidget;

  const _ModuleIdentityCard({required this.installedWidget});

  @override
  Widget build(BuildContext context) {
    final manifest = installedWidget.manifest;
    return AppCard(
      padding: const EdgeInsets.all(16),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 44,
            height: 44,
            decoration: BoxDecoration(
              color: Theme.of(
                context,
              ).colorScheme.primary.withValues(alpha: 0.12),
              borderRadius: BorderRadius.circular(14),
            ),
            child: Icon(
              Icons.extension_rounded,
              color: Theme.of(context).colorScheme.primary,
            ),
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  manifest.description,
                  style: Theme.of(context).textTheme.bodyMedium,
                ),
                const SizedBox(height: 6),
                Text(
                  [
                    if (manifest.author.isNotEmpty) manifest.author,
                    'v${manifest.version}',
                  ].join(' · '),
                  style: Theme.of(context).textTheme.bodySmall?.copyWith(
                    color: context.appTokens.mutedForeground,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _ModuleStatusCard extends StatelessWidget {
  final ScriptVideoItem item;

  const _ModuleStatusCard({required this.item});

  @override
  Widget build(BuildContext context) {
    final normalizedId = item.id.toLowerCase();
    final indicatesError =
        normalizedId.startsWith('err') ||
        item.title.contains('失败') ||
        item.title.contains('错误') ||
        item.title.contains('拦截');
    final foregroundColor = indicatesError
        ? Theme.of(context).colorScheme.error
        : context.appTokens.mutedForeground;
    return AppCard(
      padding: const EdgeInsets.all(16),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Icon(
            indicatesError
                ? Icons.error_outline_rounded
                : Icons.info_outline_rounded,
            color: foregroundColor,
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  item.title.isEmpty ? '模块提示' : item.title,
                  style: Theme.of(context).textTheme.titleSmall?.copyWith(
                    color: foregroundColor,
                    fontWeight: FontWeight.w700,
                  ),
                ),
                if (item.description.trim().isNotEmpty) ...[
                  const SizedBox(height: 4),
                  Text(
                    item.description,
                    style: Theme.of(context).textTheme.bodySmall?.copyWith(
                      color: context.appTokens.mutedForeground,
                    ),
                  ),
                ],
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _SearchControlBar extends StatelessWidget {
  final TextEditingController searchController;
  final String? searchHint;
  final bool filtersExpanded;
  final bool hasFilters;
  final VoidCallback onSearch;
  final VoidCallback onToggleFilters;

  const _SearchControlBar({
    required this.searchController,
    required this.searchHint,
    required this.filtersExpanded,
    required this.hasFilters,
    required this.onSearch,
    required this.onToggleFilters,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final tokens = context.appTokens;
    return SizedBox(
      height: 60,
      child: Padding(
        padding: const EdgeInsets.fromLTRB(16, 6, 12, 8),
        child: Row(
          children: [
            Expanded(
              child: TextField(
                controller: searchController,
                autofocus: true,
                textInputAction: TextInputAction.search,
                decoration: InputDecoration(
                  hintText: searchHint?.trim().isNotEmpty == true
                      ? searchHint
                      : '输入关键词',
                  prefixIcon: const Icon(Icons.search_rounded, size: 19),
                  filled: true,
                  fillColor: theme.colorScheme.onSurface.withValues(
                    alpha: 0.05,
                  ),
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(10),
                    borderSide: BorderSide.none,
                  ),
                  contentPadding: const EdgeInsets.symmetric(
                    horizontal: 12,
                    vertical: 10,
                  ),
                  isDense: true,
                ),
                onSubmitted: (_) => onSearch(),
              ),
            ),
            const SizedBox(width: 8),
            IconButton(
              onPressed: onSearch,
              icon: const Icon(Icons.arrow_forward_rounded),
              tooltip: '搜索',
            ),
            if (hasFilters)
              IconButton(
                onPressed: onToggleFilters,
                icon: Icon(
                  Icons.tune_rounded,
                  color: filtersExpanded
                      ? theme.colorScheme.primary
                      : tokens.mutedForeground,
                ),
                tooltip: filtersExpanded ? '收起筛选' : '展开筛选',
              ),
          ],
        ),
      ),
    );
  }
}

class _PaginationBar extends StatelessWidget {
  final bool canGoBack;
  final String pageLabel;
  final VoidCallback onPrevious;
  final VoidCallback onNext;

  const _PaginationBar({
    required this.canGoBack,
    required this.pageLabel,
    required this.onPrevious,
    required this.onNext,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        FButton(
          variant: FButtonVariant.outline,
          onPress: canGoBack ? onPrevious : null,
          child: const Text('上一页'),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 14),
          child: Text(
            pageLabel,
            style: Theme.of(context).textTheme.bodySmall?.copyWith(
              color: context.appTokens.mutedForeground,
            ),
          ),
        ),
        FButton(onPress: onNext, child: const Text('下一页')),
      ],
    );
  }
}
