import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/api/api_paths.dart'; import '../../../core/api/api_providers.dart'; import '../../auth/data/auth_providers.dart'; class _AiSuggestion { final String name; final String description; final List mainIngredients; final List missingIngredients; final String estimatedTime; const _AiSuggestion({ required this.name, required this.description, required this.mainIngredients, required this.missingIngredients, required this.estimatedTime, }); factory _AiSuggestion.fromJson(Map json) { return _AiSuggestion( name: json['name'] as String? ?? '', description: json['description'] as String? ?? '', mainIngredients: (json['mainIngredients'] as List?) ?.map((e) => e.toString()) .toList() ?? [], missingIngredients: (json['missingIngredients'] as List?) ?.map((e) => e.toString()) .toList() ?? [], estimatedTime: json['estimatedTime'] as String? ?? '', ); } } class AiRecipeSuggestionsScreen extends ConsumerStatefulWidget { const AiRecipeSuggestionsScreen({super.key}); @override ConsumerState createState() => _AiRecipeSuggestionsScreenState(); } class _AiRecipeSuggestionsScreenState extends ConsumerState { bool _isLoading = false; String? _error; List<_AiSuggestion> _suggestions = []; bool _hasFetched = false; Future _generateSuggestions() async { setState(() { _isLoading = true; _error = null; }); try { final token = await ref.read(authStateProvider.future); final api = ref.read(apiClientProvider); final data = await api.getJson(RecipeApiPaths.aiSuggestions, token: token); if (!mounted) return; final raw = data as Map; final list = (raw['suggestions'] as List?) ?? []; setState(() { _suggestions = list.map((e) => _AiSuggestion.fromJson(e as Map)).toList(); _hasFetched = true; _isLoading = false; }); } catch (e) { if (!mounted) return; setState(() { _error = 'Kunde inte hämta förslag. Försök igen.'; _isLoading = false; }); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final cs = theme.colorScheme; return Scaffold( appBar: AppBar( title: const Text('AI-receptförslag'), leading: BackButton(onPressed: () => context.pop()), ), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Vad kan jag laga?', style: theme.textTheme.headlineSmall, ), const SizedBox(height: 8), Text( 'Baserat på vad du har i ditt lager och skafferi föreslår AI vad du kan laga.', style: theme.textTheme.bodyMedium ?.copyWith(color: cs.onSurfaceVariant), ), const SizedBox(height: 16), FilledButton.icon( onPressed: _isLoading ? null : _generateSuggestions, icon: _isLoading ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, color: cs.onPrimary, ), ) : const Icon(Icons.auto_awesome), label: Text(_isLoading ? 'Genererar förslag...' : 'Generera förslag'), ), if (_error != null) ...[ const SizedBox(height: 12), Text( _error!, style: theme.textTheme.bodySmall?.copyWith(color: cs.error), ), ], const SizedBox(height: 16), if (_hasFetched && _suggestions.isEmpty && !_isLoading) Center( child: Text( 'Inga förslag hittades. Lägg till fler varor i ditt lager.', style: theme.textTheme.bodyMedium ?.copyWith(color: cs.onSurfaceVariant), textAlign: TextAlign.center, ), ) else Expanded( child: ListView.separated( itemCount: _suggestions.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, index) { final s = _suggestions[index]; return _SuggestionCard( suggestion: s, onCreateRecipe: () => context.push( '/recipes/create', extra: {'markdown': '# ${s.name}\n\n${s.description}'}, ), ); }, ), ), ], ), ), ); } } class _SuggestionCard extends StatelessWidget { final _AiSuggestion suggestion; final VoidCallback onCreateRecipe; const _SuggestionCard({ required this.suggestion, required this.onCreateRecipe, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); final cs = theme.colorScheme; return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( suggestion.name, style: theme.textTheme.titleMedium ?.copyWith(fontWeight: FontWeight.bold), ), ), if (suggestion.estimatedTime.isNotEmpty) Chip( label: Text(suggestion.estimatedTime), avatar: const Icon(Icons.timer_outlined, size: 16), visualDensity: VisualDensity.compact, padding: EdgeInsets.zero, ), ], ), const SizedBox(height: 8), Text( suggestion.description, style: theme.textTheme.bodyMedium, ), if (suggestion.mainIngredients.isNotEmpty) ...[ const SizedBox(height: 12), Text( 'Ingredienser du har:', style: theme.textTheme.labelMedium ?.copyWith(color: cs.primary), ), const SizedBox(height: 4), Wrap( spacing: 6, runSpacing: 4, children: suggestion.mainIngredients .map((ing) => Chip( label: Text(ing), visualDensity: VisualDensity.compact, padding: EdgeInsets.zero, backgroundColor: cs.primaryContainer, labelStyle: TextStyle(color: cs.onPrimaryContainer), )) .toList(), ), ], if (suggestion.missingIngredients.isNotEmpty) ...[ const SizedBox(height: 8), Text( 'Kan behövas:', style: theme.textTheme.labelMedium ?.copyWith(color: cs.error), ), const SizedBox(height: 4), Wrap( spacing: 6, runSpacing: 4, children: suggestion.missingIngredients .map((ing) => Chip( label: Text(ing), visualDensity: VisualDensity.compact, padding: EdgeInsets.zero, backgroundColor: cs.errorContainer, labelStyle: TextStyle(color: cs.onErrorContainer), )) .toList(), ), ], const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: OutlinedButton.icon( onPressed: onCreateRecipe, icon: const Icon(Icons.add, size: 18), label: const Text('Skapa recept'), ), ), ], ), ), ); } }