import 'package:dio/dio.dart'; import '../../../../core/constants/api_constants.dart'; import '../../../../core/errors/exceptions.dart'; import '../../../../core/network/dio_client.dart'; import '../models/auth_response_model.dart'; import '../models/login_dto.dart'; import '../models/register_dto.dart'; import '../models/user_model.dart'; /// Remote data source for authentication operations abstract class AuthRemoteDataSource { /// Login user with email and password Future login(LoginDto loginDto); /// Register new user Future register(RegisterDto registerDto); /// Get current user profile Future getProfile(); /// Refresh access token using refresh token Future refreshToken(String refreshToken); } /// Implementation of AuthRemoteDataSource class AuthRemoteDataSourceImpl implements AuthRemoteDataSource { final DioClient dioClient; AuthRemoteDataSourceImpl({required this.dioClient}); @override Future login(LoginDto loginDto) async { try { print('📡 DataSource: Calling login API...'); final response = await dioClient.post( ApiConstants.login, data: loginDto.toJson(), ); print('📡 DataSource: Status=${response.statusCode}'); print('📡 DataSource: Response data keys=${response.data.keys.toList()}'); if (response.statusCode == ApiConstants.statusOk) { // API returns nested structure: {success, data: {access_token, user}, message} // Extract the 'data' object final responseData = response.data['data'] as Map; print('📡 DataSource: Extracted data object with keys=${responseData.keys.toList()}'); final authResponseModel = AuthResponseModel.fromJson(responseData); print('📡 DataSource: Parsed successfully, token length=${authResponseModel.accessToken.length}'); return authResponseModel; } else { throw ServerException('Login failed with status: ${response.statusCode}'); } } on DioException catch (e) { print('❌ DataSource: DioException - ${e.message}'); throw _handleDioError(e); } catch (e, stackTrace) { print('❌ DataSource: Unexpected error - $e'); print('Stack trace: $stackTrace'); throw ServerException('Unexpected error during login: $e'); } } @override Future register(RegisterDto registerDto) async { try { final response = await dioClient.post( ApiConstants.register, data: registerDto.toJson(), ); if (response.statusCode == ApiConstants.statusCreated || response.statusCode == ApiConstants.statusOk) { // API returns nested structure: {success, data: {access_token, user}, message} // Extract the 'data' object final responseData = response.data['data'] as Map; return AuthResponseModel.fromJson(responseData); } else { throw ServerException('Registration failed with status: ${response.statusCode}'); } } on DioException catch (e) { throw _handleDioError(e); } catch (e) { throw ServerException('Unexpected error during registration: $e'); } } @override Future getProfile() async { try { print('📡 DataSource: Calling getProfile API...'); final response = await dioClient.get(ApiConstants.profile); print('📡 DataSource: Profile status=${response.statusCode}'); print('📡 DataSource: Profile response keys=${response.data?.keys?.toList()}'); print('📡 DataSource: Profile response=$response.data}'); if (response.statusCode == ApiConstants.statusOk) { // API returns nested structure: {success, data: user, message} // Extract the 'data' object final userData = response.data['data'] as Map; print('📡 DataSource: Extracted user data with keys=${userData.keys.toList()}'); final userModel = UserModel.fromJson(userData); print('📡 DataSource: User parsed successfully: ${userModel.name}'); return userModel; } else { throw ServerException('Get profile failed with status: ${response.statusCode}'); } } on DioException catch (e) { print('❌ DataSource: Profile DioException - ${e.message}'); throw _handleDioError(e); } catch (e, stackTrace) { print('❌ DataSource: Profile unexpected error - $e'); print('Stack trace: $stackTrace'); throw ServerException('Unexpected error getting profile: $e'); } } @override Future refreshToken(String refreshToken) async { try { print('📡 DataSource: Calling refresh token API...'); final response = await dioClient.post( ApiConstants.refreshToken, data: {'refreshToken': refreshToken}, ); if (response.statusCode == ApiConstants.statusOk) { // API returns nested structure: {success, data: {access_token, refresh_token, user}, message} // Extract the 'data' object final responseData = response.data['data'] as Map; print('📡 DataSource: Token refreshed successfully'); return AuthResponseModel.fromJson(responseData); } else { throw ServerException('Token refresh failed with status: ${response.statusCode}'); } } on DioException catch (e) { print('❌ DataSource: Refresh token failed - ${e.message}'); throw _handleDioError(e); } catch (e) { print('❌ DataSource: Unexpected error refreshing token: $e'); throw ServerException('Unexpected error refreshing token: $e'); } } /// Handle Dio errors and convert to custom exceptions Exception _handleDioError(DioException error) { switch (error.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return NetworkException('Connection timeout. Please check your internet connection.'); case DioExceptionType.badResponse: final statusCode = error.response?.statusCode; final message = error.response?.data?['message'] ?? error.message; switch (statusCode) { case ApiConstants.statusUnauthorized: return InvalidCredentialsException(message ?? 'Invalid email or password'); case ApiConstants.statusForbidden: return UnauthorizedException(message ?? 'Access forbidden'); case ApiConstants.statusNotFound: return NotFoundException(message ?? 'Resource not found'); case ApiConstants.statusUnprocessableEntity: return ValidationException(message ?? 'Validation failed'); case 409: // Conflict return ConflictException(message ?? 'Email already exists'); case ApiConstants.statusTooManyRequests: return ServerException('Too many requests. Please try again later.'); case ApiConstants.statusInternalServerError: case ApiConstants.statusBadGateway: case ApiConstants.statusServiceUnavailable: case ApiConstants.statusGatewayTimeout: return ServerException(message ?? 'Server error. Please try again later.'); default: return ServerException(message ?? 'Unknown error occurred'); } case DioExceptionType.connectionError: return NetworkException('No internet connection. Please check your network.'); case DioExceptionType.badCertificate: return NetworkException('SSL certificate error'); case DioExceptionType.cancel: return NetworkException('Request was cancelled'); default: return ServerException(error.message ?? 'Unknown error occurred'); } } }