// filepath: lib/core/service/client/api_logger_service.dart import 'dart:convert'; import 'package:flutter/foundation.dart'; /// Holds the details of a single API call. class ApiLogEntry { final String id; final DateTime timestamp; final String method; final String url; final String endpoint; final String environment; final Map headers; final Map? requestBody; final dynamic responseBody; final int statusCode; final bool isSuccess; final Duration? duration; ApiLogEntry({ required this.id, required this.timestamp, required this.method, required this.url, required this.endpoint, this.environment = '', required this.headers, this.requestBody, this.responseBody, required this.statusCode, required this.isSuccess, this.duration, }); /// Derives a short environment label from a full URL string. static String envFromUrl(String url) { if (url.contains('vidamergeuat')) return 'VidaMerge'; if (url.contains('vidauat')) return 'VidaPlus'; if (url.contains('orash')) return 'Orash'; if (url.contains('uat.hmgwebservices')) return 'UAT'; if (url.contains('rc.hmg.com') && url.contains('/test')) return 'RC-Test'; if (url.contains('ms.hmg.com/rc')) return 'MS-RC'; if (url.contains('rc.hmg.com')) return 'RC'; if (url.contains('mdlaboratories.com/exacartapitest')) return 'Pharmacy-Test'; if (url.contains('mdlaboratories.com')) return 'Pharmacy'; if (url.contains('hmgwebservices.com')) return 'Production'; // External / other final uri = Uri.tryParse(url); return uri?.host.isNotEmpty == true ? uri!.host.split('.').first : 'External'; } /// Returns a pretty-printed JSON string of the request body. String get prettyRequestBody { if (requestBody == null) return '(none)'; try { return const JsonEncoder.withIndent(' ').convert(requestBody); } catch (_) { return requestBody.toString(); } } /// Returns a pretty-printed JSON string of the response body. String get prettyResponseBody { if (responseBody == null) return '(none)'; try { if (responseBody is String) { final decoded = json.decode(responseBody as String); return const JsonEncoder.withIndent(' ').convert(decoded); } return const JsonEncoder.withIndent(' ').convert(responseBody); } catch (_) { return responseBody.toString(); } } /// Returns formatted text report (headers + request + response sectioned). String toFormattedText() { final buf = StringBuffer(); buf.writeln('═══════════════════════════════════'); buf.writeln('[$method] $url'); buf.writeln('Status : $statusCode | ${isSuccess ? '✅ Success' : '❌ Failed'}'); if (environment.isNotEmpty) buf.writeln('Env : $environment'); buf.writeln('Time : ${timestamp.toIso8601String()}'); if (duration != null) buf.writeln('Duration: ${duration!.inMilliseconds}ms'); buf.writeln('───────────────── HEADERS ─────────────────'); headers.forEach((k, v) => buf.writeln('$k: $v')); buf.writeln('───────────────── REQUEST ─────────────────'); buf.writeln(prettyRequestBody); buf.writeln('──────────────── RESPONSE ─────────────────'); buf.writeln(prettyResponseBody); buf.writeln('═══════════════════════════════════'); return buf.toString(); } /// Returns a cURL command equivalent. String toCurl() { final buf = StringBuffer(); buf.write("curl -X $method \\\n"); buf.write(" '${url.trim()}' \\\n"); headers.forEach((k, v) { buf.write(" -H '$k: $v' \\\n"); }); if (requestBody != null && method != 'GET') { final bodyStr = json.encode(requestBody).replaceAll("'", "\\'"); buf.write(" -d '$bodyStr'"); } return buf.toString(); } } /// Singleton service that collects API call logs in-memory. class ApiLoggerService extends ChangeNotifier { ApiLoggerService._internal(); static final ApiLoggerService instance = ApiLoggerService._internal(); final List _logs = []; List get logs => List.unmodifiable(_logs.reversed.toList()); void addLog(ApiLogEntry entry) { _logs.add(entry); // Keep last 500 entries to avoid huge memory usage if (_logs.length > 500) _logs.removeAt(0); notifyListeners(); } void clear() { _logs.clear(); notifyListeners(); } }