import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:mek_stripe_terminal/mek_stripe_terminal.dart';

import 'api_service.dart';
import 'device_info_service.dart';
import 'current_user_service.dart';
import 'current_amicale_service.dart';

/// Service pour gérer les paiements Tap to Pay avec Stripe
/// Version simplifiée qui s'appuie sur l'API backend
class StripeTapToPayService {
  static final StripeTapToPayService instance = StripeTapToPayService._internal();
  StripeTapToPayService._internal();

  bool _isInitialized = false;
  static bool _terminalInitialized = false; // Flag STATIC pour savoir si Terminal.initTerminal a été appelé (global à l'app)
  String? _stripeAccountId;
  String? _locationId;
  bool _deviceCompatible = false;

  // Stream controllers pour les événements de paiement
  final _paymentStatusController = StreamController<TapToPayStatus>.broadcast();

  // Getters publics
  bool get isInitialized => _isInitialized;
  bool get isDeviceCompatible => _deviceCompatible;
  Stream<TapToPayStatus> get paymentStatusStream => _paymentStatusController.stream;

  /// Initialise le service Tap to Pay
  Future<bool> initialize() async {
    if (_isInitialized) {
      debugPrint('ℹ️ StripeTapToPayService déjà initialisé');
      return true;
    }

    try {
      debugPrint('🚀 Initialisation de Tap to Pay...');

      // 1. Vérifier que l'utilisateur est connecté
      if (!CurrentUserService.instance.isLoggedIn) {
        debugPrint('❌ Utilisateur non connecté');
        return false;
      }

      // 2. Vérifier que l'amicale a Stripe activé
      final amicale = CurrentAmicaleService.instance.currentAmicale;
      if (amicale == null) {
        debugPrint('❌ Aucune amicale sélectionnée');
        return false;
      }

      if (!amicale.chkStripe || amicale.stripeId.isEmpty) {
        debugPrint('❌ L\'amicale n\'a pas de compte Stripe configuré');
        return false;
      }

      _stripeAccountId = amicale.stripeId;
      _locationId = amicale.stripeLocationId;

      // Vérifier que la Location existe
      if (_locationId == null || _locationId!.isEmpty) {
        debugPrint('❌ Aucune Location Stripe Terminal configurée pour cette amicale');
        debugPrint('   La Location doit être créée lors de l\'onboarding Stripe Connect');
        _paymentStatusController.add(TapToPayStatus(
          type: TapToPayStatusType.error,
          message: 'Location Stripe non configurée',
        ));
        return false;
      }

      // 3. Vérifier la compatibilité de l'appareil
      _deviceCompatible = DeviceInfoService.instance.canUseTapToPay();
      if (!_deviceCompatible) {
        debugPrint('⚠️ Cet appareil ne supporte pas Tap to Pay');
        _paymentStatusController.add(TapToPayStatus(
          type: TapToPayStatusType.error,
          message: 'Appareil non compatible avec Tap to Pay',
        ));
        return false;
      }

      // 4. Initialiser le SDK Stripe Terminal (une seule fois par session app)
      if (!_terminalInitialized) {
        try {
          debugPrint('🔧 Initialisation du SDK Stripe Terminal...');
          await Terminal.initTerminal(
            fetchToken: _fetchConnectionToken,
          );
          _terminalInitialized = true;
          debugPrint('✅ SDK Stripe Terminal initialisé');
        } catch (e) {
          final errorMsg = e.toString().toLowerCase();
          debugPrint('🔍 Exception capturée lors de l\'initialisation: $e');
          debugPrint('🔍 Type d\'exception: ${e.runtimeType}');

          // Vérifier plusieurs variantes du message "already initialized"
          if (errorMsg.contains('already initialized') ||
              errorMsg.contains('already been initialized') ||
              errorMsg.contains('sdkfailure')) {
            debugPrint('ℹ️ SDK Stripe Terminal déjà initialisé (détecté via exception)');
            _terminalInitialized = true;
            // Ne PAS rethrow - continuer normalement car c'est un état valide
          } else {
            debugPrint('❌ Erreur inattendue lors de l\'initialisation du SDK');
            rethrow; // Autre erreur, on la propage
          }
        }
      } else {
        debugPrint('ℹ️ SDK Stripe Terminal déjà initialisé, réutilisation');
      }

      _isInitialized = true;
      debugPrint('✅ Tap to Pay initialisé avec succès');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.ready,
        message: 'Tap to Pay prêt',
      ));

