import 'dart:io';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:battery_plus/battery_plus.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:dio/dio.dart';
import 'package:hive/hive.dart';

import 'api_service.dart';
import 'current_user_service.dart';
import 'app_info_service.dart'; // Remplace package_info_plus
import '../constants/app_keys.dart';

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

  final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
  final Battery _battery = Battery();

  Future<Map<String, dynamic>> collectDeviceInfo() async {
    final deviceData = <String, dynamic>{};

    try {
      // Informations réseau et IP (IPv4 uniquement)
      deviceData['device_ip_local'] = await _getLocalIpAddress();
      deviceData['device_ip_public'] = await _getPublicIpAddress();
      deviceData['device_wifi_name'] = null; // ❌ Supprimé network_info_plus (13/10/2025)
      deviceData['device_wifi_bssid'] = null; // ❌ Supprimé network_info_plus (13/10/2025)

      // Informations batterie
      final batteryLevel = await _battery.batteryLevel;
      final batteryState = await _battery.batteryState;

      deviceData['battery_level'] = batteryLevel; // Pourcentage 0-100
      deviceData['battery_charging'] = batteryState == BatteryState.charging;
      deviceData['battery_state'] = batteryState.toString().split('.').last;

      // Informations plateforme
      if (Platform.isIOS) {
        final iosInfo = await _deviceInfo.iosInfo;
        deviceData['platform'] = 'iOS';
        deviceData['device_model'] = iosInfo.model;
        deviceData['device_name'] = iosInfo.name;
        deviceData['ios_version'] = iosInfo.systemVersion;
        deviceData['device_manufacturer'] = 'Apple';
        deviceData['device_identifier'] = iosInfo.utsname.machine;

        deviceData['device_supports_tap_to_pay'] = _checkIosTapToPaySupport(
          iosInfo.utsname.machine,
          iosInfo.systemVersion
        );

      } else if (Platform.isAndroid) {
        final androidInfo = await _deviceInfo.androidInfo;
        deviceData['platform'] = 'Android';
        deviceData['device_model'] = androidInfo.model;
        deviceData['device_name'] = androidInfo.device;
        deviceData['android_version'] = androidInfo.version.release;
        deviceData['android_sdk_version'] = androidInfo.version.sdkInt;
        deviceData['device_manufacturer'] = androidInfo.manufacturer;
        deviceData['device_brand'] = androidInfo.brand;

        deviceData['device_supports_tap_to_pay'] = androidInfo.version.sdkInt >= 28;

      } else if (kIsWeb) {
        deviceData['platform'] = 'Web';
        deviceData['device_supports_tap_to_pay'] = false;
        deviceData['battery_level'] = null;
        deviceData['battery_charging'] = null;
        deviceData['battery_state'] = null;
      }

      // Vérification NFC
      if (!kIsWeb) {
        try {
          deviceData['device_nfc_capable'] = await NfcManager.instance.isAvailable();
        } catch (e) {
          deviceData['device_nfc_capable'] = false;
          debugPrint('NFC check failed: $e');
        }
      } else {
        deviceData['device_nfc_capable'] = false;
      }

      // Vérification de la certification Stripe Tap to Pay
      if (!kIsWeb) {
        try {
          deviceData['device_stripe_certified'] = await checkStripeCertification();
          debugPrint('📱 Certification Stripe: ${deviceData['device_stripe_certified']}');
        } catch (e) {
          deviceData['device_stripe_certified'] = false;
          debugPrint('❌ Erreur vérification certification Stripe: $e');
        }
      } else {
        deviceData['device_stripe_certified'] = false;
      }

      // Timestamp de la collecte
      deviceData['last_device_info_check'] = DateTime.now().toIso8601String();

    } catch (e) {
      debugPrint('Error collecting device info: $e');
      deviceData['platform'] = kIsWeb ? 'Web' : (Platform.isIOS ? 'iOS' : 'Android');
      deviceData['device_supports_tap_to_pay'] = false;
      deviceData['device_nfc_capable'] = false;
      deviceData['device_stripe_certified'] = false;
    }

    return deviceData;
  }

  /// Récupère l'adresse IP locale du device (IPv4 uniquement)
  Future<String?> _getLocalIpAddress() async {
    try {
      if (kIsWeb) {
        // Sur Web, impossible d'obtenir l'IP locale pour des raisons de sécurité
        return null;
      }

      // Via NetworkInterface avec filtre IPv4 strict
      for (var interface in await NetworkInterface.list()) {
        for (var addr in interface.addresses) {
          // Vérifier explicitement IPv4 et non loopback
          if (addr.type == InternetAddressType.IPv4 &&
              !addr.isLoopback &&
              _isIPv4(addr.address)) {
            return addr.address;
          }
        }
      }

      return null;
    } catch (e) {
      debugPrint('Error getting local IPv4: $e');
      return null;
    }
  }

  /// Récupère l'adresse IP publique IPv4 via un service externe
  Future<String?> _getPublicIpAddress() async {
    try {
      // Services qui retournent l'IPv4
      final services = [
        'https://api.ipify.org?format=json',  // Supporte IPv4 explicitement
        'https://ipv4.icanhazip.com',         // Force IPv4
        'https://v4.ident.me',                // Force IPv4
        'https://api4.ipify.org',             // API IPv4 dédiée
      ];

      final dio = Dio();
      dio.options.connectTimeout = const Duration(seconds: 5);
      dio.options.receiveTimeout = const Duration(seconds: 5);

      for (final service in services) {
        try {
          final response = await dio.get(service);

          String? ipAddress;

          // Gérer différents formats de réponse
          if (response.data is Map) {
            ipAddress = response.data['ip']?.toString();
          } else if (response.data is String) {
            ipAddress = response.data.trim();
          }

          // Vérifier que c'est bien une IPv4
          if (ipAddress != null && _isIPv4(ipAddress)) {
            return ipAddress;
          }
        } catch (e) {
          // Essayer le service suivant
          continue;
        }
      }

      return null;
    } catch (e) {
      debugPrint('Error getting public IPv4: $e');
      return null;
    }
  }

  /// Vérifie si une adresse est bien au format IPv4
  bool _isIPv4(String address) {
    // Pattern pour IPv4 : 4 groupes de 1-3 chiffres séparés par des points
    final ipv4Regex = RegExp(
      r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$'
    );

    if (!ipv4Regex.hasMatch(address)) {
      return false;
    }

    // Vérifier que chaque octet est entre 0 et 255
    final parts = address.split('.');
    for (final part in parts) {
      final num = int.tryParse(part);
      if (num == null || num < 0 || num > 255) {
        return false;
      }
    }

    // Exclure les IPv6 (contiennent ':')
    if (address.contains(':')) {
      return false;
    }

    return true;
  }

  bool _checkIosTapToPaySupport(String machine, String systemVersion) {
    // Vérifier le modèle : iPhone11,x ou supérieur (iPhone XS+)
    // Extraire le numéro majeur de l'identifiant (ex: "iPhone17,3" -> 17)
    bool deviceSupported = false;

    if (machine.startsWith('iPhone')) {
      final match = RegExp(r'iPhone(\d+),').firstMatch(machine);
      if (match != null) {
        final majorVersion = int.tryParse(match.group(1) ?? '0') ?? 0;
        // iPhone XS = iPhone11,x donc >= 11 pour supporter Tap to Pay
        deviceSupported = majorVersion >= 11;
      }
    }

    // Vérifier la version iOS (16.4+ selon la documentation officielle Stripe)
    final versionParts = systemVersion.split('.');
    if (versionParts.isNotEmpty) {
      final majorVersion = int.tryParse(versionParts[0]) ?? 0;
      final minorVersion = versionParts.length > 1 ? int.tryParse(versionParts[1]) ?? 0 : 0;

      // iOS 16.4 minimum selon Stripe docs
      return deviceSupported && (majorVersion > 16 || (majorVersion == 16 && minorVersion >= 4));
    }

    return false;
  }

  /// Collecte et envoie les informations device à l'API
  Future<bool> collectAndSendDeviceInfo() async {
    try {
      // 1. Collecter les infos device
      final deviceData = await collectDeviceInfo();

      // 2. Ajouter les infos de l'app
      deviceData['app_version'] = AppInfoService.version;
      deviceData['app_build'] = AppInfoService.buildNumber;

      // 3. Sauvegarder dans Hive Settings
      await _saveToHiveSettings(deviceData);

      // 4. Envoyer à l'API si l'utilisateur est connecté
      if (CurrentUserService.instance.isLoggedIn) {
        await _sendDeviceInfoToApi(deviceData);
      }

      return true;
    } catch (e) {
      debugPrint('Error collecting/sending device info: $e');
      return false;
    }
  }

  /// Sauvegarde les infos dans la box Settings
  Future<void> _saveToHiveSettings(Map<String, dynamic> deviceData) async {
    final settingsBox = Hive.box(AppKeys.settingsBoxName);

    // Sauvegarder chaque info dans la box settings
    for (final entry in deviceData.entries) {
      await settingsBox.put('device_${entry.key}', entry.value);
    }

    // Sauvegarder aussi l'IP pour un accès rapide
    if (deviceData['device_ip_public'] != null) {
      await settingsBox.put('last_known_public_ip', deviceData['device_ip_public']);
    }
    if (deviceData['device_ip_local'] != null) {
      await settingsBox.put('last_known_local_ip', deviceData['device_ip_local']);
    }

    debugPrint('Device info saved to Hive Settings');
  }

  /// Envoie les infos device à l'API
  Future<void> _sendDeviceInfoToApi(Map<String, dynamic> deviceData) async {
    try {
      // Nettoyer le payload (enlever les nulls)
      final payload = <String, dynamic>{};
      deviceData.forEach((key, value) {
        if (value != null) {
          payload[key] = value;
        }
      });

      // Envoyer à l'API
      final response = await ApiService.instance.post(
        '/users/device-info',
        data: payload,
      );

      if (response.statusCode == 200 || response.statusCode == 201) {
        debugPrint('Device info sent to API successfully');
      }
    } catch (e) {
      // Ne pas bloquer si l'envoi échoue
      debugPrint('Failed to send device info to API: $e');
    }
  }

  /// Récupère les infos device depuis Hive
  Map<String, dynamic> getStoredDeviceInfo() {
    final settingsBox = Hive.box(AppKeys.settingsBoxName);
    final deviceInfo = <String, dynamic>{};

    // Liste des clés à récupérer
    final keys = [
      'platform', 'device_model', 'device_name', 'device_manufacturer',
      'device_brand', 'device_identifier', 'ios_version',
      'android_version', 'android_sdk_version', 'device_nfc_capable',
      'device_supports_tap_to_pay', 'device_stripe_certified', 'battery_level',
      'battery_charging', 'battery_state', 'last_device_info_check', 'app_version',
      'app_build', 'device_ip_local', 'device_ip_public', 'device_wifi_name',
      'device_wifi_bssid'
    ];

    for (final key in keys) {
      final value = settingsBox.get('device_$key');
      if (value != null) {
        deviceInfo[key] = value;
      }
    }

    return deviceInfo;
  }

  /// Vérifie la certification Stripe Tap to Pay via le SDK Stripe Terminal
  Future<bool> checkStripeCertification() async {
    try {
      // Sur Web, toujours non supporté
      if (kIsWeb) {
        debugPrint('📱 Web platform - Tap to Pay non supporté');
        return false;
      }

      // iOS : vérification locale (iPhone XS+ avec iOS 16.4+)
      if (Platform.isIOS) {
        final iosInfo = await _deviceInfo.iosInfo;
        final isSupported = _checkIosTapToPaySupport(
          iosInfo.utsname.machine,
          iosInfo.systemVersion
        );
        debugPrint('📱 iOS Tap to Pay support: $isSupported');
        return isSupported;
      }

      // Android : vérification des pré-requis hardware de base
      // Note: Le vrai check de compatibilité avec découverte de readers se fera
      // dans StripeTapToPayService lors du premier paiement
      if (Platform.isAndroid) {
        final androidInfo = await _deviceInfo.androidInfo;
        debugPrint('📱 Vérification Tap to Pay pour ${androidInfo.manufacturer} ${androidInfo.model}');

        // Vérifications préalables de base
        if (androidInfo.version.sdkInt < 28) {
          debugPrint('❌ Android SDK trop ancien: ${androidInfo.version.sdkInt} (minimum 28)');
          return false;
        }

        // Vérifier la disponibilité du NFC
        try {
          final nfcAvailable = await NfcManager.instance.isAvailable();
          if (!nfcAvailable) {
            debugPrint('❌ NFC non disponible sur cet appareil');
            return false;
          }
          debugPrint('✅ NFC disponible');
        } catch (e) {
          debugPrint('⚠️ Impossible de vérifier NFC: $e');
          // On continue quand même, ce n'est pas bloquant à ce stade
        }

        // Pré-requis de base OK
        debugPrint('✅ Pré-requis Android OK (SDK ${androidInfo.version.sdkInt}, NFC disponible)');
        return true;
      }

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


  /// Vérifie si le device peut utiliser Tap to Pay
  bool canUseTapToPay() {
    final deviceInfo = getStoredDeviceInfo();

    // checkStripeCertification() fait déjà toutes les vérifications (modèle, iOS version, NFC Android)
    final stripeCertified = deviceInfo['device_stripe_certified'] ?? deviceInfo['device_supports_tap_to_pay'];
    final batteryLevel = deviceInfo['battery_level'] as int?;

    // Batterie minimum 10% pour les paiements
    final sufficientBattery = batteryLevel != null && batteryLevel >= 10;

    return stripeCertified == true && sufficientBattery;
  }

  /// Retourne la raison pour laquelle Tap to Pay n'est pas disponible
  /// Retourne null si Tap to Pay est disponible
  String? getTapToPayUnavailableReason() {
    // Sur Web, Tap to Pay n'est jamais disponible
    if (kIsWeb) {
      return 'Tap to Pay non disponible sur Web';
    }

    final deviceInfo = getStoredDeviceInfo();

    // Vérifier la batterie
    final batteryLevel = deviceInfo['battery_level'] as int?;
    if (batteryLevel == null) {
      return 'Niveau de batterie inconnu';
    }
    if (batteryLevel < 10) {
      return 'Batterie insuffisante ($batteryLevel%) - Min. 10% requis';
    }

    // Vérifier la certification Stripe (inclut déjà modèle, iOS version, NFC Android)
    final stripeCertified = deviceInfo['device_stripe_certified'] ?? deviceInfo['device_supports_tap_to_pay'];
    if (stripeCertified != true) {
      return 'Appareil non certifié pour Tap to Pay';
    }

    // Tout est OK
    return null;
  }

  /// Version asynchrone avec vérification NFC en temps réel (Android uniquement)
  Future<String?> getTapToPayUnavailableReasonAsync() async {
    // Sur Web, Tap to Pay n'est jamais disponible
    if (kIsWeb) {
      return 'Tap to Pay non disponible sur Web';
    }

    final deviceInfo = getStoredDeviceInfo();
    final platform = deviceInfo['platform'];

    // Vérifier la batterie
    final batteryLevel = deviceInfo['battery_level'] as int?;
    if (batteryLevel == null) {
      return 'Niveau de batterie inconnu';
    }
    if (batteryLevel < 10) {
      return 'Batterie insuffisante ($batteryLevel%) - Min. 10% requis';
    }

    // Sur Android, vérifier le NFC EN TEMPS RÉEL (peut être désactivé dans les paramètres)
    if (platform == 'Android') {
      try {
        final nfcAvailable = await NfcManager.instance.isAvailable();
        if (!nfcAvailable) {
          return 'NFC désactivé - Activez-le dans les paramètres Android';
        }
      } catch (e) {
        debugPrint('⚠️ Impossible de vérifier le statut NFC: $e');
        return 'Impossible de vérifier le NFC';
      }
    }

    // Vérifier la certification Stripe (inclut déjà modèle, iOS version)
    final stripeCertified = deviceInfo['device_stripe_certified'] ?? deviceInfo['device_supports_tap_to_pay'];
    if (stripeCertified != true) {
      return 'Appareil non certifié pour Tap to Pay';
    }

    // Tout est OK
    return null;
  }
}