import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:real_estate_mobile/core/constants/api_constants.dart'; import 'package:real_estate_mobile/core/network/api_client.dart'; import 'package:real_estate_mobile/core/network/api_exceptions.dart'; import 'package:real_estate_mobile/features/agents/data/models/agent_profile.dart'; import 'package:real_estate_mobile/features/agents/data/models/agent_type.dart'; import 'package:real_estate_mobile/features/agents/data/models/filterable_field.dart'; class SearchAgentsResponse { final List data; final int total; final int page; final int limit; final int totalPages; const SearchAgentsResponse({ required this.data, required this.total, required this.page, required this.limit, required this.totalPages, }); /// Parses: { data: [...], meta: { total, page, limit, totalPages } } factory SearchAgentsResponse.fromJson(Map json) { final meta = json['meta'] as Map? ?? {}; return SearchAgentsResponse( data: (json['data'] as List?) ?.map( (e) => AgentProfile.fromJson(e as Map)) .toList() ?? [], total: meta['total'] as int? ?? 0, page: meta['page'] as int? ?? 1, limit: meta['limit'] as int? ?? 10, totalPages: meta['totalPages'] as int? ?? 1, ); } } class AgentsRepository { final Dio _dio = ApiClient.instance.dio; /// Search agents: GET /agents /// Response: { success, data: { data: [...], meta: {...} } } Future searchAgents({ String? agentTypeId, String? search, int page = 1, int limit = 10, String? sortBy, String? sortOrder, Map>? filters, }) async { try { final queryParams = { 'page': page, 'limit': limit, }; if (agentTypeId != null) queryParams['agentTypeId'] = agentTypeId; if (search != null && search.isNotEmpty) queryParams['search'] = search; if (sortBy != null) queryParams['sortBy'] = sortBy; if (sortOrder != null) queryParams['sortOrder'] = sortOrder; if (filters != null && filters.isNotEmpty) { queryParams['filters'] = jsonEncode(filters); } final response = await _dio.get( ApiConstants.agents, queryParameters: queryParams, ); // Backend wraps: { success: true, data: { data: [...], meta: {...} } } final responseData = response.data; final payload = responseData['data'] as Map; return SearchAgentsResponse.fromJson(payload); } on DioException catch (e) { if (e.error is ApiException) { throw e.error as ApiException; } throw ApiException( message: e.message ?? 'Failed to fetch agents', statusCode: e.response?.statusCode, ); } } /// Get filterable profile fields: GET /profile-fields/filterable /// When `agentTypeId` is provided, backend scopes filters to that agent type + globals. /// Response: { success, data: [...] } Future> getFilterableFields({String? agentTypeId}) async { try { final response = await _dio.get( ApiConstants.filterableFields, queryParameters: agentTypeId != null && agentTypeId.isNotEmpty ? {'agentTypeId': agentTypeId} : null, ); final data = response.data['data'] as List; return data .map((e) => FilterableField.fromJson(e as Map)) .toList(); } on DioException catch (e) { if (e.error is ApiException) { throw e.error as ApiException; } throw ApiException( message: e.message ?? 'Failed to fetch filterable fields', statusCode: e.response?.statusCode, ); } } /// Get agent by ID: GET /agents/{id} /// Response: { success, data: AgentProfile } Future getAgentById(String id) async { try { final response = await _dio.get('${ApiConstants.agents}/$id'); final data = response.data['data'] as Map; return AgentProfile.fromJson(data); } on DioException catch (e) { if (e.error is ApiException) throw e.error as ApiException; throw ApiException( message: e.message ?? 'Failed to fetch agent', statusCode: e.response?.statusCode, ); } } /// Get field values for agent: GET /agents/{id}/field-values /// Response: { success, data: { agentProfileId, fieldValues: [...] } } Future> getFieldValues(String agentId) async { try { final response = await _dio.get('${ApiConstants.agents}/$agentId/field-values'); final data = response.data['data'] as Map; final fieldValues = data['fieldValues'] as List? ?? []; return fieldValues .map((e) => AgentFieldValue.fromFieldValueResponse( e as Map)) .toList(); } on DioException catch (e) { if (e.error is ApiException) throw e.error as ApiException; throw ApiException( message: e.message ?? 'Failed to fetch field values', statusCode: e.response?.statusCode, ); } } /// Get testimonials for agent: GET /testimonials/agent/{agentProfileId} Future>> getTestimonials(String agentId, {int limit = 10}) async { try { final response = await _dio.get( '${ApiConstants.testimonials}/agent/$agentId', queryParameters: {'limit': limit}, ); final data = response.data['data'] as List? ?? []; return data.cast>(); } on DioException catch (_) { return []; } } /// Get connection status: GET /connection-requests/status/{agentProfileId} Future?> getConnectionStatus(String agentId) async { try { final response = await _dio .get('${ApiConstants.connectionRequests}/status/$agentId'); return response.data['data'] as Map?; } on DioException catch (_) { return null; } } /// Create connection request: POST /connection-requests Future createConnectionRequest(String agentId, {String? message}) async { try { await _dio.post(ApiConstants.connectionRequests, data: { 'agentProfileId': agentId, if (message != null && message.isNotEmpty) 'message': message, }); return true; } on DioException catch (_) { return false; } } /// Cancel connection request: DELETE /connection-requests/{requestId} Future cancelConnectionRequest(String requestId) async { try { await _dio.delete('${ApiConstants.connectionRequests}/$requestId'); return true; } on DioException catch (_) { return false; } } /// Get received connection requests: GET /connection-requests/received?status=... Future>> getReceivedRequests(String status) async { try { final response = await _dio.get( '${ApiConstants.connectionRequests}/received', queryParameters: {'status': status}, ); final data = response.data['data'] as List? ?? []; return data.cast>(); } on DioException catch (_) { return []; } } /// Respond to connection request: PATCH /connection-requests/{id}/respond Future respondToRequest(String requestId, String status) async { try { await _dio.patch( '${ApiConstants.connectionRequests}/$requestId/respond', data: {'status': status}, ); return true; } on DioException catch (_) { return false; } } /// Get agent types: GET /agent-types /// Response: { success, data: [...] } Future> getAgentTypes() async { try { final response = await _dio.get(ApiConstants.agentTypes); // Backend wraps: { success: true, data: [...] } final data = response.data['data'] as List; return data .map((e) => AgentType.fromJson(e as Map)) .toList(); } on DioException catch (e) { if (e.error is ApiException) { throw e.error as ApiException; } throw ApiException( message: e.message ?? 'Failed to fetch agent types', statusCode: e.response?.statusCode, ); } } }