      return true;

    } catch (e) {
      debugPrint('❌ Erreur lors de l\'initialisation: $e');
      _isInitialized = false;

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.error,
        message: 'Erreur d\'initialisation: $e',
      ));

      return false;
    }
  }

  /// Callback pour récupérer un token de connexion depuis l'API
  Future<String> _fetchConnectionToken() async {
    try {
      debugPrint('🔑 Récupération du token de connexion Stripe...');

      final response = await ApiService.instance.post(
        '/stripe/terminal/connection-token',
        data: {
          'amicale_id': CurrentAmicaleService.instance.amicaleId,
          'stripe_account': _stripeAccountId,
          'location_id': _locationId,
        }
      );

      final token = response.data['secret'];
      if (token == null || token.isEmpty) {
        throw Exception('Token de connexion invalide');
      }

      debugPrint('✅ Token de connexion récupéré');
      return token;

    } catch (e) {
      debugPrint('❌ Erreur récupération token: $e');
      throw Exception('Impossible de récupérer le token de connexion');
    }
  }

  /// Crée un PaymentIntent pour un paiement Tap to Pay
  Future<PaymentIntentResult?> createPaymentIntent({
    required int amountInCents,
    String? description,
    Map<String, dynamic>? metadata,
  }) async {
    if (!_isInitialized) {
      debugPrint('❌ Service non initialisé');
      return null;
    }

    try {
      debugPrint('💰 Création PaymentIntent pour ${amountInCents / 100}€...');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.processing,
        message: 'Préparation du paiement...',
      ));

      // Créer le PaymentIntent via l'API
      // Extraire passage_id des metadata si présent
      final passageId = metadata?['passage_id'] ?? '0';

      final requestData = {
        'amount': amountInCents,
        'currency': 'eur',
        'description': description ?? 'Calendrier pompiers',
        'payment_method_types': ['card_present'],  // Pour Tap to Pay
        'capture_method': 'automatic',
        'passage_id': int.tryParse(passageId.toString()) ?? 0,
        'amicale_id': CurrentAmicaleService.instance.amicaleId,
        'member_id': CurrentUserService.instance.userId,
        'stripe_account': _stripeAccountId,
        'location_id': _locationId,
        'metadata': metadata,
      };

      debugPrint('🔵 Données envoyées create-intent: $requestData');

      final response = await ApiService.instance.post(
        '/stripe/payments/create-intent',
        data: requestData,
      );

      final result = PaymentIntentResult(
        paymentIntentId: response.data['payment_intent_id'],
        clientSecret: response.data['client_secret'],
        amount: amountInCents,
      );

      debugPrint('✅ PaymentIntent créé: ${result.paymentIntentId}');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.awaitingTap,
        message: 'Présentez la carte',
        paymentIntentId: result.paymentIntentId,
      ));

      return result;

    } catch (e) {
      debugPrint('❌ Erreur création PaymentIntent: $e');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.error,
        message: 'Erreur: $e',
      ));

      return null;
    }
  }

  /// Découvre et connecte le reader Tap to Pay local
  Future<bool> _ensureReaderConnected() async {
    try {
      debugPrint('🔍 Découverte du reader Tap to Pay...');

      // Configuration pour découvrir le reader local (Tap to Pay)
      // Détection de l'environnement via l'URL de l'API (plus fiable que kDebugMode)
      final apiUrl = ApiService.instance.baseUrl;
      final isProduction = apiUrl.contains('app3.geosector.fr');
      final isSimulated = !isProduction; // Simulé uniquement si pas en PROD

      final config = TapToPayDiscoveryConfiguration(
        isSimulated: isSimulated,
      );

      debugPrint('🔧 Environnement: ${isProduction ? "PRODUCTION (réel)" : "DEV/REC (simulé)"}');
      debugPrint('🔧 isSimulated: $isSimulated');

      // Découvrir les readers avec un Completer pour gérer le stream correctement
      final completer = Completer<Reader?>();
      StreamSubscription<List<Reader>>? subscription;

      subscription = Terminal.instance.discoverReaders(config).listen(
        (readers) {
          debugPrint('📡 Stream readers reçu: ${readers.length} reader(s)');
          if (readers.isNotEmpty && !completer.isCompleted) {
            debugPrint('📱 ${readers.length} reader(s) trouvé(s): ${readers.map((r) => r.label).join(", ")}');
            completer.complete(readers.first);
            subscription?.cancel();
          }
        },
        onError: (error) {
          debugPrint('❌ Erreur lors de la découverte: $error');
          if (!completer.isCompleted) {
            completer.complete(null);
          }
          subscription?.cancel();
        },
        onDone: () {
          debugPrint('🏁 Stream découverte terminé');
          if (!completer.isCompleted) {
            debugPrint('⚠️ Découverte terminée sans reader trouvé');
            completer.complete(null);
          }
        },
      );

      debugPrint('⏳ Attente du résultat de la découverte...');

      // Attendre le résultat avec timeout
      final reader = await completer.future.timeout(
        const Duration(seconds: 15),
        onTimeout: () {
          debugPrint('⏱️ Timeout lors de la découverte du reader');
          subscription?.cancel();
          return null;
        },
      );

      if (reader == null) {
        debugPrint('❌ Aucun reader Tap to Pay trouvé');
        return false;
      }

      debugPrint('📱 Reader trouvé: ${reader.label}');

      // Se connecter au reader
      debugPrint('🔌 Connexion au reader...');
      final connectionConfig = TapToPayConnectionConfiguration(
        locationId: _locationId ?? '',
        readerDelegate: null, // Pas de delegate pour l'instant
      );

      await Terminal.instance.connectReader(
        reader,
        configuration: connectionConfig,
      );

      debugPrint('✅ Connecté au reader Tap to Pay');
      return true;

    } catch (e) {
      debugPrint('❌ Erreur connexion reader: $e');
      return false;
    }
  }

  /// Collecte le paiement avec le SDK Stripe Terminal
  Future<bool> collectPayment(PaymentIntentResult paymentIntent) async {
    try {
      debugPrint('💳 Collecte du paiement avec SDK...');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.processing,
        message: 'Préparation du terminal...',
        paymentIntentId: paymentIntent.paymentIntentId,
      ));

      // 1. S'assurer qu'un reader est connecté
      debugPrint('🔌 Vérification connexion reader...');
      final readerConnected = await _ensureReaderConnected();
      if (!readerConnected) {
        throw Exception('Impossible de se connecter au reader Tap to Pay');
      }

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.processing,
        message: 'Lecture de la carte...',
        paymentIntentId: paymentIntent.paymentIntentId,
      ));

      // 2. Récupérer le PaymentIntent depuis le SDK avec le clientSecret
      debugPrint('💳 Récupération du PaymentIntent...');
      final stripePaymentIntent = await Terminal.instance.retrievePaymentIntent(
        paymentIntent.clientSecret,
      );

      // 3. Utiliser le SDK Stripe Terminal pour collecter le paiement
      debugPrint('💳 En attente du paiement sans contact...');
      final collectedPaymentIntent = await Terminal.instance.collectPaymentMethod(
        stripePaymentIntent,
      );

      // Sauvegarder le PaymentIntent collecté pour l'étape de confirmation
      paymentIntent._collectedPaymentIntent = collectedPaymentIntent;

      debugPrint('✅ Paiement collecté via SDK');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.confirming,
        message: 'Confirmation du paiement...',
        paymentIntentId: paymentIntent.paymentIntentId,
      ));

      return true;

    } catch (e) {
      debugPrint('❌ Erreur collecte paiement: $e');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.error,
        message: 'Erreur lors de la collecte: $e',
        paymentIntentId: paymentIntent.paymentIntentId,
      ));

      return false;
    }
  }

  /// Confirme le paiement via le SDK Stripe Terminal
  Future<bool> confirmPayment(PaymentIntentResult paymentIntent) async {
    try {
      debugPrint('✅ Confirmation du paiement via SDK...');

      // Vérifier que le paiement a été collecté
      if (paymentIntent._collectedPaymentIntent == null) {
        throw Exception('Le paiement doit d\'abord être collecté');
      }

      // Utiliser le SDK Stripe Terminal pour confirmer le paiement
      final confirmedPaymentIntent = await Terminal.instance.confirmPaymentIntent(
        paymentIntent._collectedPaymentIntent!,
      );

      // Vérifier le statut final
      if (confirmedPaymentIntent.status == PaymentIntentStatus.succeeded) {
        debugPrint('🎉 Paiement confirmé avec succès via SDK');
        debugPrint('   Payment Intent: ${paymentIntent.paymentIntentId}');

        _paymentStatusController.add(TapToPayStatus(
          type: TapToPayStatusType.success,
          message: 'Paiement réussi',
          paymentIntentId: paymentIntent.paymentIntentId,
          amount: paymentIntent.amount,
        ));

        return true;
      } else {
        throw Exception('Paiement non confirmé: ${confirmedPaymentIntent.status}');
      }

    } catch (e) {
      debugPrint('❌ Erreur confirmation paiement: $e');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.error,
        message: 'Erreur de confirmation: $e',
        paymentIntentId: paymentIntent.paymentIntentId,
      ));

      return false;
    }
  }

  /// Annule un paiement
  Future<void> cancelPayment(String paymentIntentId) async {
    try {
      await ApiService.instance.post(
        '/stripe/payments/cancel',
        data: {
          'payment_intent_id': paymentIntentId,
        },
      );

      debugPrint('❌ Paiement annulé');

      _paymentStatusController.add(TapToPayStatus(
        type: TapToPayStatusType.cancelled,
        message: 'Paiement annulé',
        paymentIntentId: paymentIntentId,
      ));

    } catch (e) {
      debugPrint('⚠️ Erreur annulation paiement: $e');
    }
  }

  /// Vérifie si le service est prêt pour les paiements
  bool isReadyForPayments() {
    return _isInitialized &&
           _deviceCompatible &&
           _stripeAccountId != null &&
           _stripeAccountId!.isNotEmpty;
  }

  /// Récupère les informations de statut
  Map<String, dynamic> getStatus() {
    return {
      'initialized': _isInitialized,
      'device_compatible': _deviceCompatible,
      'stripe_account': _stripeAccountId,
      'location_id': _locationId,
      'ready_for_payments': isReadyForPayments(),
    };
  }

  /// Nettoie les ressources
  void dispose() {
    _paymentStatusController.close();
    _isInitialized = false;
  }
}

/// Résultat de création d'un PaymentIntent
class PaymentIntentResult {
  final String paymentIntentId;
  final String clientSecret;
  final int amount;

  // PaymentIntent collecté (utilisé entre collectPayment et confirmPayment)
  PaymentIntent? _collectedPaymentIntent;

  PaymentIntentResult({
    required this.paymentIntentId,
    required this.clientSecret,
    required this.amount,
  });
}

/// Statut du processus Tap to Pay
enum TapToPayStatusType {
  ready,
  awaitingTap,
  processing,
  confirming,
  success,
  error,
  cancelled,
}

/// Classe pour représenter l'état du processus Tap to Pay
class TapToPayStatus {
  final TapToPayStatusType type;
  final String message;
  final String? paymentIntentId;
  final int? amount;
  final DateTime timestamp;

  TapToPayStatus({
    required this.type,
    required this.message,
    this.paymentIntentId,
    this.amount,
  }) : timestamp = DateTime.now();

  bool get isSuccess => type == TapToPayStatusType.success;
  bool get isError => type == TapToPayStatusType.error;
  bool get isProcessing =>
    type == TapToPayStatusType.processing ||
    type == TapToPayStatusType.confirming;
}