import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/api/api_client.dart'; import '../../../core/api/api_paths.dart'; import '../../../core/api/guarded_api_call.dart'; import '../../auth/data/auth_providers.dart'; import '../domain/admin_ai_categorize_result.dart'; import '../domain/admin_category_node.dart'; import '../domain/admin_product.dart'; import '../domain/ai_model_info.dart'; import '../domain/pending_product.dart'; import '../domain/user_admin.dart'; final adminRepositoryProvider = Provider((ref) { return AdminRepository(ref.watch(apiClientProvider), ref); }); class AdminRepository { final ApiClient _apiClient; final Ref _ref; AdminRepository(this._apiClient, this._ref); Future _token() => _ref.read(authStateProvider.future); Future> listUsers() async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.getJson(UserApiPaths.list, token: token), ); return (data as List).map((e) => UserAdmin.fromJson(e as Map)).toList(); } Future setRole(int userId, String newRole) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.patchJson(UserApiPaths.setRole(userId), body: {'role': newRole}, token: token), ); return UserAdmin.fromJson(data); } Future setPremium(int userId, {required bool isPremium}) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.patchJson(UserApiPaths.setPremium(userId), body: {'isPremium': isPremium}, token: token), ); return UserAdmin.fromJson(data); } Future setRecipeSharing(int userId, {required bool canShareRecipes}) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.patchJson( UserApiPaths.setRecipeSharing(userId), body: {'canShareRecipes': canShareRecipes}, token: token, ), ); return UserAdmin.fromJson(data); } Future updateEmail(int userId, String email) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.patchJson( UserApiPaths.updateEmail(userId), body: {'email': email}, token: token, ), ); } Future createUser({ required String username, required String email, required String password, String role = 'user', }) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.postJson(UserApiPaths.list, body: { 'username': username, 'email': email, 'password': password, 'role': role, }, token: token), ); return UserAdmin.fromJson(data as Map); } Future deleteUser(int userId) async { final token = await _token(); return guardedApiCall( _ref, () => _apiClient.deleteJson(UserApiPaths.delete(userId), token: token), ); } /// Returns `{ temporaryPassword, to, subject, body }`. Future> resetPassword(int userId) async { final token = await _token(); final result = await guardedApiCall( _ref, () => _apiClient.postJson(UserApiPaths.resetPassword(userId), token: token), ); return (result as Map); } Future> listPendingProducts() async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.getJson(ProductApiPaths.pending, token: token), ); return (data as List) .map((e) => PendingProduct.fromJson(e as Map)) .toList(); } Future setProductStatus(int productId, String status) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.patchJson( ProductApiPaths.setStatus(productId), body: {'status': status}, token: token, ), ); } Future> listAiModels() async { final data = await guardedApiCall( _ref, () => _apiClient.getJson(AiApiPaths.models), ); return (data as List) .map((e) => AiModelInfo.fromJson(e as Map)) .toList(); } Future> listProducts() async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.getJson(ProductApiPaths.list, token: token), ); return (data as List) .map((e) => AdminProduct.fromJson(e as Map)) .toList(); } /// Skapar en ny aktiv produkt (kräver admin). Returnerar `{id, name, categoryId?}`. Future> createProduct(String name, {int? categoryId}) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.postJson( ProductApiPaths.list, body: { 'name': name.trim(), if (categoryId != null) 'categoryId': categoryId, }, token: token, ), ); return data as Map; } Future> listDeletedProducts() async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.getJson(ProductApiPaths.deleted, token: token), ); return (data as List) .map((e) => AdminProduct.fromJson(e as Map)) .toList(); } Future> listCategoryTree() async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.getJson(CategoryApiPaths.tree, token: token), ); return (data as List) .map((e) => AdminCategoryNode.fromJson(e as Map)) .toList(); } Future bulkSetCategory(List ids, {required int? categoryId}) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.postJson( ProductApiPaths.bulkUpdate, body: {'ids': ids, 'categoryId': categoryId}, token: token, ), ); } Future setProductCategory(int productId, {required int? categoryId}) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.patchJson( ProductApiPaths.update(productId), body: {'categoryId': categoryId}, token: token, ), ); } Future removeProduct(int productId) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.deleteJson(ProductApiPaths.remove(productId), token: token), ); } Future restoreProduct(int productId) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.postJson(ProductApiPaths.restore(productId), token: token), ); } Future mergeProducts({ required int sourceProductId, required int targetProductId, }) async { final token = await _token(); await guardedApiCall( _ref, () => _apiClient.postJson( ProductApiPaths.merge, body: { 'sourceProductId': sourceProductId, 'targetProductId': targetProductId, }, token: token, ), ); } Future> aiCategorizeBulk({ List? productIds, }) async { final token = await _token(); final data = await guardedApiCall( _ref, () => _apiClient.postJson( ProductApiPaths.aiCategorizeBulk, body: productIds == null || productIds.isEmpty ? null : {'productIds': productIds}, token: token, ), ); return (data as List) .map((e) => AdminAiCategorizeResult.fromJson(e as Map)) .where((e) => e.productId > 0 && e.categoryId > 0) .toList(); } }