import 'package:dio/dio.dart'; import '../../../../core/network/api_problem.dart'; import '../../../../data/network/api_client.dart'; import '../models/profile_settings.dart'; class ProfileApi { const ProfileApi({required ApiClient apiClient}) : _apiClient = apiClient; final ApiClient _apiClient; Future getProfile() async { final json = await _apiClient.getJson('/api/v1/users/me/profile'); return _toSettings(json); } Future updateProfile(ProfileSettingsV1 next) async { final payload = { 'display_name': next.displayName, 'bio': next.bio, if (next.avatarPath != null && next.avatarPath!.isNotEmpty) 'avatar_path': next.avatarPath, }; final json = await _apiClient.rawDio.patch>( '/api/v1/users/me/profile', data: payload, ); final data = json.data; if (data is! Map) { throw ApiProblem( status: 502, title: 'Invalid profile payload', detail: 'Expected profile response object', ); } return _toSettings(data); } Future updateSettings(ProfileSettingsV1 settings) async { final payload = { 'settings': { 'version': settings.version, 'preferences': { 'language': settings.preferences.language, 'timezone': settings.preferences.timezone, }, 'privacy': { 'can_sell': settings.privacy.canSell, 'profile_visibility': settings.privacy.profileVisibility, }, 'notification': { 'allow_notifications': settings.notification.allowNotifications, 'allow_vibration': settings.notification.allowVibration, }, 'divination_tutorial': { 'divination_entry_shown': settings.divinationTutorial.divinationEntryShown, 'auto_divination_shown': settings.divinationTutorial.autoDivinationShown, 'manual_divination_shown': settings.divinationTutorial.manualDivinationShown, }, }, }; final json = await _apiClient.rawDio.patch>( '/api/v1/users/me/settings', data: payload, ); final data = json.data; if (data is! Map) { throw ApiProblem( status: 502, title: 'Invalid settings payload', detail: 'Expected settings response object', ); } return _toSettings(data); } Future uploadAvatar(String filePath) async { final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile(filePath), }); final response = await _apiClient.rawDio.post>( '/api/v1/users/me/avatar', data: formData, ); final data = response.data; if (data is! Map) { throw ApiProblem( status: 502, title: 'Invalid profile payload', detail: 'Expected profile response object', ); } return _toSettings(data); } Future deleteAccount() async { await _apiClient.deleteNoContent('/api/v1/users/me'); } ProfileSettingsV1 _toSettings(Map json) { final settingsRaw = json['settings']; final preferencesRaw = settingsRaw is Map ? settingsRaw['preferences'] : null; final preferences = preferencesRaw is Map ? PreferenceSettings( language: (preferencesRaw['language'] as String?) ?? 'zh-CN', timezone: (preferencesRaw['timezone'] as String?) ?? 'Asia/Shanghai', ) : const PreferenceSettings(); final privacyRaw = settingsRaw is Map ? settingsRaw['privacy'] : null; final privacy = privacyRaw is Map ? PrivacySettings( canSell: (privacyRaw['can_sell'] as bool?) ?? false, profileVisibility: (privacyRaw['profile_visibility'] as String?) ?? 'public', ) : const PrivacySettings(); final notificationRaw = settingsRaw is Map ? settingsRaw['notification'] : null; final notification = notificationRaw is Map ? NotificationSettings( allowNotifications: (notificationRaw['allow_notifications'] as bool? ?? true), allowVibration: (notificationRaw['allow_vibration'] as bool? ?? true), ) : const NotificationSettings(); final divinationTutorialRaw = settingsRaw is Map ? settingsRaw['divination_tutorial'] : null; final divinationTutorial = divinationTutorialRaw is Map ? DivinationTutorialSettings( divinationEntryShown: (divinationTutorialRaw['divination_entry_shown'] as bool?) ?? true, autoDivinationShown: (divinationTutorialRaw['auto_divination_shown'] as bool?) ?? true, manualDivinationShown: (divinationTutorialRaw['manual_divination_shown'] as bool?) ?? true, ) : const DivinationTutorialSettings(); return ProfileSettingsV1( displayName: (json['display_name'] as String?) ?? '', bio: (json['bio'] as String?) ?? '', avatarPath: json['avatar_path'] as String?, avatarUrl: json['avatar_url'] as String?, preferences: preferences, privacy: privacy, notification: notification, divinationTutorial: divinationTutorial, ); } }