import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:mek_stripe_terminal/mek_stripe_terminal.dart';
import 'package:flutter_stripe/flutter_stripe.dart' as stripe_sdk;
import 'package:permission_handler/permission_handler.dart';

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

class StripeTerminalService {
  static final StripeTerminalService instance = StripeTerminalService._internal();
  StripeTerminalService._internal();

  // Instance du terminal Stripe
  Terminal? _terminal;
  bool _isInitialized = false;
  bool _isConnected = false;

  // État du reader
  Reader? _currentReader;
  StreamSubscription<List<Reader>>? _discoverSubscription;

  // Configuration Stripe
  String? _stripePublishableKey;
  String? _stripeAccountId; // Connected account ID de l'amicale
  String? _locationId; // Location ID pour le Terminal

  // Stream controllers pour les événements
  final _paymentStatusController = StreamController<PaymentStatus>.broadcast();
  final _readerStatusController = StreamController<ReaderStatus>.broadcast();

  // Getters publics
  bool get isInitialized => _isInitialized;
  bool get isConnected => _isConnected;
  Reader? get currentReader => _currentReader;
  Stream<PaymentStatus> get paymentStatusStream => _paymentStatusController.stream;
  Stream<ReaderStatus> get readerStatusStream => _readerStatusController.stream;

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

