import 'dart:convert'; import 'package:get/get.dart'; import '../../constant/links.dart'; import '../../views/widgets/error_snakbar.dart'; import '../functions/crud.dart'; /// حالة تأمين السائق: الوثيقة النشطة، ودفتر الأقساط، والخطط المتاحة. /// /// الخطط والحالة نداءان منفصلان في الخادم (plans.php و get.php)، لكنهما /// شاشة واحدة عند السائق — فنجلبهما معاً ونعرض ما اكتمل. فشل أحدهما لا /// يُفرغ الشاشة من الآخر. class DriverInsuranceController extends GetxController { final CRUD _crud = CRUD(); final isLoading = false.obs; final isSubmitting = false.obs; /// الوثيقة النشطة، أو null إن لم يكن مشتركاً. final policy = Rxn>(); /// ملخّص الدفتر: المستحق والمسدَّد. final ledger = {}.obs; /// آخر القيود — السائق الذي يُخصم من أرباحه يجب أن يرى مقابل ماذا. final entries = >[].obs; /// كل الخطط، لا المؤهَّل لها فقط: إخفاء ما لا يستحقه بعد يجعل التأمين /// مجهولاً عنده، وإظهاره مع «ينقصك كذا رحلة» يحوّله إلى هدف. final plans = >[].obs; @override void onInit() { super.onInit(); refreshAll(); } Future refreshAll() async { isLoading.value = true; await Future.wait([_fetchStatus(), _fetchPlans()]); isLoading.value = false; } Future _fetchStatus() async { try { final data = _extract(await _crud.post( link: AppLink.getHealthInsuranceProvider, payload: const {}, )); if (data == null) return; policy.value = data['policy'] is Map ? Map.from(data['policy'] as Map) : null; ledger.assignAll(data['ledger'] is Map ? Map.from(data['ledger'] as Map) : {}); entries.assignAll(((data['entries'] as List?) ?? []) .map((e) => Map.from(e as Map)) .toList()); } catch (_) { mySnackbarError('Failed to load insurance status'.tr); } } Future _fetchPlans() async { try { final data = _extract(await _crud.post( link: AppLink.insurancePlans, payload: const {}, )); if (data == null) return; plans.assignAll(((data['plans'] as List?) ?? []) .map((e) => Map.from(e as Map)) .toList()); } catch (_) { mySnackbarError('Failed to load insurance plans'.tr); } } /// يشترك في خطة. الخادم يعيد فحص الأهلية بنفسه — الشاشة قد تكون /// مفتوحة منذ ساعة، فما عرضناه ليس حجة. Future subscribe(int planId) async { if (isSubmitting.value) return false; isSubmitting.value = true; try { final res = await _crud.post( link: AppLink.insuranceSubscribe, payload: {'plan_id': planId.toString()}, ); final decoded = res is String ? jsonDecode(res) : res; if (decoded is Map && decoded['status'] == 'success') { await refreshAll(); return true; } // رسالة الخادم تحمل سبب الرفض بالتفصيل (ما ينقص السائق تحديداً)، // فنعرضها كما هي بدل رسالة عامة لا تدلّه على شيء. mySnackbarError(_errorOf(decoded) ?? 'Subscription failed'.tr); return false; } catch (_) { mySnackbarError('Subscription failed'.tr); return false; } finally { isSubmitting.value = false; } } Future cancel({String reason = ''}) async { if (isSubmitting.value) return false; isSubmitting.value = true; try { final res = await _crud.post( link: AppLink.insuranceCancel, payload: {'reason': reason}, ); final decoded = res is String ? jsonDecode(res) : res; if (decoded is Map && decoded['status'] == 'success') { await refreshAll(); return true; } mySnackbarError(_errorOf(decoded) ?? 'Cancellation failed'.tr); return false; } catch (_) { mySnackbarError('Cancellation failed'.tr); return false; } finally { isSubmitting.value = false; } } bool get hasPolicy => policy.value != null; double get pendingTotal => double.tryParse('${ledger['pending_total'] ?? 0}') ?? 0; String get currency => '${ledger['currency'] ?? policy.value?['currency'] ?? ''}'; Map? _extract(dynamic res) { final decoded = res is String ? jsonDecode(res) : res; if (decoded is! Map || decoded['status'] != 'success') return null; final data = decoded['message'] ?? decoded['data']; return data is Map ? Map.from(data) : {}; } String? _errorOf(dynamic decoded) { if (decoded is! Map) return null; final m = decoded['message'] ?? decoded['error']; return m is String && m.isNotEmpty ? m : null; } }