import 'dart:async'; import 'dart:convert'; import 'package:signalr_core/signalr_core.dart'; import 'package:finlytic_app/core/network/api_client.dart'; import 'package:finlytic_app/features/news/models/news_article_model.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class NewsRepository { final ApiClient apiClient; final String backendUrl; final FlutterSecureStorage secureStorage = const FlutterSecureStorage(); HubConnection? _hubConnection; final _liveNewsController = StreamController.broadcast(); Stream get liveNewsStream => _liveNewsController.stream; NewsRepository({required this.apiClient, required this.backendUrl}); Future> fetchNews({ int page = 1, int pageSize = 20, String? symbol, String? isin, String? date, }) async { try { final Map queryParams = { 'page': page, 'pageSize': pageSize, }; if (symbol != null && symbol.isNotEmpty) queryParams['symbol'] = symbol; if (isin != null && isin.isNotEmpty) queryParams['isin'] = isin; if (date != null && date.isNotEmpty) queryParams['date'] = date; final response = await apiClient.get('/api/v1/news', queryParameters: queryParams); if (response.statusCode == 200) { final List data = response.data; return data.map((json) => NewsArticleModel.fromJson(json)).toList(); } return []; } catch (e) { print('Error fetching news: $e'); throw Exception('Failed to load news'); } } Future connectToLiveFeed() async { if (_hubConnection != null && _hubConnection!.state == HubConnectionState.connected) { return; } try { final token = await secureStorage.read(key: 'auth_token'); final hubUrl = '$backendUrl/hubs/news${token != null ? '?access_token=$token' : ''}'; _hubConnection = HubConnectionBuilder() .withUrl(hubUrl, HttpConnectionOptions( logging: (level, message) => print(message), )) .withAutomaticReconnect() .build(); _hubConnection!.on('ReceiveNewArticle', (arguments) { if (arguments != null && arguments.isNotEmpty) { try { // SignalR might send it as a Map or raw JSON depending on .NET format final dynamic rawPayload = arguments[0]; final Map jsonData = rawPayload is String ? jsonDecode(rawPayload) : Map.from(rawPayload); final article = NewsArticleModel.fromJson(jsonData); _liveNewsController.add(article); } catch (e) { print('Error parsing live article: $e'); } } }); await _hubConnection!.start(); print('Connected to News Live Feed'); } catch (e) { print('Error connecting to News Live Feed: $e'); } } Future disconnectFromLiveFeed() async { if (_hubConnection != null) { await _hubConnection!.stop(); _hubConnection = null; } } void dispose() { _liveNewsController.close(); disconnectFromLiveFeed(); } }