import 'package:flutter/material.dart'; import 'package:geosector_app/core/data/models/passage_model.dart'; import 'package:geosector_app/core/services/stripe_connect_service.dart'; import 'package:geosector_app/core/services/device_info_service.dart'; import 'package:geosector_app/core/utils/api_exception.dart'; import 'package:geosector_app/core/repositories/passage_repository.dart'; import 'package:geosector_app/presentation/widgets/qr_code_payment_dialog.dart'; /// Dialog de sélection de la méthode de paiement CB /// Affiche les options QR Code et/ou Tap to Pay selon la compatibilité class PaymentMethodSelectionDialog extends StatelessWidget { final PassageModel passage; final double amount; final String habitantName; final StripeConnectService stripeConnectService; final PassageRepository? passageRepository; final VoidCallback? onTapToPaySelected; const PaymentMethodSelectionDialog({ super.key, required this.passage, required this.amount, required this.habitantName, required this.stripeConnectService, this.passageRepository, this.onTapToPaySelected, }); @override Widget build(BuildContext context) { final canUseTapToPay = DeviceInfoService.instance.canUseTapToPay(); final amountEuros = amount.toStringAsFixed(2); return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( constraints: const BoxConstraints(maxWidth: 450), padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ // En-tête Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Règlement CB', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), ], ), const SizedBox(height: 24), // Informations du paiement Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.shade200), ), child: Column( children: [ Row( children: [ const Icon(Icons.person, color: Colors.blue), const SizedBox(width: 12), Expanded( child: Text( habitantName, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), ), ), ], ), const Divider(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.euro, color: Colors.blue, size: 28, ), const SizedBox(width: 8), Text( amountEuros, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: Colors.blue, ), ), ], ), ], ), ), const SizedBox(height: 32), // Titre section méthodes const Text( 'Sélectionnez une méthode de paiement :', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 16), // Bouton QR Code _buildPaymentButton( context: context, icon: Icons.qr_code_2, label: 'Paiement par QR Code', description: 'Le client scanne le code avec son téléphone', onPressed: () => _handleQRCodePayment(context), color: Colors.blue, ), if (canUseTapToPay) ...[ const SizedBox(height: 12), // Bouton Tap to Pay _buildPaymentButton( context: context, icon: Icons.contactless, label: 'Tap to Pay', description: 'Paiement sans contact sur cet appareil', onPressed: () { Navigator.of(context).pop(); onTapToPaySelected?.call(); }, color: Colors.green, ), ], const SizedBox(height: 24), // Logo Stripe Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.security, color: Colors.grey.shade600, size: 16, ), const SizedBox(width: 8), Text( 'Paiements sécurisés par Stripe', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, fontStyle: FontStyle.italic, ), ), ], ), ], ), ), ); } Widget _buildPaymentButton({ required BuildContext context, required IconData icon, required String label, required String description, required VoidCallback onPressed, required Color color, }) { return InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(12), child: Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color.withOpacity(0.1), border: Border.all(color: color.withOpacity(0.3), width: 2), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: color, size: 32), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 4), Text( description, style: TextStyle( fontSize: 13, color: Colors.grey.shade700, ), ), ], ), ), Icon(Icons.arrow_forward_ios, color: color, size: 20), ], ), ), ); } /// Gérer le paiement par QR Code Future _handleQRCodePayment(BuildContext context) async { // Sauvegarder le navigator avant de fermer les dialogs final navigator = Navigator.of(context); try { // Afficher un loader showDialog( context: context, barrierDismissible: false, builder: (context) => const Center( child: CircularProgressIndicator(), ), ); // Créer le Payment Link final amountInCents = (amount * 100).round(); debugPrint('🔵 Création Payment Link : ${amountInCents} cents, passage ${passage.id}'); final paymentLink = await stripeConnectService.createPaymentLink( amountInCents: amountInCents, passageId: passage.id, description: 'Calendrier pompiers - ${habitantName}', metadata: { 'passage_id': passage.id.toString(), 'habitant_name': habitantName, 'adresse': '${passage.numero} ${passage.rue}, ${passage.ville}', }, ); debugPrint('🔵 Payment Link reçu : ${paymentLink != null ? "OK" : "NULL"}'); if (paymentLink != null) { debugPrint(' URL: ${paymentLink.url}'); debugPrint(' ID: ${paymentLink.paymentLinkId}'); } // Fermer le loader navigator.pop(); debugPrint('🔵 Loader fermé'); if (paymentLink == null) { throw Exception('Impossible de créer le lien de paiement'); } // Sauvegarder l'URL du Payment Link dans le passage if (passageRepository != null) { try { debugPrint('🔵 Sauvegarde de l\'URL du Payment Link dans le passage...'); final updatedPassage = passage.copyWith( stripePaymentLinkUrl: paymentLink.url, ); await passageRepository!.updatePassage(updatedPassage); debugPrint('✅ URL du Payment Link sauvegardée'); } catch (e) { debugPrint('⚠️ Erreur lors de la sauvegarde de l\'URL: $e'); // On continue quand même, ce n'est pas bloquant } } // Fermer le dialog de sélection navigator.pop(); debugPrint('🔵 Dialog de sélection fermé'); // Attendre un frame pour que les dialogs soient bien fermés await Future.delayed(const Duration(milliseconds: 100)); // Afficher le QR Code avec le navigator root debugPrint('🔵 Ouverture dialog QR Code...'); await showDialog( context: navigator.context, barrierDismissible: true, builder: (context) => QRCodePaymentDialog( paymentLink: paymentLink, ), ); debugPrint('🔵 Dialog QR Code affiché'); } catch (e, stack) { debugPrint('❌ Erreur dans _handleQRCodePayment: $e'); debugPrint(' Stack: $stack'); // Fermer le loader si encore ouvert try { navigator.pop(); } catch (_) {} // Afficher l'erreur if (context.mounted) { ApiException.showError(context, e); } } } /// Afficher le dialog de sélection de méthode de paiement static Future show({ required BuildContext context, required PassageModel passage, required double amount, required String habitantName, required StripeConnectService stripeConnectService, PassageRepository? passageRepository, VoidCallback? onTapToPaySelected, }) { return showDialog( context: context, barrierDismissible: true, builder: (context) => PaymentMethodSelectionDialog( passage: passage, amount: amount, habitantName: habitantName, stripeConnectService: stripeConnectService, passageRepository: passageRepository, onTapToPaySelected: onTapToPaySelected, ), ); } }