import 'package:flutter/foundation.dart'; import 'control_plane_status_client.dart'; class ControlPlaneStatusController extends ChangeNotifier { final ControlPlaneStatusRepository statusRepository; List _edges = []; bool _loadingEdges = false; String? _edgesError; String? _selectedEdgeId; final Map _edgeStatuses = {}; final Map _loadingStatuses = {}; final Map _statusErrors = {}; final Map> _edgeEvents = {}; final Map _loadingEvents = {}; final Map _eventErrors = {}; bool _disposed = false; ControlPlaneStatusController({required this.statusRepository}); // Getters List get edges => _edges; bool get loadingEdges => _loadingEdges; String? get edgesError => _edgesError; String? get selectedEdgeId => _selectedEdgeId; Map get edgeStatuses => _edgeStatuses; Map get loadingStatuses => _loadingStatuses; Map get statusErrors => _statusErrors; Map> get edgeEvents => _edgeEvents; Map get loadingEvents => _loadingEvents; Map get eventErrors => _eventErrors; @override void dispose() { _disposed = true; super.dispose(); } void _notifyIfActive() { if (!_disposed) { notifyListeners(); } } void selectEdge(String edgeId) { if (_disposed) return; if (_selectedEdgeId == edgeId) return; _selectedEdgeId = edgeId; _notifyIfActive(); fetchEdgeDetails(edgeId); } Future fetchEdges() async { if (_disposed) return; _loadingEdges = true; _edgesError = null; _notifyIfActive(); try { final fleetResponse = await statusRepository.fetchFleetStatus(); if (_disposed) return; _edges = fleetResponse.edges; _loadingEdges = false; // Normalize selection: check if the previously selected edge is still present final hasSelected = _edges.any((e) => e.edgeId == _selectedEdgeId); if (!hasSelected) { // Clear stale status/events/loading/error cache to prevent leak/inconsistency if (_selectedEdgeId != null) { _edgeStatuses.remove(_selectedEdgeId); _loadingStatuses.remove(_selectedEdgeId); _statusErrors.remove(_selectedEdgeId); _edgeEvents.remove(_selectedEdgeId); _loadingEvents.remove(_selectedEdgeId); _eventErrors.remove(_selectedEdgeId); } if (_edges.isNotEmpty) { _selectedEdgeId = _edges.first.edgeId; } else { _selectedEdgeId = null; } } _notifyIfActive(); if (_selectedEdgeId != null) { await fetchEdgeDetails(_selectedEdgeId!); } } catch (e) { if (_disposed) return; _edgesError = e.toString(); _loadingEdges = false; _notifyIfActive(); } } Future fetchEdgeDetails(String edgeId) async { if (_disposed) return; await Future.wait([fetchEdgeStatus(edgeId), fetchEdgeEvents(edgeId)]); if (_disposed) return; } Future fetchEdgeStatus(String edgeId) async { if (_disposed) return; _loadingStatuses[edgeId] = true; _statusErrors[edgeId] = null; _notifyIfActive(); try { final status = await statusRepository.fetchEdgeStatus(edgeId); if (_disposed) return; _edgeStatuses[edgeId] = status; _loadingStatuses[edgeId] = false; _notifyIfActive(); } catch (e) { if (_disposed) return; _statusErrors[edgeId] = e.toString(); _loadingStatuses[edgeId] = false; _notifyIfActive(); } } Future fetchEdgeEvents(String edgeId) async { if (_disposed) return; _loadingEvents[edgeId] = true; _eventErrors[edgeId] = null; _notifyIfActive(); try { final events = await statusRepository.fetchEdgeEvents(edgeId); if (_disposed) return; _edgeEvents[edgeId] = events; _loadingEvents[edgeId] = false; _notifyIfActive(); } catch (e) { if (_disposed) return; _eventErrors[edgeId] = e.toString(); _loadingEvents[edgeId] = false; _notifyIfActive(); } } Future refreshSelectedStatus() async { if (_disposed) return; if (_selectedEdgeId != null) { await fetchEdgeStatus(_selectedEdgeId!); } } Future refreshSelectedEvents() async { if (_disposed) return; if (_selectedEdgeId != null) { await fetchEdgeEvents(_selectedEdgeId!); } } }