import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../core/services/chit_group_service.dart'; import '../../core/models/chit_group.dart'; import '../../shared/widgets/draw_animation_selector.dart'; import '../../core/utils/whatsapp_util.dart'; class DrawAnimationPage extends StatefulWidget { final ChitGroup group; final int month; final int year; final String serverSeed; final int nonce; final List> eligibleMembers; const DrawAnimationPage({ super.key, required this.group, required this.month, required this.year, required this.serverSeed, required this.nonce, required this.eligibleMembers, }); @override State createState() => _DrawAnimationPageState(); } class _DrawAnimationPageState extends State with SingleTickerProviderStateMixin { late AnimationController _fadeController; late Animation _fadeAnimation; bool _isComplete = false; @override void initState() { super.initState(); _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _fadeController, curve: Curves.easeInOut, )); _fadeController.forward(); } @override void dispose() { _fadeController.dispose(); super.dispose(); } void _onDrawComplete(String winnerId) async { if (_isComplete) return; setState(() { _isComplete = true; }); // Wait a moment to show the winner await Future.delayed(const Duration(seconds: 2)); // Find winner (avoid firstWhere/orElse RTI mismatch Map vs dynamic on web) Map winner = {'name': 'Unknown', 'mobile': ''}; for (final raw in widget.eligibleMembers) { final m = Map.from(raw); if (m['id'] == winnerId) { winner = m; break; } } // Get the bid amount for this month from financial data final chitGroupService = Get.find(); await chitGroupService.loadGroupFinancialData(widget.group.id); // Find the financial data for this specific month final monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; final monthKey = '${monthNames[widget.month - 1]}-${widget.year.toString().substring(2)}'; final financialEntry = chitGroupService.financialData.firstWhereOrNull( (entry) => entry.monthYear == monthKey, ); final bidAmount = financialEntry?.bidAmount ?? widget.group.monthlyInstallment; // Show confirmation dialog with winner and bid amount final shouldSave = await _showWinnerConfirmation(winner, bidAmount); if (shouldSave == true) { await _saveDrawResult(winnerId, bidAmount); } else { // User cancelled, go back without saving Get.back(result: false); } } Future _showWinnerConfirmation(Map winner, double bidAmount) async { return showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), title: Row( children: [ Icon(Icons.emoji_events, color: Colors.amber.shade600, size: 32.w), SizedBox(width: 12.w), Expanded( child: Text( 'Draw Winner!', style: TextStyle( fontSize: 22.sp, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), ), ], ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsets.all(20.w), decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: Colors.green.shade200, width: 2), ), child: Column( children: [ Text( winner['name'] ?? 'Unknown', style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.green.shade900, ), textAlign: TextAlign.center, ), SizedBox(height: 8.h), Text( winner['mobile'] ?? '', style: TextStyle( fontSize: 16.sp, color: Colors.grey.shade700, ), ), SizedBox(height: 16.h), Divider(color: Colors.green.shade200), SizedBox(height: 16.h), Text( 'Prize Amount', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Text( _formatIndianCurrency(bidAmount), style: TextStyle( fontSize: 32.sp, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), ], ), ), SizedBox(height: 16.h), // Quick Actions - Share & Screenshot Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: () => _shareViaWhatsApp(winner, bidAmount), icon: Icon(Icons.send_rounded, size: 18.w, color: Color(0xFF25D366)), label: Text('Share', style: TextStyle(fontSize: 13.sp, color: Color(0xFF25D366))), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 10.h), side: BorderSide(color: Color(0xFF25D366)), ), ), ), SizedBox(width: 8.w), Expanded( child: OutlinedButton.icon( onPressed: () { Get.snackbar( 'Screenshot Tip', 'Take a screenshot now to save this result!', backgroundColor: Colors.blue, colorText: Colors.white, duration: Duration(seconds: 3), ); }, icon: Icon(Icons.screenshot, size: 18.w, color: Colors.blue.shade600), label: Text('Capture', style: TextStyle(fontSize: 13.sp, color: Colors.blue.shade600)), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 10.h), side: BorderSide(color: Colors.blue.shade600), ), ), ), ], ), SizedBox(height: 20.h), Text( 'Do you want to save this draw result?', style: TextStyle( fontSize: 16.sp, color: Colors.grey.shade700, fontWeight: FontWeight.w600, ), textAlign: TextAlign.center, ), ], ), actionsAlignment: MainAxisAlignment.end, actionsPadding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h), actions: [ // Cancel button OutlinedButton( onPressed: () => Navigator.pop(context, false), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 14.h), side: BorderSide(color: Colors.grey.shade400), ), child: Text( 'Cancel', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), ), SizedBox(width: 12.w), // Save button ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 14.h), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.save, size: 20.w), SizedBox(width: 8.w), Text( 'Save Result', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), ], ), ), ], ), ); } void _shareViaWhatsApp(Map winner, double bidAmount) { final monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; final monthName = monthNames[widget.month - 1]; final message = ''' 🎉 *${widget.group.name}* 🎉 📅 *Monthly Draw Result* Month: $monthName ${widget.year} 🏆 *WINNER* ${winner['name']} ${winner['mobile']} 💰 *Prize Amount* ${_formatIndianCurrency(bidAmount)} ✨ This draw was conducted using our provably fair system for complete transparency. _Congratulations to the winner!_ '''; // Share to all members or specific number WhatsAppUtil.shareText(message); } Future _saveDrawResult(String winnerId, double bidAmount) async { final chitGroupService = Get.find(); try { // Show loading Get.dialog( WillPopScope( onWillPop: () async => false, child: Center( child: Container( padding: EdgeInsets.all(32.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16.r), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator(), SizedBox(height: 16.h), Text( 'Saving draw result...', style: TextStyle(fontSize: 16.sp), ), ], ), ), ), ), barrierDismissible: false, ); final created = await chitGroupService.createMonthlyDraw( widget.group.id, widget.month, widget.year, clientSeed: 'DRAW_${DateTime.now().millisecondsSinceEpoch}', prizeAmount: bidAmount, ); if (!mounted) return; Get.back(); if (created == null) { Get.back(result: false); Get.snackbar( 'Error', 'Failed to save draw result', backgroundColor: Colors.red, colorText: Colors.white, duration: const Duration(seconds: 4), snackPosition: SnackPosition.TOP, ); return; } final drawId = created['id']?.toString(); if (drawId != null) { final publicUrl = await WhatsAppUtil.getDrawPublicShareUrl(drawId); if (mounted && publicUrl != null && publicUrl.isNotEmpty) { await _showPublicResultLinkDialog(publicUrl); } else if (mounted && (publicUrl == null || publicUrl.isEmpty)) { Get.snackbar( 'Draw saved', 'Public link unavailable — set PUBLIC_BASE_URL on the server to share results.', backgroundColor: Colors.orange.shade700, colorText: Colors.white, duration: const Duration(seconds: 5), snackPosition: SnackPosition.TOP, ); } } if (!mounted) return; Get.back(result: true); Get.snackbar( 'Draw Saved! 🎉', 'Winner has been recorded. Share the public link so anyone can view results.', backgroundColor: Colors.green, colorText: Colors.white, duration: const Duration(seconds: 3), snackPosition: SnackPosition.TOP, ); } catch (e) { if (mounted) Get.back(); Get.back(result: false); Get.snackbar( 'Error', 'Failed to save draw result: ${e.toString()}', backgroundColor: Colors.red, colorText: Colors.white, duration: const Duration(seconds: 4), ); } } Future _showPublicResultLinkDialog(String url) async { await showDialog( context: context, barrierDismissible: false, builder: (ctx) => AlertDialog( title: const Text('Public result link'), content: SelectableText( url, style: TextStyle(fontSize: 13.sp), ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('Close'), ), FilledButton( onPressed: () async { await Clipboard.setData(ClipboardData(text: url)); if (ctx.mounted) Navigator.of(ctx).pop(); Get.snackbar( 'Copied', 'Link copied — anyone can open it in a browser', snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 3), ); }, child: const Text('Copy link'), ), ], ), ); } String _formatIndianCurrency(double amount) { int intAmount = amount.round(); String amountStr = intAmount.toString(); String formatted = ''; if (amountStr.length <= 3) { formatted = amountStr; } else { int remaining = amountStr.length; int start = 0; if (remaining > 3) { formatted = amountStr.substring(amountStr.length - 3); remaining -= 3; start = amountStr.length - 3; } else { formatted = amountStr; remaining = 0; } while (remaining > 0) { int groupSize = remaining >= 2 ? 2 : remaining; int groupStart = start - groupSize; String group = amountStr.substring(groupStart, start); formatted = group + ',' + formatted; start = groupStart; remaining -= groupSize; } } return '₹$formatted'; } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { // Prevent back button during animation if (_isComplete) return true; final result = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Cancel Draw?'), content: const Text('Are you sure you want to cancel the draw? This action cannot be undone.'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Continue Draw'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Cancel Draw'), ), ], ), ); return result ?? false; }, child: Scaffold( backgroundColor: Colors.black, body: SafeArea( child: FadeTransition( opacity: _fadeAnimation, child: Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Colors.purple.shade900, Colors.blue.shade900, Colors.indigo.shade900, ], ), ), child: Column( children: [ // Header Padding( padding: EdgeInsets.all(20.w), child: Column( children: [ Row( children: [ if (!_isComplete) IconButton( icon: Icon( Icons.close, color: Colors.white.withOpacity(0.8), size: 28.w, ), onPressed: () async { final result = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Cancel Draw?'), content: const Text('Are you sure you want to cancel the draw?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Continue'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Cancel'), ), ], ), ); if (result == true) { Get.back(result: false); } }, ) else SizedBox(width: 48.w), Expanded( child: Column( children: [ Text( widget.group.name, style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.white, ), textAlign: TextAlign.center, ), SizedBox(height: 8.h), Container( padding: EdgeInsets.symmetric( horizontal: 16.w, vertical: 6.h, ), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20.r), border: Border.all( color: Colors.white.withOpacity(0.3), ), ), child: Text( 'Month ${widget.month}/${widget.year}', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ], ), ), SizedBox(width: 48.w), ], ), ], ), ), // Animation Area - Scrollable Expanded( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), child: DrawAnimationSelector( members: widget.eligibleMembers, onDrawComplete: _onDrawComplete, serverSeed: widget.serverSeed, clientSeed: 'DRAW_${DateTime.now().millisecondsSinceEpoch}', nonce: widget.nonce, animationDuration: const Duration(seconds: 8), ), ), ), // Footer Padding( padding: EdgeInsets.all(20.w), child: Column( children: [ Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), border: Border.all( color: Colors.white.withOpacity(0.2), ), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildInfoItem( 'Eligible Members', '${widget.eligibleMembers.length}', Icons.people, ), Container( width: 1, height: 30.h, color: Colors.white.withOpacity(0.3), ), _buildInfoItem( 'Total Members', '${widget.group.maxMembers}', Icons.group, ), ], ), ], ), ), SizedBox(height: 12.h), Text( '🎲 Provably Fair Draw', style: TextStyle( fontSize: 14.sp, color: Colors.white.withOpacity(0.7), fontWeight: FontWeight.w500, ), ), ], ), ), ], ), ), ), ), ), ); } Widget _buildInfoItem(String label, String value, IconData icon) { return Column( children: [ Icon( icon, color: Colors.white.withOpacity(0.8), size: 24.w, ), SizedBox(height: 6.h), Text( value, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), Text( label, style: TextStyle( fontSize: 12.sp, color: Colors.white.withOpacity(0.7), ), ), ], ); } }