import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../models/device.dart'; import '../models/user_info.dart'; import '../models/static_talkgroup.dart'; import '../models/device_profile.dart'; import 'brandmeister_client.dart'; class AuthenticationManager extends ChangeNotifier { static const String _tokenKey = 'apiToken'; late final FlutterSecureStorage _storage; AuthenticationManager() { _initStorage(); _loadToken(); } void _initStorage() { _storage = const FlutterSecureStorage( aOptions: AndroidOptions( encryptedSharedPreferences: true, ), iOptions: IOSOptions( accessibility: KeychainAccessibility.first_unlock, ), webOptions: WebOptions( dbName: 'bmmanager', publicKey: 'bmmanager_public_key', ), ); } bool _isAuthenticated = false; bool _isInitializing = true; String _apiToken = ''; UserInfo? _userInfo; BrandmeisterClient? _client; bool get isAuthenticated => _isAuthenticated; bool get isInitializing => _isInitializing; String get apiToken => _apiToken; UserInfo? get userInfo => _userInfo; Future _loadToken() async { try { final token = await _storage.read(key: _tokenKey); if (token != null && token.isNotEmpty) { _apiToken = token; _client = BrandmeisterClient(apiToken: token); // Try to verify the token is still valid try { _userInfo = await _client!.whoami(); _isAuthenticated = true; } catch (e) { // Token is invalid, clear it await logout(); } } } catch (e) { debugPrint('Error loading token: $e'); } finally { _isInitializing = false; notifyListeners(); } } Future _saveToken(String token) async { try { await _storage.write(key: _tokenKey, value: token); _apiToken = token; _client = BrandmeisterClient(apiToken: token); } catch (e) { throw BrandmeisterError('Failed to save token: $e'); } } Future verifyAndSaveToken(String token) async { if (token.isEmpty) { throw BrandmeisterError('Token cannot be empty'); } final tempClient = BrandmeisterClient(apiToken: token); final user = await tempClient.whoami(); await _saveToken(token); _userInfo = user; _isAuthenticated = true; notifyListeners(); return user; } Future logout() async { try { await _storage.delete(key: _tokenKey); _apiToken = ''; _userInfo = null; _isAuthenticated = false; _client = null; notifyListeners(); } catch (e) { debugPrint('Error during logout: $e'); } } Future refreshUserInfo() async { if (_client == null) return; try { _userInfo = await _client!.whoami(); notifyListeners(); } catch (e) { debugPrint('Error refreshing user info: $e'); rethrow; } } // Device Operations Future> getDevices() async { if (_client == null || _userInfo == null) { throw BrandmeisterError('Not authenticated'); } if (_userInfo!.username == null) { throw BrandmeisterError('Username not available'); } return await _client!.getDevicesByCallsign(_userInfo!.username!); } Future getDevice(int dmrId) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } return await _client!.getDevice(dmrId); } Future getDeviceProfile(int dmrId) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } return await _client!.getDeviceProfile(dmrId); } Future> getTalkgroups(int dmrId) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } return await _client!.getDeviceTalkgroups(dmrId); } Future> getAllTalkgroups() async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } return await _client!.getTalkgroups(); } Future linkTalkgroup({ required int talkgroupId, required int dmrId, required int timeslot, }) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } await _client!.addTalkgroup( dmrId: dmrId, talkgroupId: talkgroupId, slot: timeslot, ); } Future unlinkTalkgroup({ required String talkgroupId, required int dmrId, required String timeslot, }) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } final tgId = int.tryParse(talkgroupId); final slot = int.tryParse(timeslot); if (tgId == null || slot == null) { throw BrandmeisterError('Invalid talkgroup ID or timeslot'); } await _client!.removeTalkgroup( dmrId: dmrId, talkgroupId: tgId, slot: slot, ); } Future dropAutoStaticGroup(int dmrId) async { if (_client == null) { throw BrandmeisterError('Not authenticated'); } await _client!.dropAutoStaticGroup(dmrId); } }