import 'package:dio/dio.dart'; import '../config/api_config.dart'; import '../models/models.dart'; class ApiException implements Exception { ApiException(this.message, {this.statusCode}); final String message; final int? statusCode; @override String toString() => message; } class ApiClient { ApiClient() { dio = Dio( BaseOptions( baseUrl: resolveApiBaseUrl(), connectTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 15), headers: const { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ), ); dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { if (token != null && token!.isNotEmpty) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); }, ), ); } late final Dio dio; String? token; Future<({String token, AppUser user})> staffLogin(String phone, String password) { return _send( dio.post('/auth/staff-login', data: {'phone': phone, 'password': password}), (data) { final map = data as Map; return ( token: map['token'] as String, user: AppUser.fromJson(map['user'] as Map), ); }, ); } Future<({String requestId, String secret})> dealerLogin(String phone) { return _send(dio.post('/auth/dealer-login', data: {'phone': phone}), (data) { final map = data as Map; return (requestId: map['requestId'] as String, secret: map['secret'] as String); }); } Future dealerLoginStatus(String requestId, String secret) { return _send( dio.get('/auth/dealer-login/$requestId', queryParameters: {'secret': secret}), (data) => DealerLoginStatus.fromJson(data as Map), ); } Future> loginRequests() { return _send(dio.get('/auth/login-requests'), (data) => _list(data, LoginRequestItem.fromJson)); } Future approveLogin(String id) { return _send(dio.post('/auth/login-requests/$id/approve'), (_) {}); } Future rejectLogin(String id) { return _send(dio.post('/auth/login-requests/$id/reject'), (_) {}); } Future me() { return _send( dio.get('/auth/me'), (data) => AppUser.fromJson(data as Map), ); } Future> dealers() { return _send(dio.get('/dealers'), (data) => _list(data, Dealer.fromJson)); } Future> stocks({String? dealerId}) { return _send( dio.get( '/stocks', queryParameters: { if (dealerId != null && dealerId.isNotEmpty) 'dealerId': dealerId, }, ), (data) => _list(data, StockItem.fromJson), ); } Future createStock(Map body) { return _send( dio.post('/stocks', data: body), (data) => StockItem.fromJson((data as Map)['data'] as Map), ); } Future updateStage(String chassisNumber, Map body) { return _send( dio.patch('/stocks/$chassisNumber/stage', data: body), (data) => StockItem.fromJson((data as Map)['data'] as Map), ); } Future transferStock(String chassisNumber, String dealerId) { return _send( dio.post('/stocks/$chassisNumber/transfer', data: {'dealerId': dealerId}), (data) => StockItem.fromJson((data as Map)['data'] as Map), ); } Future> events({String? dealerId, String? planType}) { return _send( dio.get( '/events', queryParameters: { if (dealerId != null && dealerId.isNotEmpty) 'dealerId': dealerId, if (planType != null && planType.isNotEmpty) 'planType': planType, }, ), (data) => _list(data, EventPlan.fromJson), ); } Future createEvent(Map body) { return _send( dio.post('/events', data: body), (data) => EventPlan.fromJson((data as Map)['data'] as Map), ); } Future> servicePoints({String? district, String? search}) { return _send( dio.get( '/service-points', queryParameters: { if (district != null && district.isNotEmpty) 'district': district, if (search != null && search.isNotEmpty) 'search': search, }, ), (data) => _list(data, ServicePoint.fromJson), ); } Future createServicePoint(Map body) { return _send( dio.post('/service-points', data: body), (data) => ServicePoint.fromJson((data as Map)['data'] as Map), ); } Future createDealer(Map body) { return _send( dio.post('/admin/dealers', data: body), (data) => Dealer.fromJson((data as Map)['data'] as Map), ); } Future deleteDealer(String id) { return _send(dio.delete('/admin/dealers/$id'), (_) {}); } Future> adminDealers() { return _send(dio.get('/admin/dealers'), (data) => _list(data, Dealer.fromJson)); } Future> salesReps() { return _send(dio.get('/admin/sales-reps'), (data) => _list(data, AppUser.fromJson)); } Future createSalesRep(Map body) { return _send( dio.post('/admin/sales-reps', data: body), (data) => AppUser.fromJson((data as Map)['data'] as Map), ); } Future assignDealer({ required String salesRepId, required String dealerId, required bool assigned, }) { return _send( dio.patch( '/admin/assign-dealer', data: { 'salesRepId': salesRepId, 'dealerId': dealerId, 'assigned': assigned, }, ), (data) => AppUser.fromJson((data as Map)['data'] as Map), ); } Future _send(Future> request, T Function(dynamic data) parse) async { try { final response = await request; return parse(response.data); } on DioException catch (error) { final data = error.response?.data; final message = data is Map && data['message'] is String ? data['message'] as String : 'Network request failed'; throw ApiException(message, statusCode: error.response?.statusCode); } } List _list(dynamic data, T Function(Map) parse) { final raw = (data as Map)['data'] as List; return raw.map((item) => parse(item as Map)).toList(); } }