// lib/remote/remote_client.dart import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; import 'remote_models.dart'; import 'auth_client.dart'; class RemoteJsonClient { final Uri indexUri; final RemoteAuth? auth; RemoteJsonClient( String baseUrl, String indexPath, { this.auth, }) : indexUri = Uri.parse( baseUrl.endsWith('/') ? baseUrl : '$baseUrl/', ).resolve(indexPath); // 🔥 PING SUPER LEGGERO → usa /ping Future ping() async { final client = HttpClient(); client.connectionTimeout = const Duration(seconds: 2); // Costruisce l’URL /ping a partire dalla baseUrl final pingUri = Uri.parse(indexUri.origin + '/ping'); try { final req = await client.getUrl(pingUri); req.followRedirects = false; final res = await req.close().timeout(const Duration(seconds: 2)); print('[remote-client] PING url=$pingUri status=${res.statusCode}'); // 5xx → errore server if (res.statusCode >= 500) { throw HttpException('HTTP ${res.statusCode}', uri: pingUri); } // 200, 401, 403, 404 → server raggiungibile } on TimeoutException { throw TimeoutException('Ping timeout'); } on SocketException catch (e) { throw SocketException('Network error: ${e.message}'); } on HttpException { rethrow; } catch (e) { rethrow; } finally { client.close(); } } // FETCH ALL (come prima) Future> fetchAll() async { Map headers = {}; if (auth != null) { headers = await auth!.authHeaders(); } print('[remote-client] GET $indexUri'); http.Response res; try { res = await http .get(indexUri, headers: headers) .timeout(const Duration(seconds: 20)); } catch (e) { throw Exception('Errore rete su $indexUri: $e'); } if (res.statusCode == 401 && auth != null) { headers = await auth!.refreshAndHeaders(); res = await http .get(indexUri, headers: headers) .timeout(const Duration(seconds: 20)); } if ({301, 302, 307, 308}.contains(res.statusCode) && res.headers['location'] != null) { final loc = res.headers['location']!; final redirectUri = indexUri.resolve(loc); res = await http .get(redirectUri, headers: headers) .timeout(const Duration(seconds: 20)); } if (res.statusCode != 200) { final snippet = utf8.decode(res.bodyBytes.take(200).toList()); throw HttpException( 'HTTP ${res.statusCode} ${res.reasonPhrase}. Body: $snippet', uri: indexUri, ); } final body = utf8.decode(res.bodyBytes); final dynamic decoded = json.decode(body); if (decoded is! List) { throw Exception( 'JSON inatteso: atteso array top-level, ricevuto ${decoded.runtimeType}', ); } final rawList = decoded as List; return rawList.map((e) { if (e is! Map) { throw Exception('Elemento JSON non è una mappa: ${e.runtimeType} -> $e'); } return RemotePhotoItem.fromJson(e); }).toList(); } }