import 'dart:async';

import 'package:flutter/material.dart';

/// 轻量级异步搜索控制器，配合 [SearchBar] 使用。
///
/// 替代已停维的 `flappy_search_bar` 的 `SearchBarController`。
/// 保留泛型 `<T>` 以兼容调用方 `SearchBarController<Product>()` 写法。
/// [replayLastSearch] 用于“加载更多 / 分页”：外部 page++ 后触发重跑上一次搜索。
class SearchBarController<T> {
  String lastQuery = '';
  VoidCallback? _replay;

  void _register(VoidCallback replay) {
    _replay = replay;
  }

  /// 重新执行上一次搜索（分页加载更多时调用）。
  void replayLastSearch() => _replay?.call();
}

/// 轻量级 Web 异步搜索栏，替代已停维的 `flappy_search_bar` 的 `SearchBar`。
///
/// 基本流程：输入达到 [minimumChars] 个字符后，调用 [onSearch] 取结果，
/// 用 [onItemFound] 渲染每一条；无结果时显示 [emptyWidget]；出错显示 [onError]。
///
/// 注意：`searchBarController` 参数刻意使用裸类型（`SearchBarController?`），
/// 以兼容调用方 `SearchBarController _controller = SearchBarController<Product>();`
/// 的写法（Dart 3 sound 类型下避免泛型不变性报错）。
class SearchBar<T> extends StatefulWidget {
  final SearchBarController? searchBarController;
  final int minimumChars;
  final String? hintText;
  final Widget? cancellationWidget;
  final Future<List<T>> Function(String text) onSearch;
  final Widget Function(T item, int index) onItemFound;
  final Widget Function(dynamic error)? onError;
  final Widget? emptyWidget;
  final Duration debounceDuration;

  const SearchBar({
    super.key,
    this.searchBarController,
    this.minimumChars = 2,
    this.hintText,
    this.cancellationWidget,
    required this.onSearch,
    required this.onItemFound,
    this.onError,
    this.emptyWidget,
    this.debounceDuration = const Duration(milliseconds: 350),
  });

  @override
  State<SearchBar<T>> createState() => _SearchBarState<T>();
}

class _SearchBarState<T> extends State<SearchBar<T>> {
  final TextEditingController _textController = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  Timer? _debounce;
  List<T> _results = <T>[];
  bool _loading = false;
  dynamic _error;
  bool _hasSearched = false;

  @override
  void initState() {
    super.initState();
    widget.searchBarController?._register(_runSearch);
    _textController.addListener(_onTextChanged);
  }

  @override
  void dispose() {
    _debounce?.cancel();
    _textController.dispose();
    _focusNode.dispose();
    super.dispose();
  }

  void _onTextChanged() {
    final text = _textController.text;
    _debounce?.cancel();
    if (text.length >= widget.minimumChars) {
      _debounce = Timer(widget.debounceDuration, _runSearch);
    } else if (text.isEmpty) {
      setState(() {
        _results = <T>[];
        _hasSearched = false;
        _error = null;
      });
    }
  }

  Future<void> _runSearch() async {
    final query = _textController.text;
    if (query.length < widget.minimumChars) return;
    if (widget.searchBarController != null) {
      widget.searchBarController!.lastQuery = query;
    }
    setState(() {
      _loading = true;
      _error = null;
      _hasSearched = true;
    });
    try {
      final results = await widget.onSearch(query);
      if (mounted) {
        setState(() {
          _results = results;
          _loading = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _error = e;
          _loading = false;
        });
      }
    }
  }

  void _cancel() {
    _textController.clear();
    _focusNode.unfocus();
    setState(() {
      _results = <T>[];
      _hasSearched = false;
      _error = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          controller: _textController,
          focusNode: _focusNode,
          decoration: InputDecoration(
            hintText: widget.hintText,
            prefixIcon: const Icon(Icons.search),
            suffixIcon: _textController.text.isNotEmpty
                ? GestureDetector(
                    onTap: _cancel,
                    child: widget.cancellationWidget ??
                        const Icon(Icons.close, size: 20),
                  )
                : null,
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
            ),
            contentPadding: const EdgeInsets.symmetric(horizontal: 12),
          ),
        ),
        const SizedBox(height: 8),
        Expanded(child: _buildBody()),
      ],
    );
  }

  Widget _buildBody() {
    if (_loading) {
      return const Center(child: CircularProgressIndicator());
    }
    if (_error != null) {
      return widget.onError != null
          ? widget.onError!(_error)
          : Center(child: Text('$_error'));
    }
    if (!_hasSearched) {
      return const SizedBox.shrink();
    }
    if (_results.isEmpty) {
      return widget.emptyWidget ?? const SizedBox.shrink();
    }
    return ListView.builder(
      itemCount: _results.length,
      itemBuilder: (BuildContext context, int index) =>
          widget.onItemFound(_results[index], index),
    );
  }
}
