import 'package:em2rp/models/event_model.dart';
import 'package:em2rp/models/equipment_model.dart';
import 'package:em2rp/models/container_model.dart';
import 'package:em2rp/models/qr_code_process_result.dart';
import 'package:em2rp/utils/debug_log.dart';

/// Service pour traiter les codes QR scannés ou saisis manuellement
/// pendant la préparation d'un événement
class QRCodeProcessingService {
  /// Traiter un code (équipement ou container)
  Future<QRCodeProcessResult> processCode({
    required String code,
    required EventModel event,
    required dynamic step, // Changed to dynamic to accept any PreparationStep enum
    required Map<String, EquipmentModel> equipmentCache,
    required Map<String, ContainerModel> containerCache,
    required Map<String, bool> validationState,
    required Map<String, int> currentQuantities,
  }) async {
    try {
      DebugLog.info('[QRCodeProcessingService] Processing code: $code');

      // Identifier le type selon le préfixe
      final isContainer = code.startsWith('BOX_');

      if (isContainer) {
        return await _processContainer(
          code: code,
          event: event,
          step: step,
          equipmentCache: equipmentCache,
          containerCache: containerCache,
          validationState: validationState,
          currentQuantities: currentQuantities,
        );
      } else {
        return await _processEquipment(
          code: code,
          event: event,
          step: step,
          equipmentCache: equipmentCache,
          validationState: validationState,
          currentQuantities: currentQuantities,
        );
      }
    } catch (e) {
      DebugLog.error('[QRCodeProcessingService] Error processing code', e);
      return QRCodeProcessResult.error('Erreur lors du traitement du code: $e');
    }
  }

  /// Traiter un code d'équipement
  Future<QRCodeProcessResult> _processEquipment({
    required String code,
    required EventModel event,
    required dynamic step,
    required Map<String, EquipmentModel> equipmentCache,
    required Map<String, bool> validationState,
    required Map<String, int> currentQuantities,
  }) async {
    // Chercher l'équipement dans les équipements assignés
    final eventEquipment = event.assignedEquipment
        .cast<EventEquipment?>()
        .firstWhere(
          (eq) => eq?.equipmentId == code,
          orElse: () => null,
        );

    if (eventEquipment == null) {
      DebugLog.info('[QRCodeProcessingService] Equipment $code not found in event');
      return QRCodeProcessResult.notFoundInEvent(code);
    }

    final equipment = equipmentCache[code];
    final equipmentName = equipment?.name ?? 'Équipement inconnu';

    // Vérifier si l'équipement a des quantités
    if (equipment?.hasQuantity ?? false) {
      return _processQuantitativeEquipment(
        code: code,
        equipmentName: equipmentName,
        eventEquipment: eventEquipment,
        step: step,
        validationState: validationState,
        currentQuantities: currentQuantities,
      );
    } else {
      return _processNonQuantitativeEquipment(
        code: code,
        equipmentName: equipmentName,
        validationState: validationState,
      );
    }
  }

  /// Traiter un équipement quantitatif (incrémenter la quantité)
  QRCodeProcessResult _processQuantitativeEquipment({
    required String code,
    required String equipmentName,
    required EventEquipment eventEquipment,
    required dynamic step,
    required Map<String, bool> validationState,
    required Map<String, int> currentQuantities,
  }) {
    final currentQty = currentQuantities[code] ?? 0;
    final targetQty = _getTargetQuantity(eventEquipment, step);

    // Vérifier si on a déjà atteint la quantité cible
    if (currentQty >= targetQty) {
      return QRCodeProcessResult.error(
        'Quantité cible déjà atteinte pour $equipmentName ($currentQty/$targetQty)',
      );
    }

    // Incrémenter la quantité
    final newQty = currentQty + 1;
    final shouldCheck = newQty >= targetQty;

    return QRCodeProcessResult.success(
      message: '$equipmentName : $newQty/$targetQty${shouldCheck ? " ✓" : ""}',
      affectedEquipmentIds: [code],
      updatedQuantities: {code: newQty},
      updatedValidationState: shouldCheck ? {code: true} : null,
    );
  }

