import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:social_app/core/config/env.dart'; import 'package:social_app/app/di/injection.dart'; import 'package:social_app/app/router/app_routes.dart'; import 'package:social_app/core/l10n/l10n.dart'; import 'package:social_app/core/theme/design_tokens.dart'; import 'package:social_app/core/auth/session_controller.dart'; import 'package:social_app/features/contacts/data/models/user_profile.dart'; import 'package:social_app/features/contacts/data/repositories/friend_repository.dart'; import 'package:social_app/shared/widgets/app_button.dart'; import 'package:social_app/shared/widgets/app_loading_indicator.dart'; import 'package:social_app/shared/widgets/app_pressable.dart'; import 'package:social_app/shared/widgets/destructive_action_sheet.dart'; import 'package:social_app/shared/widgets/toast/toast.dart'; import 'package:social_app/shared/widgets/toast/toast_type.dart'; import 'package:social_app/core/utils/phone_display_formatter.dart'; import 'package:social_app/features/settings/data/apis/settings_api.dart'; import 'package:social_app/features/settings/data/apis/automation_jobs_api.dart'; import 'package:social_app/features/settings/data/repositories/user_profile_cache_repository.dart'; import 'package:social_app/app/router/home_return_policy.dart'; import '../widgets/settings_page_scaffold.dart'; const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button'); const settingsLogoutButtonKey = ValueKey('settings_logout_button'); class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { final FriendRepository _friendRepository = sl(); final SessionController _sessionController = sl(); final AutomationJobsApi _automationJobsApi = sl(); final UserProfileCacheRepository _userCache = sl(); UserProfile? _user; bool _isLoading = true; int _friendsCount = 0; String? _firstFriendName; int _enabledJobsCount = 0; String? _firstEnabledJobTitle; @override void initState() { super.initState(); final cachedUser = _userCache.cachedUser; if (cachedUser != null) { _user = cachedUser; _isLoading = false; } _loadData(); } Future _loadData() async { try { final user = await _userCache.getProfile(); if (mounted) { setState(() { _user = user; _isLoading = false; }); } } catch (e) { if (mounted && _user == null) { setState(() { _isLoading = false; }); } } try { final friends = await _friendRepository.getFriends(); if (mounted) { setState(() { _friendsCount = friends.length; _firstFriendName = friends.isNotEmpty ? friends.first.username : null; }); } } catch (e) { // Keep profile available even when contacts fail. } try { final jobs = await _automationJobsApi.list(); final enabledJobs = jobs.where((job) => job.isActive).toList(); if (mounted) { setState(() { _enabledJobsCount = enabledJobs.length; _firstEnabledJobTitle = enabledJobs.isNotEmpty ? enabledJobs.first.title : null; }); } } catch (e) { // Keep profile available even when automation jobs fail. } } @override Widget build(BuildContext context) { return SettingsPageScaffold( title: context.l10n.settingsTitle, onBack: () => returnToHomePreserveState(context, forceGoHome: true), body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildProfileHero(), const SizedBox(height: AppSpacing.lg), _buildQuickActions(context), const SizedBox(height: AppSpacing.lg), _buildSubscriptionCard(), const SizedBox(height: AppSpacing.lg), _buildMenuCard(context), const SizedBox(height: AppSpacing.xl), _buildLogoutAction(), ], ), ); } Widget _buildProfileHero() { final colorScheme = Theme.of(context).colorScheme; if (_isLoading) { return Container( width: double.infinity, height: 120, padding: const EdgeInsets.all(AppSpacing.xl), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.xxl), ), child: const Center(child: AppLoadingIndicator(size: 22)), ); } final l10n = context.l10n; final username = _user?.username ?? l10n.settingsUnset; final phone = _user?.phone == null ? l10n.settingsUnset : formatPhoneForDisplay(_user?.phone); return Container( width: double.infinity, padding: const EdgeInsets.all(AppSpacing.xl), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [colorScheme.surface, colorScheme.surfaceContainerLow], ), borderRadius: BorderRadius.circular(AppRadius.xxl), border: Border.all(color: colorScheme.outlineVariant), boxShadow: [ BoxShadow( color: colorScheme.shadow.withValues(alpha: 0.2), blurRadius: 14, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: 64, height: 64, decoration: BoxDecoration( borderRadius: BorderRadius.circular(32), boxShadow: [ BoxShadow( color: colorScheme.primary.withValues(alpha: 0.2), blurRadius: 12, offset: const Offset(0, 4), ), ], ), clipBehavior: Clip.antiAlias, child: _buildAvatarImage(_user?.avatarUrl), ), const SizedBox(width: AppSpacing.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( username, style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Text( phone, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: colorScheme.onSurfaceVariant, ), ), ], ), ), const SizedBox(width: AppSpacing.md), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ AppPressable( key: settingsProfileEditButtonKey, onTap: _onTapEditProfile, borderRadius: BorderRadius.circular(AppRadius.lg), child: SizedBox( width: AppSpacing.xl * 2, height: AppSpacing.xl * 2, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( Icons.edit, size: 14, color: colorScheme.onSurfaceVariant, ), const SizedBox(height: 3), Container( width: 12, height: 1.5, decoration: BoxDecoration( color: colorScheme.onSurfaceVariant, borderRadius: BorderRadius.circular( AppRadius.full, ), ), ), ], ), ), ), const SizedBox(height: AppSpacing.sm), _buildFreeBadge(), ], ), ], ), ], ), ); } Widget _buildFreeBadge() { final colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( gradient: LinearGradient( colors: [ colorScheme.primaryContainer, colorScheme.surfaceContainerLow, ], ), borderRadius: BorderRadius.circular(12), border: Border.all(color: colorScheme.outlineVariant), ), child: Text( context.l10n.settingsFreeBadge, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: colorScheme.primary, ), ), ); } Widget _buildAvatarImage(String? avatarUrl) { final colorScheme = Theme.of(context).colorScheme; if (avatarUrl == null) { return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primaryContainer, colorScheme.surfaceContainerLow, ], ), ), child: Icon(Icons.person, size: 28, color: colorScheme.primary), ); } return Image.network( avatarUrl, fit: BoxFit.cover, width: 64, height: 64, errorBuilder: (context, error, stackTrace) { return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primaryContainer, colorScheme.surfaceContainerLow, ], ), ), child: Icon(Icons.person, size: 28, color: colorScheme.primary), ); }, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primaryContainer, colorScheme.surfaceContainerLow, ], ), ), child: Center( child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(colorScheme.primary), ), ), ), ); }, ); } String _buildFriendsSubtitle() { final l10n = context.l10n; if (_friendsCount == 0) { return l10n.settingsNoContacts; } if (_friendsCount == 1) { return l10n.settingsContactsAddedOne( _firstFriendName ?? l10n.commonUnknown, ); } return l10n.settingsContactsAddedMany(_friendsCount); } String _buildAutomationSubtitle() { final l10n = context.l10n; if (_enabledJobsCount == 0) { return l10n.settingsNoEnabledPlans; } if (_enabledJobsCount == 1) { return l10n.settingsEnabledPlanOne( _firstEnabledJobTitle ?? l10n.settingsFeaturesTitle, ); } return l10n.settingsEnabledPlanMany(_enabledJobsCount); } Widget _buildQuickActions(BuildContext context) { return Row( children: [ Expanded( child: _buildActionCard( icon: Icons.people, title: context.l10n.contactsTitle, subtitle: _buildFriendsSubtitle(), onTap: () => context.push(AppRoutes.contactsList), ), ), const SizedBox(width: AppSpacing.md), Expanded( child: _buildActionCard( icon: Icons.auto_awesome, title: context.l10n.settingsFeaturesTitle, subtitle: _buildAutomationSubtitle(), onTap: () => context.push(AppRoutes.settingsFeatures), ), ), ], ); } Widget _buildActionCard({ required IconData icon, required String title, required String subtitle, required VoidCallback onTap, }) { final colorScheme = Theme.of(context).colorScheme; return AppPressable( onTap: onTap, borderRadius: BorderRadius.circular(AppRadius.xl), child: Container( constraints: const BoxConstraints(minHeight: 136), padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: colorScheme.outlineVariant), boxShadow: [ BoxShadow( color: colorScheme.shadow.withValues(alpha: 0.15), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: colorScheme.surfaceContainerLow, borderRadius: BorderRadius.circular(10), ), child: Icon(icon, size: 18, color: colorScheme.primary), ), const Spacer(), Icon( Icons.chevron_right, size: 16, color: colorScheme.onSurfaceVariant, ), ], ), const SizedBox(height: AppSpacing.md), Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( subtitle, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: colorScheme.onSurfaceVariant, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ); } Widget _buildSubscriptionCard() { final colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [colorScheme.surface, colorScheme.surfaceContainerLow], ), borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: colorScheme.outlineVariant), boxShadow: [ BoxShadow( color: colorScheme.shadow.withValues(alpha: 0.15), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primaryContainer, colorScheme.surfaceContainerLow, ], ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: colorScheme.primary.withValues(alpha: 0.2), blurRadius: 6, offset: const Offset(0, 1), ), ], ), child: Icon( Icons.workspace_premium, size: 22, color: colorScheme.primary, ), ), const SizedBox(width: AppSpacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.l10n.settingsUpgradeProTitle, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( context.l10n.settingsUpgradeProDesc, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: colorScheme.onSurfaceVariant, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( gradient: LinearGradient( colors: [ colorScheme.primary, colorScheme.primary.withValues(alpha: 0.85), ], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: colorScheme.primary.withValues(alpha: 0.3), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Text( context.l10n.settingsUpgradeButton, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: colorScheme.onPrimary, ), ), ), ], ), ); } Widget _buildMenuCard(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; return Container( decoration: BoxDecoration( color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: colorScheme.outlineVariant), ), child: Column( children: [ _buildMenuItem( icon: Icons.notifications, title: context.l10n.settingsMenuNotifications, onTap: () {}, ), _buildDivider(), _buildMenuItem( icon: Icons.bookmark, title: context.l10n.memoryTitle, onTap: () => context.push(AppRoutes.settingsMemory), ), _buildDivider(), _buildMenuItem( icon: Icons.system_update, title: context.l10n.settingsMenuCheckUpdates, trailing: 'v${Env.version}', onTap: _checkForUpdates, ), ], ), ); } Widget _buildMenuItem({ required IconData icon, required String title, String? trailing, required VoidCallback onTap, }) { final colorScheme = Theme.of(context).colorScheme; return AppPressable( onTap: onTap, borderRadius: BorderRadius.circular(AppRadius.md), child: Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 14), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(icon, size: 20, color: colorScheme.onSurfaceVariant), const SizedBox(width: 10), Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: colorScheme.onSurface, ), ), ], ), Row( children: [ if (trailing != null) ...[ Text( trailing, style: TextStyle( fontSize: 14, color: colorScheme.onSurfaceVariant, ), ), const SizedBox(width: 6), ], Icon( Icons.chevron_right, size: 18, color: colorScheme.onSurfaceVariant, ), ], ), ], ), ), ); } Widget _buildDivider() { final colorScheme = Theme.of(context).colorScheme; return Container( height: 1, margin: const EdgeInsets.symmetric(horizontal: 14), color: colorScheme.outlineVariant, ); } Future _onTapEditProfile() async { final changed = await context.push(AppRoutes.settingsEditProfile); if (changed == true && mounted) { final cached = _userCache.cachedUser; if (cached != null) { setState(() { _user = cached; }); } } } Future _onTapLogout() async { final confirmed = await showDestructiveActionSheet( context, title: context.l10n.settingsLogoutTitle, message: context.l10n.settingsLogoutConfirmMessage, confirmText: context.l10n.settingsLogoutConfirm, ); if (!confirmed || !mounted) { return; } await _userCache.invalidate(); if (!mounted) { return; } try { await _sessionController.logoutAndWaitUnauthenticated(); } catch (_) { if (!mounted) return; Toast.show( context, context.l10n.settingsLogoutFailed, type: ToastType.error, ); return; } if (!mounted) return; context.go(AppRoutes.authLogin); } Future _checkForUpdates() async { try { final settingsApi = sl(); final result = await settingsApi.checkUpdates( currentVersionCode: Env.build, currentVersionName: Env.version, platform: 'android', ); if (!mounted) return; if (!result.hasUpdate) { Toast.show( context, context.l10n.settingsLatestVersion, type: ToastType.success, ); return; } final message = result.updateType == 'required' ? context.l10n.settingsUpdateRequired(result.latestVersionName) : context.l10n.settingsUpdateOptional(result.latestVersionName); final shouldUpdate = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(context.l10n.settingsUpdateDialogTitle), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text(context.l10n.commonCancel), ), if (result.downloadUrl != null) TextButton( onPressed: () => Navigator.pop(context, true), child: Text(context.l10n.settingsUpdateAction), ), ], ), ); if (shouldUpdate == true && result.downloadUrl != null && mounted) { Toast.show( context, context.l10n.settingsDownloadLink(result.downloadUrl!), type: ToastType.info, ); } } catch (e) { if (!mounted) return; Toast.show( context, context.l10n.settingsUpdateCheckFailed, type: ToastType.error, ); } } Widget _buildLogoutAction() { return SizedBox( width: double.infinity, height: 52, child: AppButton( key: settingsLogoutButtonKey, text: context.l10n.settingsLogoutTitle, isOutlined: true, onPressed: () => _onTapLogout(), ), ); } }