// lib/screens/challenges_screen.dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:google_fonts/google_fonts.dart'; import '../models/app_models.dart'; import '../services/firebase_service.dart'; import '../services/i18n_service.dart'; // KzEduca Palette const _kGradientStart = Color(0xFF512DA8); // Roxo vibrante const _kGradientEnd = Color(0xFF000000); // Preto profundo const _kAccent = Color(0xFF00BFA5); // Verde água const _kAction = Color(0xFFFFD600); // Amarelo/dourado class ChallengesScreen extends StatefulWidget { const ChallengesScreen({Key? key}) : super(key: key); @override State createState() => _ChallengesScreenState(); } class _ChallengesScreenState extends State { List _publicChallenges = []; List _userChallenges = []; FinancialChallenge? _dailyChallenge; bool _isLoading = true; String _error = ''; @override void initState() { super.initState(); _loadAll(); } Future _loadAll() async { setState(() { _isLoading = true; _error = ''; }); final service = Provider.of(context, listen: false); final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) { setState(() { _error = 'Usuário não autenticado.'; _isLoading = false; }); return; } try { // 1) Carrega todos os desafios públicos _publicChallenges = await service.getAllChallenges(); // 2) Carrega desafios aceitos pelo usuário e converte Timestamp para String final snapshot = await service.streamUserChallenges(uid).first; _userChallenges = snapshot.docs.map((doc) { final data = doc.data() as Map; // acceptedAt pode vir como Timestamp ou String final rawAccepted = data['acceptedAt']; final acceptedAtStr = rawAccepted is Timestamp ? rawAccepted.toDate().toIso8601String() : (rawAccepted?.toString() ?? ''); // lastUpdated pode vir como Timestamp, String ou nulo final rawLast = data['lastUpdated']; final lastUpdatedStr = rawLast is Timestamp ? rawLast.toDate().toIso8601String() : rawLast?.toString(); return UserChallenge( id: doc.id, challengeId: data['challengeId'] ?? '', nome: data['nome'] ?? '', meta: data['meta'] ?? '', progresso: (data['progresso'] as num?)?.toInt() ?? 0, acceptedAt: acceptedAtStr, lastUpdated: lastUpdatedStr, ); }).toList(); // 3) Escolhe um desafio do dia aleatório entre os não-aceitos final remaining = _publicChallenges.where( (c) => !_userChallenges.any((uc) => uc.challengeId == c.id), ).toList(); if (remaining.isNotEmpty) { _dailyChallenge = remaining[Random().nextInt(remaining.length)]; } setState(() { _isLoading = false; }); } catch (e) { setState(() { _error = 'Erro ao carregar desafios: $e'; _isLoading = false; }); } } Future _acceptChallenge(FinancialChallenge challenge) async { final service = Provider.of(context, listen: false); final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return; await service.acceptChallenge(uid, { 'challengeId': challenge.id, 'nome': challenge.nome, 'meta': challenge.meta, 'progresso': 0, 'acceptedAt': DateTime.now().toIso8601String(), }); await _loadAll(); if (mounted) { final msg = Provider.of(context, listen: false) .t('challenge_accepted'); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(msg)), ); } } Future _updateProgress(UserChallenge uc) async { final service = Provider.of(context, listen: false); final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return; int newProgress = uc.progresso; await showModalBottomSheet( context: context, backgroundColor: Colors.grey[900], shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (ctx) { return StatefulBuilder(builder: (ctx, setSheet) { return Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( uc.nome, style: GoogleFonts.montserrat( color: Colors.white, fontSize: 20, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), Text( Provider.of(context, listen: false) .t('update_progress'), style: GoogleFonts.montserrat(color: Colors.white70), ), Slider( value: newProgress.toDouble(), min: 0, max: 100, divisions: 20, activeColor: _kAccent, inactiveColor: Colors.white12, label: '$newProgress%', onChanged: (v) => setSheet(() => newProgress = v.round()), ), const SizedBox(height: 16), ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Colors.black87, backgroundColor: _kAction, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: () async { await service.updateChallengeProgress( uid, uc.id, newProgress); Navigator.pop(ctx); await _loadAll(); }, child: Text( Provider.of(context, listen: false).t('save'), style: GoogleFonts.montserrat(fontWeight: FontWeight.w600), ), ), const SizedBox(height: 16), ], ), ); }); }, ); } @override Widget build(BuildContext context) { final i18n = Provider.of(context); return Scaffold( backgroundColor: Colors.black, body: Container( decoration: const BoxDecoration( gradient: LinearGradient( colors: [_kGradientStart, _kGradientEnd], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: SafeArea( child: _isLoading ? const Center(child: CircularProgressIndicator(color: _kAccent)) : _error.isNotEmpty ? Center( child: Text( _error, style: GoogleFonts.montserrat( color: Colors.redAccent, fontSize: 16, ), ), ) : SingleChildScrollView( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( children: [ IconButton( icon: const Icon(Icons.arrow_back_rounded, color: Colors.white), onPressed: () => Navigator.pop(context), ), const SizedBox(width: 8), Text( i18n.t('challenges_title'), style: GoogleFonts.montserrat( color: Colors.white, fontSize: 28, fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: 24), // Daily Challenge if (_dailyChallenge != null) ...[ _DailyCard( challenge: _dailyChallenge!, onAccept: () => _acceptChallenge(_dailyChallenge!), ), const SizedBox(height: 32), ], // Public Challenges Text( i18n.t('public_challenges_title'), style: GoogleFonts.montserrat( color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), Column( children: _publicChallenges .where((c) => c.id != _dailyChallenge?.id) .map((c) => _PublicCard( challenge: c, onAccept: () => _acceptChallenge(c), )) .toList(), ), const SizedBox(height: 32), // My Challenges Text( i18n.t('accepted_challenges_title'), style: GoogleFonts.montserrat( color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 16), if (_userChallenges.isEmpty) _EmptyState( icon: Icons.hourglass_empty_rounded, message: i18n.t('no_accepted_challenges'), ) else Column( children: _userChallenges .map((uc) => _ProgressCard( uc: uc, onTap: () => _updateProgress(uc), )) .toList(), ), const SizedBox(height: 40), ], ), ), ), ), ); } } // Daily Challenge Card class _DailyCard extends StatelessWidget { final FinancialChallenge challenge; final VoidCallback onAccept; const _DailyCard({ required this.challenge, required this.onAccept, Key? key, }) : super(key: key); @override Widget build(BuildContext c) { final i18n = Provider.of(c, listen: false); return Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( gradient: const LinearGradient(colors: [_kAccent, _kGradientStart]), borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.5), blurRadius: 14, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( i18n.t('daily_challenge_new'), style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 14, letterSpacing: 1.2, ), ), const SizedBox(height: 8), Text( challenge.nome, style: GoogleFonts.montserrat( color: Colors.white, fontSize: 26, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 12), Text( challenge.meta, style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 16, height: 1.4, ), ), const SizedBox(height: 24), Center( child: ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Colors.black87, backgroundColor: _kAction, padding: const EdgeInsets.symmetric( horizontal: 36, vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: onAccept, child: Text( i18n.t('accept_challenge'), style: GoogleFonts.montserrat( fontWeight: FontWeight.w600, fontSize: 16, ), ), ), ), ], ), ); } } // Public Challenge Card class _PublicCard extends StatelessWidget { final FinancialChallenge challenge; final VoidCallback onAccept; const _PublicCard({ required this.challenge, required this.onAccept, Key? key, }) : super(key: key); @override Widget build(BuildContext c) { final i18n = Provider.of(c, listen: false); return Container( margin: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.grey[850], borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 6), ) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(challenge.nome, style: GoogleFonts.montserrat( color: Colors.white, fontSize: 20, fontWeight: FontWeight.w600, )), const SizedBox(height: 8), Text(challenge.meta, style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 16, height: 1.4, )), const SizedBox(height: 16), Align( alignment: Alignment.centerRight, child: TextButton( style: TextButton.styleFrom( backgroundColor: _kAccent, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: onAccept, child: Text( i18n.t('accept_challenge'), style: GoogleFonts.montserrat( color: Colors.black87, fontWeight: FontWeight.w600, ), ), ), ), ], ), ); } } // Progress Challenge Card with circular indicator class _ProgressCard extends StatelessWidget { final UserChallenge uc; final VoidCallback onTap; const _ProgressCard({ required this.uc, required this.onTap, Key? key, }) : super(key: key); @override Widget build(BuildContext c) { final progress = (uc.progresso / 100).clamp(0.0, 1.0); return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.grey[850], borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 6), ) ], ), child: Row( children: [ CustomPaint( painter: _CircleProgressPainter(progress, _kAccent), size: const Size(60, 60), child: Center( child: Text('${uc.progresso}%', style: GoogleFonts.montserrat( color: Colors.white, fontWeight: FontWeight.w600, )), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(uc.nome, style: GoogleFonts.montserrat( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, )), const SizedBox(height: 4), Text(uc.meta, style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 14, height: 1.3, )), ], ), ), const Icon(Icons.edit, color: Colors.white54), ], ), ), ); } } class _CircleProgressPainter extends CustomPainter { final double progress; final Color color; _CircleProgressPainter(this.progress, this.color); @override void paint(Canvas canvas, Size size) { final strokeWidth = 6.0; final center = Offset(size.width / 2, size.height / 2); final radius = (size.width - strokeWidth) / 2; final bgPaint = Paint() ..color = Colors.white12 ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth; final fgPaint = Paint() ..color = color ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round ..strokeWidth = strokeWidth; canvas.drawCircle(center, radius, bgPaint); final sweep = 2 * pi * progress; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -pi / 2, sweep, false, fgPaint, ); } @override bool shouldRepaint(covariant _CircleProgressPainter old) => old.progress != progress || old.color != color; } // Empty State Widget class _EmptyState extends StatelessWidget { final IconData icon; final String message; const _EmptyState({ required this.icon, required this.message, Key? key, }) : super(key: key); @override Widget build(BuildContext c) { return Column( children: [ const SizedBox(height: 40), Icon(icon, size: 80, color: Colors.white24), const SizedBox(height: 20), Text( message, style: GoogleFonts.montserrat( color: Colors.white54, fontSize: 18, fontStyle: FontStyle.italic, ), textAlign: TextAlign.center, ), ], ); } }