import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:em2rp/utils/colors.dart';

/// Service pour la génération de QR codes optimisée
class QRCodeService {
  // Cache pour éviter de régénérer les mêmes QR codes
  static final Map<String, Uint8List> _qrCache = {};
  static ui.Image? _cachedLogoImage;

  /// Génère un QR code simple sans logo
  static Future<Uint8List> generateQRCode(
    String data, {
    double size = 512,
    bool useCache = true,
  }) async {
    // Vérifier le cache
    if (useCache && _qrCache.containsKey(data)) {
      return _qrCache[data]!;
    }

    final qrValidationResult = QrValidator.validate(
      data: data,
      version: QrVersions.auto,
      errorCorrectionLevel: QrErrorCorrectLevel.L,
    );

    if (qrValidationResult.status != QrValidationStatus.valid) {
      throw Exception('QR code validation failed for data: $data');
    }

    final qrCode = qrValidationResult.qrCode!;
    final painter = QrPainter.withQr(
      qr: qrCode,
      eyeStyle: const QrEyeStyle(
        eyeShape: QrEyeShape.square,
        color: AppColors.noir,
      ),
      gapless: true,
    );

    final picData = await painter.toImageData(size, format: ui.ImageByteFormat.png);
    final bytes = picData!.buffer.asUint8List();

    // Mettre en cache
    if (useCache) {
      _qrCache[data] = bytes;
    }

    return bytes;
  }

  /// Génère un QR code avec logo embarqué
  static Future<Uint8List> generateQRCodeWithLogo(
    String data, {
    double size = 512,
    bool useCache = true,
  }) async {
    final cacheKey = '${data}_logo';

    // Vérifier le cache
    if (useCache && _qrCache.containsKey(cacheKey)) {
      return _qrCache[cacheKey]!;
    }

    final qrValidationResult = QrValidator.validate(
      data: data,
      version: QrVersions.auto,
      errorCorrectionLevel: QrErrorCorrectLevel.L,
    );

    if (qrValidationResult.status != QrValidationStatus.valid) {
      throw Exception('QR code validation failed for data: $data');
    }

    final qrCode = qrValidationResult.qrCode!;
    final embedded = await _loadLogoImage();

    final painter = QrPainter.withQr(
      qr: qrCode,
      embeddedImage: embedded,
      embeddedImageStyle: const QrEmbeddedImageStyle(
        size: Size(80, 80),
      ),
      gapless: true,
    );

    final picData = await painter.toImageData(size, format: ui.ImageByteFormat.png);
    final bytes = picData!.buffer.asUint8List();

    // Mettre en cache
    if (useCache) {
      _qrCache[cacheKey] = bytes;
    }

    return bytes;
  }

  /// Charge le logo depuis les assets (avec cache)
  static Future<ui.Image> _loadLogoImage() async {
    if (_cachedLogoImage != null) {
      return _cachedLogoImage!;
    }

    final data = await rootBundle.load('assets/logos/SquareLogoBlack.png');
    final codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    final frame = await codec.getNextFrame();
    _cachedLogoImage = frame.image;
    return _cachedLogoImage!;
  }

  /// Génère plusieurs QR codes en parallèle par batches optimisés (réduit les lags)
  static Future<List<Uint8List>> generateBulkQRCodes(
    List<String> dataList, {
    double size = 512,
    bool withLogo = false,
    bool useCache = true,
    Function(int, int)? onProgress, // Callback de progression
  }) async {
    if (dataList.isEmpty) return [];

    // Si tout est en cache, retourner immédiatement
    if (useCache) {
      final allCached = dataList.every((data) {
        final key = withLogo ? '${data}_logo' : data;
        return _qrCache.containsKey(key);
      });

      if (allCached) {
        return dataList.map((data) {
          final key = withLogo ? '${data}_logo' : data;
          return _qrCache[key]!;
        }).toList();
      }
    }

    // Batching adaptatif ultra-optimisé
    int batchSize;
    if (dataList.length <= 10) {
      // Pour petites quantités, tout en une fois
      batchSize = dataList.length;
    } else if (size <= 200) {
      batchSize = 20; // Petits QR : lots de 20 (réduit de 100)
    } else if (size <= 300) {
      batchSize = 10; // Moyens QR : lots de 10 (réduit de 50)
    } else if (size <= 400) {
      batchSize = 5;  // Grands QR : lots de 5 (réduit de 20)
    } else {
      batchSize = 3;  // Très grands : lots de 3 (réduit de 10)
    }

    final List<Uint8List> results = [];
    int processed = 0;

    for (int i = 0; i < dataList.length; i += batchSize) {
      final batch = dataList.skip(i).take(batchSize).toList();

      // Générer le batch
      final batchResults = await Future.wait(
        batch.map((data) => withLogo
            ? generateQRCodeWithLogo(data, size: size, useCache: useCache)
            : generateQRCode(data, size: size, useCache: useCache)),
      );

      results.addAll(batchResults);
      processed += batch.length;

      // Callback de progression
      if (onProgress != null) {
        onProgress(processed, dataList.length);
      }

      // Pause micro pour éviter de bloquer l'UI (seulement pour gros batches)
      if (batchSize >= 5 && i + batchSize < dataList.length) {
        await Future.delayed(const Duration(milliseconds: 10));
      }
    }

    return results;
  }

  /// Vide le cache des QR codes
  static void clearCache() {
    _qrCache.clear();
  }

  /// Obtient la taille du cache
  static int getCacheSize() {
    return _qrCache.length;
  }
}

