import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../../features/records/records_service.dart'; /// Secure credentials and repository configuration for GitHub ride sync class GitHubAuthService { static const String _keyToken = 'github_pat_token_enc'; static const String _keyRepo = 'github_target_repo'; static const String _keyBranch = 'github_target_branch'; static const String defaultRepo = 'bponders12-cell/ride_logs'; static const String defaultBranch = 'main'; final SharedPreferences _prefs; final http.Client _client; GitHubAuthService(this._prefs, [http.Client? client]) : _client = client ?? http.Client(); /// Reads stored Personal Access Token String? getToken() { final raw = _prefs.getString(_keyToken); if (raw == null || raw.isEmpty) return null; try { return utf8.decode(base64.decode(raw)); } catch (_) { return raw; } } /// Stores Personal Access Token securely Future saveToken(String token) async { final encoded = base64.encode(utf8.encode(token.trim())); await _prefs.setString(_keyToken, encoded); } /// Clears stored token Future clearToken() async { await _prefs.remove(_keyToken); } /// Target GitHub repository (default: bponders12-cell/ride_logs) String getRepository() { return _prefs.getString(_keyRepo) ?? defaultRepo; } Future setRepository(String repo) async { await _prefs.setString(_keyRepo, repo.trim()); } /// Target branch (default: main) String getBranch() { return _prefs.getString(_keyBranch) ?? defaultBranch; } Future setBranch(String branch) async { await _prefs.setString(_keyBranch, branch.trim()); } /// Tests connectivity and permissions against GitHub API Future> testConnection() async { final token = getToken(); if (token == null || token.isEmpty) { return {'success': false, 'message': 'No GitHub token configured'}; } final repo = getRepository(); final url = Uri.parse('https://api.github.com/repos/$repo'); try { final response = await _client.get( url, headers: { 'Authorization': 'Bearer $token', 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'MotorSignal-Telemetry/1.0.2', }, ).timeout(const Duration(seconds: 10)); if (response.statusCode == 200) { final data = jsonDecode(response.body); return { 'success': true, 'message': 'Connected to ${data['full_name']} (${data['default_branch']})', 'canPush': data['permissions']?['push'] ?? true, }; } else if (response.statusCode == 401) { return {'success': false, 'message': 'Authentication failed (401: Invalid Token)'}; } else if (response.statusCode == 404) { return {'success': false, 'message': 'Repository not found or token lacks repo scope (404)'}; } else { return {'success': false, 'message': 'GitHub API error (${response.statusCode})'}; } } catch (e) { return {'success': false, 'message': 'Connection error: $e'}; } } } final githubAuthServiceProvider = Provider((ref) { final prefs = ref.watch(sharedPreferencesProvider); return GitHubAuthService(prefs); });