  /// Traiter un équipement non quantitatif (cocher)
  QRCodeProcessResult _processNonQuantitativeEquipment({
    required String code,
    required String equipmentName,
    required Map<String, bool> validationState,
  }) {
    // Vérifier si déjà coché
    if (validationState[code] == true) {
      return QRCodeProcessResult.error('$equipmentName est déjà coché');
    }

    return QRCodeProcessResult.success(
      message: '$equipmentName a été coché ✓',
      affectedEquipmentIds: [code],
      updatedValidationState: {code: true},
    );
  }

  /// Traiter un code de container (cocher tous les enfants)
  Future<QRCodeProcessResult> _processContainer({
    required String code,
    required EventModel event,
    required dynamic step,
    required Map<String, EquipmentModel> equipmentCache,
    required Map<String, ContainerModel> containerCache,
    required Map<String, bool> validationState,
    required Map<String, int> currentQuantities,
  }) async {
    // Vérifier que le container est assigné à l'événement
    if (!event.assignedContainers.contains(code)) {
      DebugLog.info('[QRCodeProcessingService] Container $code not found in event');
      return QRCodeProcessResult.notFoundInEvent(code);
    }

    final container = containerCache[code];
    if (container == null) {
      return QRCodeProcessResult.error('Container introuvable dans le cache');
    }

    // Traiter tous les équipements enfants
    final updatedValidation = <String, bool>{};
    final updatedQuantities = <String, int>{};
    int processedCount = 0;

    for (final childId in container.equipmentIds) {
      final childEventEq = event.assignedEquipment
          .cast<EventEquipment?>()
          .firstWhere(
            (eq) => eq?.equipmentId == childId,
            orElse: () => null,
          );

      if (childEventEq == null) continue;

      final childEquipment = equipmentCache[childId];

      // Si quantitatif, mettre la quantité actuelle = quantité cible
      if (childEquipment?.hasQuantity ?? false) {
        final targetQty = _getTargetQuantity(childEventEq, step);
        updatedQuantities[childId] = targetQty;
      }

      // Cocher l'enfant
      updatedValidation[childId] = true;
      processedCount++;
    }

    if (processedCount == 0) {
      return QRCodeProcessResult.error(
        'Aucun équipement trouvé dans le container ${container.name}',
      );
    }

    return QRCodeProcessResult.success(
      message: 'Container ${container.name} : $processedCount équipement(s) validé(s) ✓',
      affectedEquipmentIds: updatedValidation.keys.toList(),
      updatedValidationState: updatedValidation,
      updatedQuantities: updatedQuantities.isNotEmpty ? updatedQuantities : null,
    );
  }

  /// Obtenir la quantité requise selon l'étape
  /// Logique: chaque étape utilise la quantité actuelle de l'étape N-1
  int _getTargetQuantity(EventEquipment eventEquipment, dynamic step) {
    // Convertir l'enum en string pour comparer
    final stepString = step.toString().split('.').last;

    switch (stepString) {
      case 'preparation':
        // Étape 1 : Quantité définie à la création de l'événement
        return eventEquipment.quantity;

      case 'loadingOutbound':
        // Étape 2 : Quantité validée à l'étape 1 (préparation)
        return eventEquipment.quantityAtPreparation ?? eventEquipment.quantity;

      case 'unloadingReturn':
        // Étape 3 : Quantité validée à l'étape 2 (chargement)
        return eventEquipment.quantityAtLoading ??
            eventEquipment.quantityAtPreparation ??
            eventEquipment.quantity;

      case 'return_':
        // Étape 4 : Quantité validée à l'étape 3 (déchargement)
        return eventEquipment.quantityAtUnloading ??
            eventEquipment.quantityAtLoading ??
            eventEquipment.quantityAtPreparation ??
            eventEquipment.quantity;

      default:
        return eventEquipment.quantity;
    }
  }
}