    try {
      debugPrint('🚀 Initialisation de Stripe Terminal...');

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

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

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

      _stripeAccountId = amicale.stripeId;

      // 3. Demander les permissions nécessaires
      await _requestPermissions();

      // 4. Récupérer la configuration Stripe depuis l'API
      await _fetchStripeConfiguration();

      // 5. Initialiser le SDK Stripe Terminal
      await Terminal.initTerminal(
        fetchToken: _fetchConnectionToken,
      );

      _terminal = Terminal.instance;

      // 6. Vérifier la compatibilité Tap to Pay
      final canUseTapToPay = DeviceInfoService.instance.canUseTapToPay();
      if (!canUseTapToPay) {
        debugPrint('⚠️ Cet appareil ne supporte pas Tap to Pay');
        // Ne pas bloquer l'initialisation, juste informer
      }

      _isInitialized = true;
      debugPrint('✅ Stripe Terminal initialisé avec succès');

      return true;

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

  /// Demande les permissions nécessaires pour le Terminal
  Future<void> _requestPermissions() async {
    if (kIsWeb) return; // Pas de permissions sur web

    final permissions = <Permission>[
      Permission.locationWhenInUse,
      Permission.bluetooth,
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
    ];

    final statuses = await permissions.request();

    for (final entry in statuses.entries) {
      if (!entry.value.isGranted) {
        debugPrint('⚠️ Permission refusée: ${entry.key}');
      }
    }
  }

  /// Récupère la configuration Stripe depuis l'API
  Future<void> _fetchStripeConfiguration() async {
    try {
      final response = await ApiService.instance.get('/stripe/configuration');

      if (response.data['publishable_key'] != null) {
        _stripePublishableKey = response.data['publishable_key'];

        // Initialiser aussi le SDK Flutter Stripe standard
        stripe_sdk.Stripe.publishableKey = _stripePublishableKey!;

        // Si on a un connected account ID, le configurer
        if (_stripeAccountId != null) {
          stripe_sdk.Stripe.stripeAccountId = _stripeAccountId;
        }

        // Récupérer le location ID si disponible
        _locationId = response.data['location_id'];

      } else {
        throw Exception('Clé publique Stripe non trouvée');
      }
    } catch (e) {
      debugPrint('❌ Erreur récupération config Stripe: $e');
      rethrow;
    }
  }

  /// 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');
    }
  }

  /// Découvre les readers disponibles (Tap to Pay sur iPhone)
  Future<bool> discoverReaders() async {
    if (!_isInitialized || _terminal == null) {
      debugPrint('❌ Terminal non initialisé');
      return false;
    }

    try {
      debugPrint('🔍 Recherche des readers disponibles...');

      // Annuler la découverte précédente si elle existe
      await _discoverSubscription?.cancel();

      // Configuration pour découvrir le reader local (Tap to Pay)
      final config = TapToPayDiscoveryConfiguration();

      // Lancer la découverte (retourne un Stream)
      _discoverSubscription = _terminal!
          .discoverReaders(config)
          .listen((List<Reader> readers) {

        debugPrint('📱 ${readers.length} reader(s) trouvé(s)');

        if (readers.isNotEmpty) {
          // Prendre le premier reader (devrait être l'iPhone local)
          final reader = readers.first;
          debugPrint('📱 Reader trouvé: ${reader.label} (${reader.serialNumber})');

          // Se connecter automatiquement au premier reader trouvé
          connectToReader(reader);
        } else {
          debugPrint('⚠️ Aucun reader trouvé');
          _readerStatusController.add(ReaderStatus(
            isConnected: false,
            reader: null,
            errorMessage: 'Aucun reader disponible',
          ));
        }
      }, onError: (error) {
        debugPrint('❌ Erreur découverte readers: $error');
        _readerStatusController.add(ReaderStatus(
          isConnected: false,
          reader: null,
          errorMessage: error.toString(),
        ));
      });

      return true;

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

  /// Se connecte à un reader spécifique
  Future<bool> connectToReader(Reader reader) async {
    if (!_isInitialized || _terminal == null) {
      return false;
    }

    try {
      debugPrint('🔌 Connexion au reader: ${reader.label}...');

      // Configuration pour la connexion Tap to Pay
      final config = TapToPayConnectionConfiguration(
        locationId: _locationId ?? '',
        autoReconnectOnUnexpectedDisconnect: true,
        readerDelegate: null, // Pas de délégué pour le moment
      );

      // Se connecter au reader
      final connectedReader = await _terminal!.connectReader(
        reader,
        configuration: config,
      );

      _currentReader = connectedReader;
      _isConnected = true;

      debugPrint('✅ Connecté au reader: ${connectedReader.label}');

      _readerStatusController.add(ReaderStatus(
        isConnected: true,
        reader: connectedReader,
      ));

      // Arrêter la découverte
      await _discoverSubscription?.cancel();
      _discoverSubscription = null;

      return true;

    } catch (e) {
      debugPrint('❌ Erreur connexion reader: $e');
      _readerStatusController.add(ReaderStatus(
        isConnected: false,
        reader: null,
        errorMessage: e.toString(),
      ));
      return false;
    }
  }

  /// Déconnecte le reader actuel
  Future<void> disconnectReader() async {
    if (!_isConnected || _terminal == null) return;

    try {
      debugPrint('🔌 Déconnexion du reader...');
      await _terminal!.disconnectReader();

      _currentReader = null;
      _isConnected = false;

      _readerStatusController.add(ReaderStatus(
        isConnected: false,
        reader: null,
      ));

      debugPrint('✅ Reader déconnecté');
    } catch (e) {
      debugPrint('❌ Erreur déconnexion reader: $e');
    }
  }

  /// Processus complet de paiement
  Future<PaymentResult> processPayment(int amountInCents, {String? description}) async {
    if (!_isConnected || _terminal == null) {
      throw Exception('Terminal non connecté');
    }

    PaymentIntent? paymentIntent;

    try {
      debugPrint('💰 Démarrage du paiement de ${amountInCents / 100}€...');

      // 1. Créer le PaymentIntent côté serveur
      final response = await ApiService.instance.post(
        '/stripe/terminal/create-payment-intent',
        data: {
          'amount': amountInCents,
          'currency': 'eur',
          'description': description ?? 'Calendrier pompiers',
          'amicale_id': CurrentAmicaleService.instance.amicaleId,
          'member_id': CurrentUserService.instance.userId,
          'stripe_account': _stripeAccountId,
        },
      );

      final clientSecret = response.data['client_secret'];
      if (clientSecret == null) {
        throw Exception('Client secret manquant');
      }

      // 2. Récupérer le PaymentIntent depuis le SDK
      debugPrint('💳 Récupération du PaymentIntent...');
      paymentIntent = await _terminal!.retrievePaymentIntent(clientSecret);

      _paymentStatusController.add(PaymentStatus(
        status: PaymentIntentStatus.requiresPaymentMethod,
        timestamp: DateTime.now(),
      ));

      // 3. Collecter la méthode de paiement (présenter l'interface Tap to Pay)
      debugPrint('💳 En attente du paiement sans contact...');
      final collectedPaymentIntent = await _terminal!.collectPaymentMethod(paymentIntent);

      _paymentStatusController.add(PaymentStatus(
        status: PaymentIntentStatus.requiresConfirmation,
        timestamp: DateTime.now(),
      ));

      // 4. Confirmer le paiement
      debugPrint('✅ Confirmation du paiement...');
      final confirmedPaymentIntent = await _terminal!.confirmPaymentIntent(collectedPaymentIntent);

      // Vérifier le statut final
      if (confirmedPaymentIntent.status == PaymentIntentStatus.succeeded) {
        debugPrint('🎉 Paiement réussi!');

        _paymentStatusController.add(PaymentStatus(
          status: PaymentIntentStatus.succeeded,
          timestamp: DateTime.now(),
        ));

        // Notifier le serveur du succès
        await _notifyPaymentSuccess(confirmedPaymentIntent);

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

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

      _paymentStatusController.add(PaymentStatus(
        status: PaymentIntentStatus.canceled,
        timestamp: DateTime.now(),
        errorMessage: e.toString(),
      ));

      // Annuler le PaymentIntent si nécessaire
      if (paymentIntent != null) {
        try {
          await _terminal!.cancelPaymentIntent(paymentIntent);
        } catch (_) {
          // Ignorer les erreurs d'annulation
        }
      }

      return PaymentResult(
        success: false,
        errorMessage: e.toString(),
      );
    }
  }

  /// Notifie le serveur du succès du paiement
  Future<void> _notifyPaymentSuccess(PaymentIntent paymentIntent) async {
    try {
      await ApiService.instance.post(
        '/stripe/terminal/payment-success',
        data: {
          'payment_intent_id': paymentIntent.id,
          'amount': paymentIntent.amount,
          'status': paymentIntent.status.toString(),
          'amicale_id': CurrentAmicaleService.instance.amicaleId,
          'member_id': CurrentUserService.instance.userId,
        },
      );
    } catch (e) {
      debugPrint('⚠️ Erreur notification succès paiement: $e');
      // Ne pas bloquer si la notification échoue
    }
  }

  /// Simule un reader de test (pour le développement)
  Future<bool> simulateTestReader() async {
    if (!_isInitialized || _terminal == null) {
      debugPrint('❌ Terminal non initialisé');
      return false;
    }

    try {
      debugPrint('🧪 Simulation d\'un reader de test...');

      // Configuration pour un reader simulé
      final config = TapToPayDiscoveryConfiguration(isSimulated: true);

      // Découvrir le reader simulé
      _terminal!.discoverReaders(config).listen((readers) async {
        if (readers.isNotEmpty) {
          final testReader = readers.first;
          debugPrint('🧪 Reader de test trouvé: ${testReader.label}');

          // Se connecter au reader de test
          await connectToReader(testReader);
        }
      });

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

  /// Vérifie si l'appareil supporte Tap to Pay
  bool isTapToPaySupported() {
    return DeviceInfoService.instance.canUseTapToPay();
  }

  /// Nettoie les ressources
  void dispose() {
    _discoverSubscription?.cancel();
    _paymentStatusController.close();
    _readerStatusController.close();
    disconnectReader();
    _isInitialized = false;
    _terminal = null;
  }
}

/// Classe pour représenter le résultat d'un paiement
class PaymentResult {
  final bool success;
  final PaymentIntent? paymentIntent;
  final String? errorMessage;

  PaymentResult({
    required this.success,
    this.paymentIntent,
    this.errorMessage,
  });
}

/// Classe pour représenter le statut d'un paiement
class PaymentStatus {
  final PaymentIntentStatus status;
  final DateTime timestamp;
  final String? errorMessage;

  PaymentStatus({
    required this.status,
    required this.timestamp,
    this.errorMessage,
  });
}

/// Classe pour représenter le statut du reader
class ReaderStatus {
  final bool isConnected;
  final Reader? reader;
  final String? errorMessage;

  ReaderStatus({
    required this.isConnected,
    this.reader,
    this.errorMessage,
  });
}