import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'supabase_provider.dart'; import '../utils/app_time.dart'; /// Admin user query parameters for server-side pagination. class AdminUserQuery { /// Creates admin user query parameters. const AdminUserQuery({ this.offset = 0, this.limit = 50, this.searchQuery = '', }); /// Offset for pagination. final int offset; /// Number of items per page (default: 50). final int limit; /// Full text search query. final String searchQuery; AdminUserQuery copyWith({int? offset, int? limit, String? searchQuery}) { return AdminUserQuery( offset: offset ?? this.offset, limit: limit ?? this.limit, searchQuery: searchQuery ?? this.searchQuery, ); } } final adminUserQueryProvider = StateProvider( (ref) => const AdminUserQuery(), ); final adminUserControllerProvider = Provider((ref) { final client = ref.watch(supabaseClientProvider); return AdminUserController(client); }); class AdminUserStatus { AdminUserStatus({required this.email, required this.bannedUntil}); final String? email; final DateTime? bannedUntil; bool get isLocked { if (bannedUntil == null) return false; return bannedUntil!.isAfter(AppTime.now()); } } class AdminUserController { AdminUserController(this._client); final SupabaseClient _client; Future updateProfile({ required String userId, required String fullName, required String role, }) async { await _client .from('profiles') .update({'full_name': fullName, 'role': role}) .eq('id', userId); } Future updateRole({ required String userId, required String role, }) async { await _client.from('profiles').update({'role': role}).eq('id', userId); } /// Password administration — forwarded to the admin Edge Function. Future setPassword({ required String userId, required String password, }) async { final payload = { 'action': 'set_password', 'userId': userId, 'password': password, }; final accessToken = _client.auth.currentSession?.accessToken; final response = await _client.functions.invoke( 'admin_user_management', body: payload, headers: accessToken == null ? null : {'Authorization': 'Bearer $accessToken'}, ); if (response.status != 200) { throw Exception(response.data ?? 'Failed to reset password'); } } /// Set/unset a user's ban/lock via the admin Edge Function (preferred). Future setLock({required String userId, required bool locked}) async { final payload = {'action': 'set_lock', 'userId': userId, 'locked': locked}; final accessToken = _client.auth.currentSession?.accessToken; final response = await _client.functions.invoke( 'admin_user_management', body: payload, headers: accessToken == null ? null : {'Authorization': 'Bearer $accessToken'}, ); if (response.status != 200) { throw Exception(response.data ?? 'Failed to update lock state'); } } /// Fetch user email + banned state from the admin Edge Function (auth.user). Future fetchStatus(String userId) async { final payload = {'action': 'get_user', 'userId': userId}; final accessToken = _client.auth.currentSession?.accessToken; final response = await _client.functions.invoke( 'admin_user_management', body: payload, headers: accessToken == null ? null : {'Authorization': 'Bearer $accessToken'}, ); if (response.status != 200) { return AdminUserStatus(email: null, bannedUntil: null); } final data = response.data; final user = (data is Map) ? (data['user'] as Map?) : null; final email = user?['email'] as String?; DateTime? bannedUntil; final bannedRaw = user?['banned_until']; if (bannedRaw is String) { bannedUntil = DateTime.tryParse(bannedRaw); } else if (bannedRaw is DateTime) { bannedUntil = bannedRaw; } return AdminUserStatus( email: email, bannedUntil: bannedUntil == null ? null : AppTime.toAppTime(bannedUntil), ); } /// Server-side paginated listing via Edge Function (returns auth + profile light view). Future>> listUsers(AdminUserQuery q) async { final payload = { 'action': 'list_users', 'offset': q.offset, 'limit': q.limit, 'searchQuery': q.searchQuery, }; final accessToken = _client.auth.currentSession?.accessToken; final response = await _client.functions.invoke( 'admin_user_management', body: payload, headers: accessToken == null ? null : {'Authorization': 'Bearer $accessToken'}, ); if (response.status != 200) { throw Exception(response.data ?? 'Failed to list users'); } final users = (response.data is Map && response.data['users'] is List) ? (response.data['users'] as List).cast>() : >[]; return users; } } final adminUserStatusProvider = FutureProvider.family .autoDispose((ref, userId) { return ref.watch(adminUserControllerProvider).fetchStatus(userId); }); final adminUsersProvider = FutureProvider.autoDispose>>((ref) { final q = ref.watch(adminUserQueryProvider); final ctrl = ref.watch(adminUserControllerProvider); return ctrl.listUsers(q); });