import 'dart:developer' as developer; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tatlock_ui/core/auth/auth_provider.dart'; import 'package:tatlock_ui/core/semantics/semantic_ids.dart'; import 'package:tatlock_ui/core/theme/theme_provider.dart'; import 'package:tatlock_ui/routing/app_router.dart'; /// Profile dropdown menu in the header. /// /// Shows user info when authenticated, with Settings and Logout options. class ProfileDropdown extends ConsumerWidget { const ProfileDropdown({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); final currentTheme = ref.watch(themeProvider); final colorScheme = Theme.of(context).colorScheme; return authState.when( data: (auth) => Semantics( identifier: ProfileSemantics.button, label: 'Profile menu', button: true, child: PopupMenuButton( offset: const Offset(0, 48), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), child: Tooltip( message: auth.isAuthenticated ? auth.userName ?? 'User' : 'Guest', child: CircleAvatar( radius: 18, backgroundColor: colorScheme.primaryContainer, child: auth.isAuthenticated ? Text( _getInitials(auth.userName), style: TextStyle( color: colorScheme.onPrimaryContainer, fontWeight: FontWeight.w500, ), ) : Icon( Icons.person_outline, size: 20, color: colorScheme.onPrimaryContainer, ), ), ), itemBuilder: (context) => [ // User info header (non-selectable) PopupMenuItem( enabled: false, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( auth.isAuthenticated ? auth.userName ?? 'User' : 'Guest', style: Theme.of(context).textTheme.titleSmall, ), if (auth.userEmail != null) Text( auth.userEmail!, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: colorScheme.onSurfaceVariant, ), ), ], ), ), const PopupMenuDivider(), // Settings PopupMenuItem( value: 'settings', child: Semantics( identifier: ProfileSemantics.settings, child: const Row( children: [ Icon(Icons.settings_outlined, size: 20), SizedBox(width: 12), Text('Settings'), ], ), ), ), // Theme submenu header PopupMenuItem( enabled: false, height: 32, child: Text( 'THEME', style: Theme.of(context).textTheme.labelSmall?.copyWith( color: colorScheme.onSurfaceVariant, letterSpacing: 0.5, ), ), ), // Theme: System PopupMenuItem( value: 'theme_system', height: 40, child: Semantics( identifier: ProfileSemantics.themeSystem, child: Row( children: [ Icon( Icons.brightness_auto, size: 18, color: currentTheme == ThemeSetting.system ? colorScheme.primary : null, ), const SizedBox(width: 12), Text( 'System', style: currentTheme == ThemeSetting.system ? TextStyle(color: colorScheme.primary) : null, ), const Spacer(), if (currentTheme == ThemeSetting.system) Icon(Icons.check, size: 16, color: colorScheme.primary), ], ), ), ), // Theme: Light PopupMenuItem( value: 'theme_light', height: 40, child: Semantics( identifier: ProfileSemantics.themeLight, child: Row( children: [ Icon( Icons.light_mode, size: 18, color: currentTheme == ThemeSetting.light ? colorScheme.primary : null, ), const SizedBox(width: 12), Text( 'Light', style: currentTheme == ThemeSetting.light ? TextStyle(color: colorScheme.primary) : null, ), const Spacer(), if (currentTheme == ThemeSetting.light) Icon(Icons.check, size: 16, color: colorScheme.primary), ], ), ), ), // Theme: Dark PopupMenuItem( value: 'theme_dark', height: 40, child: Semantics( identifier: ProfileSemantics.themeDark, child: Row( children: [ Icon( Icons.dark_mode, size: 18, color: currentTheme == ThemeSetting.dark ? colorScheme.primary : null, ), const SizedBox(width: 12), Text( 'Dark', style: currentTheme == ThemeSetting.dark ? TextStyle(color: colorScheme.primary) : null, ), const Spacer(), if (currentTheme == ThemeSetting.dark) Icon(Icons.check, size: 16, color: colorScheme.primary), ], ), ), ), const PopupMenuDivider(), // Logout (only if authenticated) if (auth.isAuthenticated) PopupMenuItem( value: 'logout', child: Semantics( identifier: ProfileSemantics.logout, child: const Row( children: [ Icon(Icons.logout, size: 20), SizedBox(width: 12), Text('Logout'), ], ), ), ), ], onSelected: (value) { switch (value) { case 'settings': context.go(AppRoutes.settings); case 'theme_system': _updateTheme(ref, ThemeSetting.system); case 'theme_light': _updateTheme(ref, ThemeSetting.light); case 'theme_dark': _updateTheme(ref, ThemeSetting.dark); case 'logout': ref.read(authProvider.notifier).signOut(); } }, ), ), loading: () => const CircleAvatar( radius: 18, child: SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), ), error: (err, stack) => CircleAvatar( radius: 18, backgroundColor: colorScheme.errorContainer, child: Icon( Icons.error_outline, size: 20, color: colorScheme.onErrorContainer, ), ), ); } String _getInitials(String? name) { if (name == null || name.isEmpty) return '?'; final parts = name.trim().split(' '); if (parts.length >= 2) { return '${parts.first[0]}${parts.last[0]}'.toUpperCase(); } return name[0].toUpperCase(); } /// Update theme locally and sync to API. Future _updateTheme(WidgetRef ref, ThemeSetting setting) async { // Update local theme immediately for instant UI response await ref.read(themeProvider.notifier).setSetting(setting); // Sync to backend (fire-and-forget, errors logged not shown) try { await ref.read(authProvider.notifier).updatePreferences( theme: setting.name, ); } catch (e) { // Theme still works locally even if API sync fails developer.log('Failed to sync theme preference: $e', name: 'profile'); } } }