import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../config/app_config.dart'; import '../auth/auth_provider.dart'; /// Client HTTP Dio configuré pour l'API Laverie. class ApiClient { ApiClient({ required this.baseUrl, this.getAccessToken, this.onRefreshToken, this.onSessionExpired, }) : _dio = Dio( BaseOptions( baseUrl: baseUrl, connectTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 30), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, ), ) { _dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) async { final token = await getAccessToken?.call(); if (token != null && token.isNotEmpty) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); }, onError: (error, handler) async { final response = error.response; final alreadyRetried = error.requestOptions.extra['auth_retried'] == true; if (response?.statusCode == 401 && !alreadyRetried && onRefreshToken != null) { final newToken = await onRefreshToken!(); if (newToken != null && newToken.isNotEmpty) { final request = error.requestOptions; request.extra['auth_retried'] = true; request.headers['Authorization'] = 'Bearer $newToken'; try { final retryResponse = await _dio.fetch(request); return handler.resolve(retryResponse); } on DioException catch (retryError) { return handler.next(retryError); } } await onSessionExpired?.call(); } handler.next(error); }, ), ); } final String baseUrl; final Future Function()? getAccessToken; final Future Function()? onRefreshToken; final Future Function()? onSessionExpired; final Dio _dio; Dio get dio => _dio; Future> get( String path, { Map? queryParameters, }) { return _dio.get(path, queryParameters: queryParameters); } Future> post( String path, { dynamic data, Map? queryParameters, }) { return _dio.post(path, data: data, queryParameters: queryParameters); } Future> patch( String path, { dynamic data, Map? queryParameters, }) { return _dio.patch(path, data: data, queryParameters: queryParameters); } Future> put( String path, { dynamic data, Map? queryParameters, }) { return _dio.put(path, data: data, queryParameters: queryParameters); } Future> delete( String path, { dynamic data, Map? queryParameters, }) { return _dio.delete(path, data: data, queryParameters: queryParameters); } } /// Fournisseur Riverpod du client API (injecte le token depuis l'état auth). final apiClientProvider = Provider((ref) { final authNotifier = ref.read(authProvider.notifier); return ApiClient( baseUrl: kApiBaseUrl, getAccessToken: () async => ref.read(authProvider).accessToken, onRefreshToken: () => authNotifier.refreshAccessToken(), onSessionExpired: () => authNotifier.sessionExpired(), ); });