import 'package:finlytic_app/core/widgets/status_badge.dart'; import 'package:finlytic_app/features/favorites/cubit/favorites_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/network/api_client.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/utils/time_utils.dart'; import '../../../core/widgets/glass_container.dart'; import '../../asset_detail/views/asset_detail_screen.dart'; import '../models/news_article_model.dart'; import 'article_sentiment_dialog.dart'; /// News Card Item displaying news info, tagged assets, timestamp, and sentiment badge (wie auf dem Dashboard). class NewsCardItem extends StatelessWidget { final dynamic item; final ApiClient apiClient; final ValueChanged? onArticleUpdated; const NewsCardItem({ super.key, required this.item, required this.apiClient, this.onArticleUpdated, }); @override Widget build(BuildContext context) { final Map article = item is NewsArticleModel ? (item as NewsArticleModel).toJson() : (item is Map && item.containsKey('article') && item['article'] is Map ? Map.from(item['article']) : (item is Map ? Map.from(item) : {})); final Map? sentimentObj = (item is Map && item.containsKey('sentiment') && item['sentiment'] is Map) ? Map.from(item['sentiment']) : null; final title = article['title']?.toString() ?? article['Title']?.toString() ?? 'Nachrichtenartikel'; final summary = article['summary']?.toString() ?? article['Summary']?.toString() ?? article['contentRaw']?.toString() ?? ''; final author = article['author']?.toString() ?? article['Author']?.toString() ?? article['source']?.toString() ?? 'Finlytic News'; final rawPubTime = article['publishedAt']?.toString() ?? article['PublishedAt']?.toString() ?? article['scrapedAt']?.toString(); final formattedTime = TimeUtils.formatRelativeTime(rawPubTime); final pubTime = formattedTime.isNotEmpty ? formattedTime : 'Heute'; final rawStatus = article['status']?.toString() ?? article['Status']?.toString() ?? 'Completed'; final String? sentimentLabel = sentimentObj?['label']?.toString() ?? article['sentiment']?.toString(); final double? score = ((sentimentObj?['compoundScore'] ?? sentimentObj?['compound_score'] ?? article['sentimentScore']) as num?)?.toDouble(); final Widget? badgeWidget = (sentimentLabel != null && sentimentLabel.trim().isNotEmpty) ? StatusBadge.sentiment(sentimentLabel, score: score) : null; final matchedAssetsRaw = article['MatchedAssets'] ?? article['matchedAssets']; final matchedAssets = matchedAssetsRaw is List ? matchedAssetsRaw : []; final favourites = context.read().state.favoriteDetails; return GlassContainer( margin: const EdgeInsets.only(bottom: 12), onTap: () { final NewsArticleModel articleModel; if (item is NewsArticleModel) { articleModel = item; } else if (item is Map) { articleModel = NewsArticleModel.fromJson(item); } else if (item is Map) { articleModel = NewsArticleModel.fromJson(Map.from(item)); } else { articleModel = NewsArticleModel( id: '', title: title, author: author, summary: summary, contentRaw: '', sourceUrl: '', scrapedAt: DateTime.now(), publishedAt: DateTime.now(), status: rawStatus, sentiment: sentimentLabel ?? 'NEUTRAL', sentimentScore: score ?? 0.0, confidence: 0.0, ); } showDialog( context: context, builder: (_) => ArticleSentimentDialog( articleData: articleModel, apiClient: apiClient, onArticleUpdated: onArticleUpdated, ), ); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14.5), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), if (badgeWidget != null) ...[ const SizedBox(width: 8), badgeWidget, ], ], ), if (summary.isNotEmpty) ...[ const SizedBox(height: 8), Text( summary, maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle(color: AppTheme.textSecondary, fontSize: 12.5), ), ], const SizedBox(height: 10), Text('$author • $pubTime', style: TextStyle(color: AppTheme.textMuted, fontSize: 11)), if (matchedAssets.isNotEmpty) ...[ const SizedBox(height: 8), Wrap( spacing: 6, runSpacing: 4, children: matchedAssets.map((assetItem) { final String isin = (assetItem is Map ? (assetItem['isin'] ?? assetItem['Isin']) : assetItem)?.toString() ?? ''; final String name = (assetItem is Map ? (assetItem['name'] ?? assetItem['Name'] ?? assetItem['symbol'] ?? isin) : isin)?.toString() ?? isin; final match = favourites.where((e) => e.isin == isin); final symbol = match.isEmpty ? null : match.first; return ActionChip( avatar: Icon(Icons.show_chart, size: 14, color: AppTheme.accentCyan), label: Text(name, style: TextStyle(fontSize: 11, color: AppTheme.accentCyan, fontWeight: FontWeight.bold)), backgroundColor: AppTheme.glassSurface, side: BorderSide(color: AppTheme.glassBorder), padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onPressed: () { if (isin.isNotEmpty) { Navigator.push( context, MaterialPageRoute( builder: (_) => AssetDetailScreen( isin: isin, name: name, symbol: symbol != null ? symbol.symbol : null, apiClient: apiClient, ), ), ); } }, ); }).toList(), ), ], ], ), ); } }