import 'package:siro_driver/constant/currency.dart'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:siro_driver/constant/box_name.dart'; import 'package:siro_driver/constant/links.dart'; import 'package:siro_driver/controller/functions/crud.dart'; import '../../../main.dart'; // ════════════════════════════════════════════ // نموذج التحدي // ════════════════════════════════════════════ class Challenge { final String id; final String titleEn; final String titleAr; final String descriptionEn; final String descriptionAr; final IconData icon; final Color color; final int target; final int reward; // نقاط final String type; // 'daily' or 'weekly' final String metric; // 'trips', 'earnings', 'hours', 'acceptance_rate' int currentProgress; bool isClaimed; Challenge({ required this.id, required this.titleEn, required this.titleAr, required this.descriptionEn, required this.descriptionAr, required this.icon, required this.color, required this.target, required this.reward, required this.type, required this.metric, this.currentProgress = 0, this.isClaimed = false, }); double get progress => (currentProgress / target).clamp(0.0, 1.0); bool get isCompleted => currentProgress >= target; } // ════════════════════════════════════════════ class ChallengesController extends GetxController { bool isLoading = false; List dailyChallenges = []; List weeklyChallenges = []; @override void onInit() { super.onInit(); _generateChallenges(); _loadProgress(); fetchChallengeProgress(); } void _generateChallenges() { final now = DateTime.now(); final isWeekend = now.weekday == 5 || now.weekday == 6; // الجمعة والسبت String country = box.read(BoxName.countryCode) ?? 'Jordan'; int dailyEarningsTarget; int dailyEarningsReward; int weeklyEarningsTarget; int weeklyEarningsReward; String dailyEarningsDescAr; String dailyEarningsDescEn; String weeklyEarningsDescAr; String weeklyEarningsDescEn; if (country == 'Syria') { dailyEarningsTarget = 150000; dailyEarningsReward = 100; // 100 pts -> 10,000 SYP weeklyEarningsTarget = 1000000; weeklyEarningsReward = 500; // 500 pts -> 50,000 SYP dailyEarningsDescAr = 'اربح 150,000 ل.س اليوم'; dailyEarningsDescEn = 'Earn 150,000 SYP today'; weeklyEarningsDescAr = 'اربح 1,000,000 ل.س هذا الأسبوع'; weeklyEarningsDescEn = 'Earn 1,000,000 SYP this week'; } else if (country == 'Egypt') { dailyEarningsTarget = 300; dailyEarningsReward = 30; // 30 pts -> 30 EGP weeklyEarningsTarget = 2000; weeklyEarningsReward = 200; // 200 pts -> 200 EGP dailyEarningsDescAr = 'اربح 300 ج.م اليوم'; dailyEarningsDescEn = 'Earn 300 EGP today'; weeklyEarningsDescAr = 'اربح 2000 ج.م هذا الأسبوع'; weeklyEarningsDescEn = 'Earn 2000 EGP this week'; } else { // Jordan / default dailyEarningsTarget = 30; dailyEarningsReward = 60; // 60 pts -> 3 JOD (60 * 0.05 = 3 JOD) weeklyEarningsTarget = 200; weeklyEarningsReward = 400; // 400 pts -> 20 JOD (400 * 0.05 = 20 JOD) dailyEarningsDescAr = 'اربح 30 د.أ اليوم'; dailyEarningsDescEn = 'Earn 30 JOD today'; weeklyEarningsDescAr = 'اربح 200 د.أ هذا الأسبوع'; weeklyEarningsDescEn = 'Earn 200 JOD this week'; } dailyChallenges = [ Challenge( id: 'daily_trips_5', titleEn: 'Road Runner', titleAr: 'سائق سريع', descriptionEn: 'Complete 5 trips today', descriptionAr: 'أكمل 5 رحلات اليوم', icon: Icons.local_taxi_rounded, color: const Color(0xFF2196F3), target: 5, reward: country == 'Syria' ? 50 : (country == 'Egypt' ? 50 : 100), type: 'daily', metric: 'trips', ), Challenge( id: 'daily_trips_10', titleEn: 'Marathon Driver', titleAr: 'سائق الماراثون', descriptionEn: 'Complete 10 trips today', descriptionAr: 'أكمل 10 رحلات اليوم', icon: Icons.directions_car_rounded, color: const Color(0xFFFF9800), target: 10, reward: country == 'Syria' ? 150 : (country == 'Egypt' ? 150 : 300), type: 'daily', metric: 'trips', ), Challenge( id: 'daily_earnings', titleEn: 'Money Maker', titleAr: 'صانع المال', descriptionEn: dailyEarningsDescEn, descriptionAr: dailyEarningsDescAr, icon: Icons.monetization_on_rounded, color: const Color(0xFF4CAF50), target: dailyEarningsTarget, reward: dailyEarningsReward, type: 'daily', metric: 'earnings', ), if (isWeekend) Challenge( id: 'daily_weekend_bonus', titleEn: 'Weekend Warrior', titleAr: 'محارب عطلة نهاية الأسبوع', descriptionEn: 'Complete 8 trips on the weekend', descriptionAr: 'أكمل 8 رحلات في عطلة نهاية الأسبوع', icon: Icons.celebration_rounded, color: const Color(0xFFE91E63), target: 8, reward: country == 'Syria' ? 200 : (country == 'Egypt' ? 200 : 400), type: 'daily', metric: 'trips', ), ]; weeklyChallenges = [ Challenge( id: 'weekly_trips_30', titleEn: 'Weekly Champion', titleAr: 'بطل الأسبوع', descriptionEn: 'Complete 30 trips this week', descriptionAr: 'أكمل 30 رحلة هذا الأسبوع', icon: Icons.emoji_events_rounded, color: const Color(0xFFFFD700), target: 30, reward: country == 'Syria' ? 300 : (country == 'Egypt' ? 300 : 600), type: 'weekly', metric: 'trips', ), Challenge( id: 'weekly_earnings', titleEn: 'Big Earner', titleAr: 'الربح الكبير', descriptionEn: weeklyEarningsDescEn, descriptionAr: weeklyEarningsDescAr, icon: Icons.account_balance_wallet_rounded, color: const Color(0xFF9C27B0), target: weeklyEarningsTarget, reward: weeklyEarningsReward, type: 'weekly', metric: 'earnings', ), Challenge( id: 'weekly_hours', titleEn: 'Time Master', titleAr: 'سيد الوقت', descriptionEn: 'Drive for 20 hours this week', descriptionAr: 'اقضِ 20 ساعة في القيادة هذا الأسبوع', icon: Icons.timer_rounded, color: const Color(0xFF00BCD4), target: 20, reward: country == 'Syria' ? 400 : (country == 'Egypt' ? 400 : 800), type: 'weekly', metric: 'hours', ) ]; } void _loadProgress() { final today = DateTime.now().toIso8601String().split('T')[0]; final savedDate = box.read('challenges_date'); if (savedDate != today) { // يوم جديد — إعادة تعيين التحديات اليومية box.write('challenges_date', today); for (var c in dailyChallenges) { box.write('challenge_${c.id}_claimed', false); } } // تحميل حالة المطالبة for (var c in dailyChallenges) { c.isClaimed = box.read('challenge_${c.id}_claimed') ?? false; } for (var c in weeklyChallenges) { c.isClaimed = box.read('challenge_${c.id}_claimed') ?? false; } } Future fetchChallengeProgress() async { isLoading = true; update(); try { // جلب رحلات اليوم var todayRes = await CRUD().getWallet( link: AppLink.getDriverPaymentToday, payload: {'driverID': box.read(BoxName.driverID).toString()}, ); int todayTrips = 0; double todayEarnings = 0; if (todayRes != null && todayRes != 'failure') { var data = jsonDecode(todayRes); todayEarnings = double.tryParse( data['message']?[0]?['todayAmount']?.toString() ?? '0') ?? 0; todayTrips = int.tryParse( data['message']?[0]?['todayCount']?.toString() ?? '0') ?? 0; } // تحديث التحديات اليومية for (var c in dailyChallenges) { switch (c.metric) { case 'trips': c.currentProgress = todayTrips; break; case 'earnings': c.currentProgress = todayEarnings.toInt(); break; } } // 2. Fetch weekly earnings from PAYMENT server var weeklyEarningsRes = await CRUD().getWallet( link: AppLink.getDriverWeekPaymentMove, payload: {'driverID': box.read(BoxName.driverID).toString()}, ); double weeklyEarnings = 0; if (weeklyEarningsRes != null && weeklyEarningsRes != 'failure') { var data = jsonDecode(weeklyEarningsRes); if (data['message'] is List && data['message'].isNotEmpty) { weeklyEarnings = double.tryParse( data['message'][0]['totalAmount']?.toString() ?? '0') ?? 0; } } // 3. Fetch weekly trips and hours from RIDES server (avoiding earnings join) var weeklyAggregateRes = await CRUD().get( link: AppLink.getWeeklyAggregate, payload: {'driver_id': box.read(BoxName.driverID).toString()}, ); int weeklyTrips = 0; double weeklyHours = 0; if (weeklyAggregateRes != null && weeklyAggregateRes != 'failure') { var data = jsonDecode(weeklyAggregateRes); if (data['message'] is List) { for (var day in data['message']) { weeklyTrips += int.tryParse(day['trips']?.toString() ?? '0') ?? 0; weeklyHours += double.tryParse(day['hours']?.toString() ?? '0') ?? 0; } } } for (var c in weeklyChallenges) { switch (c.metric) { case 'trips': c.currentProgress = weeklyTrips; break; case 'earnings': c.currentProgress = weeklyEarnings.toInt(); break; case 'hours': c.currentProgress = weeklyHours.toInt(); break; } } } catch (e) { debugPrint('❌ [Challenges] Error: $e'); } isLoading = false; update(); } final Set _claimingChallenges = {}; bool isClaiming(String challengeId) => _claimingChallenges.contains(challengeId); Future claimReward(Challenge challenge) async { if (!challenge.isCompleted || challenge.isClaimed || _claimingChallenges.contains(challenge.id)) return; _claimingChallenges.add(challenge.id); update(); try { var res = await CRUD().post( link: AppLink.claimChallengeReward, payload: { 'driver_id': box.read(BoxName.driverID).toString(), 'challenge_id': challenge.id, 'points': challenge.reward.toString(), }, ); if (res != null && res != 'failure') { challenge.isClaimed = true; box.write('challenge_${challenge.id}_claimed', true); debugPrint('🎉 Claimed ${challenge.reward} points for ${challenge.id}'); } } catch (e) { debugPrint('❌ [Challenges] Claim error: $e'); } finally { _claimingChallenges.remove(challenge.id); update(); } } }