import 'dart:async'; import 'dart:convert'; import 'package:f0ckapp/controller/media_controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:f0ckapp/models/suggestion_model.dart'; class CustomSearchDelegate extends SearchDelegate { final MediaController controller = Get.find(); Timer? _debounceTimer; List? _suggestions; bool _isLoading = false; String? _error; String _lastFetchedQuery = ""; @override List buildActions(BuildContext context) { return [ IconButton( icon: const Icon(Icons.clear), onPressed: () { query = ''; _clearResults(); showSuggestions(context); }, ), ]; } @override Widget buildLeading(BuildContext context) { return IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { _debounceTimer?.cancel(); close(context, 'null'); }, ); } @override Widget buildResults(BuildContext context) { return Center(child: Text('Suchergebnisse für: "$query"')); } Future> fetchSuggestions(String query) async { final Uri uri = Uri.parse('https://api.f0ck.me/search/?q=$query'); try { final http.Response response = await http .get(uri) .timeout(const Duration(seconds: 5)); if (response.statusCode == 200) { final dynamic decoded = jsonDecode(response.body); if (decoded is List) { final suggestions = decoded .map((item) => Suggestion.fromJson(item as Map)) .toList(); suggestions.sort((a, b) => b.score.compareTo(a.score)); return suggestions; } else { throw Exception('Unerwartetes Format: Es wurde eine Liste erwartet.'); } } else if (response.statusCode == 400) { final dynamic error = jsonDecode(response.body); final String message = error is Map ? error['detail']?.toString() ?? 'Unbekannter Fehler.' : 'Unbekannter Fehler.'; throw Exception('Client-Fehler 400: $message'); } else { throw Exception( 'Fehler beim Abrufen der Vorschläge: ${response.statusCode}', ); } } on TimeoutException { throw Exception('Anfrage an die API hat zu lange gedauert.'); } catch (e) { throw Exception('Fehler bei der Verarbeitung der Anfrage: $e'); } } @override Widget buildSuggestions(BuildContext context) { return StatefulBuilder( builder: (BuildContext context, void Function(void Function()) setState) { if (query.isEmpty) { _debounceTimer?.cancel(); return Container( padding: const EdgeInsets.all(16.0), child: const Text(''), ); } if (query != _lastFetchedQuery) { _debounceTimer?.cancel(); _isLoading = true; _error = null; _suggestions = null; _debounceTimer = Timer(Duration(milliseconds: 500), () async { try { final List results = await fetchSuggestions(query); _lastFetchedQuery = query; setState(() { _suggestions = results; _isLoading = false; }); } catch (e) { _lastFetchedQuery = query; setState(() { _error = e.toString(); _suggestions = []; _isLoading = false; }); } }); return Center(child: _buildLoadingIndicator()); } if (_isLoading) { return Center(child: _buildLoadingIndicator()); } if (_error != null) { return Center(child: Text("Fehler: $_error")); } if (_suggestions == null || _suggestions!.isEmpty) { return Center(child: const Text("Keine Ergebnisse gefunden.")); } return ListView.builder( itemCount: _suggestions!.length, itemBuilder: (BuildContext context, int index) { final Suggestion suggestion = _suggestions![index]; return ListTile( title: Text(suggestion.tag), subtitle: Text( 'Getaggt: ${suggestion.tagged}x • Score: ${suggestion.score.toStringAsFixed(2)}', style: TextStyle(fontSize: 12), ), onTap: () async { await controller.setTag(suggestion.tag); close(context, suggestion.tag); }, ); }, ); }, ); } Widget _buildLoadingIndicator() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(strokeWidth: 3.0), const SizedBox(height: 12), const Text( 'Vorschläge werden geladen...', style: TextStyle(fontStyle: FontStyle.italic), ), ], ); } void _clearResults() { _debounceTimer?.cancel(); _suggestions = null; _isLoading = false; _error = null; _lastFetchedQuery = ""; } @override void close(BuildContext context, String result) { _debounceTimer?.cancel(); super.close(context, result); } }