import 'package:hl_lieferservice/model/article.dart';
import 'package:hl_lieferservice/model/delivery.dart';

/// Aggregiert eine [Delivery] mit ihren scannbaren Artikeln zu einer
/// Belade-Einheit. Bildet die Datenbasis für die Beladen-Phase (Vollbild-
/// Kunde + Übersicht).
///
/// **Wichtige Geschäftslogik:** Für die Frage "ist diese Lieferung fertig
/// beladen?" zählen nur Artikel aus dem **Standardlager** (warehouseNr
/// `null` oder `"0"`). Außenlager-Artikel werden separat beim
/// Kundenbesuch in der Ausliefer-Phase abgeholt — sie blockieren also
/// nicht den Beladen-Abschluss. Konsequenz: alle Counter-Getter
/// ([totalArticles], [completeArticles], [scannedUnits], [totalUnits],
/// [isComplete], [isPartial], [hasAnyScanned]) ignorieren Außenlager-
/// Artikel. Wer alle Artikel braucht, greift direkt auf [articles] zu.
///
/// Aufrufer füllen [articles] mit den scannbaren Artikeln dieser Lieferung
/// (also nicht alle Artikel der Lieferung — vorgefiltert über
/// `Article.scannable`).
class LoadingGroup {
  /// Die zugrundeliegende Lieferung (inkl. Kunde, Adresse, State).
  final Delivery delivery;

  /// Nummernschild des zugewiesenen Fahrzeugs zur Darstellung im Badge.
  /// `null` wenn die Lieferung noch keinem Auto zugeordnet ist.
  final String? carPlate;

  /// Die scannbaren Artikel der Lieferung (bereits vorgefiltert).
  final List<Article> articles;

  const LoadingGroup({
    required this.delivery,
    required this.articles,
    this.carPlate,
  });

  /// Alle Standardlager-Artikel (Lager-Nummer `null` oder `"0"`). Bildet
  /// die Basis aller Beladen-Counter, weil Außenlager-Ware nicht in der
  /// Belade-Halle scannbar ist.
  List<Article> get _standardArticles => articles
      .where((a) => !_isExternalWarehouse(a.warehouseNr))
      .toList(growable: false);

  /// Anzahl der scannbaren Standardlager-Artikel. Parent-Artikel zählen
  /// als 1 (nicht je Komponente).
  int get totalArticles => _standardArticles.length;

  /// Anzahl der vollständig gescannten Standardlager-Artikel. Bei Parent-
  /// Artikeln gilt "vollständig" = alle Komponenten vollständig.
  int get completeArticles =>
      _standardArticles.where((a) => a.isFullyScanned).length;

  /// Gesamtanzahl der erwarteten Einzelstücke aus dem Standardlager —
  /// bei Parent-Artikeln summiert über die Required-Amounts der
  /// Komponenten.
  int get totalUnits => _standardArticles.fold(0, (sum, a) {
        if (a.isParent && a.components.isNotEmpty) {
          return sum + a.components.fold(0, (s, c) => s + c.requiredAmount);
        }
        return sum + a.amount;
      });

  /// Bereits gescannte Einzelstücke aus dem Standardlager — analog zu
  /// [totalUnits].
  int get scannedUnits => _standardArticles.fold(0, (sum, a) {
        if (a.isParent && a.components.isNotEmpty) {
          return sum + a.components.fold(0, (s, c) => s + c.scannedAmount);
        }
        return sum + a.scannedAmount + a.scannedRemovedAmount;
      });

  /// `true`, wenn alle Standardlager-Artikel vollständig gescannt sind.
  ///
  /// Edge-Case: Lieferung **ohne** Standardlager-Artikel (alle Artikel
  /// liegen in Außenlagern) → automatisch fertig, weil in der Beladen-
  /// Phase nichts zu tun ist.
  bool get isComplete {
    if (articles.isEmpty) return false;
    if (_standardArticles.isEmpty) return true;
    return completeArticles == totalArticles;
  }

  /// `true`, wenn mindestens ein Stück gescannt wurde — egal ob Artikel
  /// vollständig oder nicht.
  bool get hasAnyScanned => scannedUnits > 0;

  /// `true`, wenn die Lieferung angefangen, aber nicht abgeschlossen wurde.
  bool get isPartial => hasAnyScanned && !isComplete;

  /// `true`, wenn mindestens ein Artikel der Lieferung NICHT aus dem
  /// Standard-Lager kommt. Standard-Lager hat die Nummer "0"; ein
  /// `warehouseNr == null` interpretieren wir als "nicht angegeben" und
  /// damit als Standard (kein False-Positive auf Datenlücken).
  bool get hasExternalWarehouseArticles =>
      articles.any((a) => _isExternalWarehouse(a.warehouseNr));

  /// Eindeutige Liste der Außenlager-Namen, die in dieser Lieferung
  /// vorkommen — für Badges/Hinweise in der Übersicht. Wenn ein Artikel
  /// nur eine `warehouseNr` aber keinen Namen hat, wird die Nummer als
  /// Fallback genommen.
  List<String> get externalWarehouseLabels {
    final labels = <String>{};
    for (final a in articles) {
      if (!_isExternalWarehouse(a.warehouseNr)) continue;
      final label = (a.warehouseName?.isNotEmpty ?? false)
          ? a.warehouseName!
          : "Lager ${a.warehouseNr}";
      labels.add(label);
    }
    return labels.toList(growable: false);
  }

  static bool _isExternalWarehouse(String? nr) =>
      nr != null && nr.isNotEmpty && nr != "0";
}
