import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import 'package:cached_network_image/cached_network_image.dart'; import '../../core/api/api_client.dart'; import '../../core/auth/auth_token_store.dart'; import '../../core/api/api_config.dart'; import '../../core/api/services/user_api.dart'; import '../../core/user/account_refresh.dart'; import '../recharge/payment_webview_screen.dart'; import '../../core/theme/app_colors.dart'; import '../../core/user/user_state.dart'; import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_typography.dart'; /// Profile screen - matches Pencil KXeow class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key, required this.isActive}); final bool isActive; @override State createState() => _ProfileScreenState(); } class _ProfileScreenState extends State { @override void initState() { super.initState(); if (widget.isActive) refreshAccount(updateProfile: true); } @override void didUpdateWidget(covariant ProfileScreen oldWidget) { super.didUpdateWidget(oldWidget); if (widget.isActive && !oldWidget.isActive) { refreshAccount(updateProfile: true); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, body: SingleChildScrollView( padding: const EdgeInsets.fromLTRB( AppSpacing.screenPadding, AppSpacing.xxl, AppSpacing.screenPadding, AppSpacing.screenPaddingLarge, ), child: Column( children: [ ValueListenableBuilder( valueListenable: UserState.avatar, builder: (_, avatarUrl, __) { return ValueListenableBuilder( valueListenable: UserState.userName, builder: (_, userName, __) { return ValueListenableBuilder( valueListenable: UserState.userId, builder: (_, userId, __) { return _ProfileHeader( avatarUrl: avatarUrl, userName: userName, uid: userId, ); }, ); }, ); }, ), const SizedBox(height: AppSpacing.xl), _BalanceCard( balance: UserCreditsData.of(context)?.creditsDisplay ?? '--', onRecharge: () => Navigator.of(context).pushNamed('/recharge'), ), const SizedBox(height: AppSpacing.xxl), _MenuSection( items: [ _MenuItem( title: 'Privacy Policy', icon: LucideIcons.chevron_right, onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => const PaymentWebViewScreen( paymentUrl: 'https://www.petsheroai.xyz/privacy.html', title: 'Privacy Policy', ), ), ), ), _MenuItem( title: 'User Agreement', icon: LucideIcons.chevron_right, onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => const PaymentWebViewScreen( paymentUrl: 'https://www.petsheroai.xyz/terms.html', title: 'User Agreement', ), ), ), ), _MenuItem( title: 'Delete Account', icon: LucideIcons.trash_2, iconColor: const Color(0xFFDC2626), onTap: () => _DeleteAccountDialog.show(context), ), ], ), ], ), ), ); } } class _ProfileHeader extends StatelessWidget { const _ProfileHeader({ this.avatarUrl, this.userName, this.uid, }); final String? avatarUrl; final String? userName; final String? uid; @override Widget build(BuildContext context) { return Container( height: 220, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 80, height: 80, decoration: BoxDecoration( color: AppColors.border, borderRadius: BorderRadius.circular(40), border: Border.all( color: AppColors.primaryLight, width: 2, ), ), clipBehavior: Clip.antiAlias, child: avatarUrl != null && avatarUrl!.isNotEmpty ? CachedNetworkImage( imageUrl: avatarUrl!, fit: BoxFit.cover, placeholder: (_, __) => const Icon( LucideIcons.user, size: 40, color: AppColors.textSecondary, ), errorWidget: (_, __, ___) => const Icon( LucideIcons.user, size: 40, color: AppColors.textSecondary, ), ) : const Icon( LucideIcons.user, size: 40, color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.lg), Text( userName ?? 'VIP', style: AppTypography.bodyLarge.copyWith( color: AppColors.textPrimary, ), ), const SizedBox(height: AppSpacing.lg), Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.lg, vertical: AppSpacing.sm, ), decoration: BoxDecoration( color: AppColors.surfaceAlt, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border), ), child: Text( uid != null && uid!.isNotEmpty ? 'UID $uid' : 'UID --', style: AppTypography.caption.copyWith( color: AppColors.textSecondary, ), ), ), ], ), ); } } class _BalanceCard extends StatelessWidget { const _BalanceCard({ required this.balance, required this.onRecharge, }); final String balance; final VoidCallback onRecharge; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xxl, vertical: AppSpacing.xl, ), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(16), boxShadow: const [ BoxShadow( color: AppColors.shadowLight, blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'AVAILABLE BALANCE', style: AppTypography.label.copyWith( color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.sm), Text( balance, style: AppTypography.bodyLarge.copyWith( fontSize: 24, fontWeight: FontWeight.w700, color: AppColors.primary, ), ), ], ), GestureDetector( onTap: onRecharge, child: Container( padding: const EdgeInsets.symmetric( horizontal: 14, vertical: AppSpacing.md, ), decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(12), ), child: Text( 'Recharge', style: AppTypography.bodyRegular.copyWith( color: AppColors.surface, fontWeight: FontWeight.w600, ), ), ), ), ], ), ); } } class _MenuSection extends StatelessWidget { const _MenuSection({required this.items}); final List<_MenuItem> items; @override Widget build(BuildContext context) { return Column( children: items .map( (item) => Padding( padding: const EdgeInsets.only(bottom: AppSpacing.md), child: _MenuItem( title: item.title, icon: item.icon, onTap: item.onTap, ), ), ) .toList(), ); } } class _MenuItem extends StatelessWidget { const _MenuItem({ required this.title, required this.icon, required this.onTap, this.iconColor, }); final String title; final IconData icon; final VoidCallback onTap; final Color? iconColor; @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xl, vertical: 14, ), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( color: AppColors.shadowLight, blurRadius: 6, offset: Offset(0, 2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: AppTypography.bodyRegular.copyWith( color: iconColor ?? AppColors.textPrimary, ), ), Icon(icon, size: 20, color: iconColor ?? AppColors.textMuted), ], ), ), ); } } /// Delete Account confirmation dialog - matches Pencil Xp6Qz class _DeleteAccountDialog extends StatefulWidget { const _DeleteAccountDialog({required this.parentContext}); final BuildContext parentContext; static void show(BuildContext context) { showDialog( context: context, barrierColor: const Color(0x80000000), builder: (_) => _DeleteAccountDialog(parentContext: context), ); } @override State<_DeleteAccountDialog> createState() => _DeleteAccountDialogState(); } class _DeleteAccountDialogState extends State<_DeleteAccountDialog> { bool _deleting = false; String? _errorText; final _verifyController = TextEditingController(); static const _verifyCode = 'DELETE'; bool get _isVerifyMatch => _verifyController.text.trim().toUpperCase() == _verifyCode; @override void initState() { super.initState(); _verifyController.addListener(() => setState(() {})); } @override void dispose() { _verifyController.dispose(); super.dispose(); } Future _onDelete() async { setState(() { _errorText = null; _deleting = true; }); try { final res = await UserApi.deleteAccount( sentinel: ApiConfig.appId, asset: UserState.userId.value, ); if (!mounted) return; if (res.isSuccess) { // Clear user state and token UserState.setCredits(null); UserState.setUserId(null); UserState.setAvatar(null); UserState.setUserName(null); UserState.setNavigate(null); await AuthTokenStore.clear(); ApiClient.instance.setUserToken(null); if (!mounted) return; Navigator.of(context).pop(); if (widget.parentContext.mounted) { ScaffoldMessenger.of(widget.parentContext).showSnackBar( const SnackBar( content: Text('Account deleted'), behavior: SnackBarBehavior.floating, ), ); } } else { setState(() => _errorText = res.msg.isNotEmpty ? res.msg : 'Delete failed'); } } catch (e) { if (mounted) { setState(() => _errorText = e.toString().replaceAll('Exception: ', '')); } } finally { if (mounted) { setState(() => _deleting = false); } } } @override Widget build(BuildContext context) { return Center( child: Material( color: Colors.transparent, child: Container( width: 342, margin: const EdgeInsets.symmetric(horizontal: 24), padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: const [ BoxShadow( color: Color(0x26000000), blurRadius: 24, offset: Offset(0, 8), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Delete Account?', style: AppTypography.bodyLarge.copyWith( fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), GestureDetector( onTap: _deleting ? null : () => Navigator.of(context).pop(), child: const SizedBox( width: 40, height: 40, child: Icon(LucideIcons.x, size: 24, color: AppColors.textMuted), ), ), ], ), const SizedBox(height: 20), Text( 'This action cannot be undone. All your data will be permanently deleted.', style: AppTypography.bodyRegular.copyWith( color: AppColors.textMuted, fontSize: 14, ), ), const SizedBox(height: 20), Text( 'Type $_verifyCode to confirm', style: AppTypography.label.copyWith( color: AppColors.textMuted, fontSize: 12, ), ), const SizedBox(height: 8), TextField( controller: _verifyController, decoration: InputDecoration( hintText: 'Type $_verifyCode here', hintStyle: AppTypography.bodyRegular.copyWith( color: AppColors.textMuted, fontSize: 14, ), filled: true, fillColor: const Color(0xFFFAFAFA), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE4E4E7)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE4E4E7)), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, ), ), style: AppTypography.bodyRegular.copyWith( color: AppColors.textPrimary, fontSize: 14, ), textCapitalization: TextCapitalization.characters, ), if (_errorText != null) ...[ const SizedBox(height: 12), Text( _errorText!, style: AppTypography.caption.copyWith(color: Colors.red), ), ], const SizedBox(height: 20), Row( children: [ Expanded( child: GestureDetector( onTap: _deleting ? null : () => Navigator.of(context).pop(), child: Container( height: 52, alignment: Alignment.center, decoration: BoxDecoration( color: const Color(0xFFF4F4F5), borderRadius: BorderRadius.circular(14), ), child: Text( 'Cancel', style: AppTypography.bodyMedium.copyWith( fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ), ), ), ), const SizedBox(width: 12), Expanded( child: GestureDetector( onTap: (_deleting || !_isVerifyMatch) ? null : _onDelete, child: Container( height: 52, alignment: Alignment.center, decoration: BoxDecoration( color: _isVerifyMatch ? const Color(0xFFDC2626) : Color(0xFFDC2626).withValues(alpha: 0.5), borderRadius: BorderRadius.circular(14), ), child: _deleting ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : Text( 'Delete', style: AppTypography.bodyMedium.copyWith( fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ), ], ), ], ), ), ), ); } }