import 'dart:async';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart';
import 'package:hl_lieferservice/feature/car_selection/bloc/state.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/phase_bloc.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/phase_event.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_bloc.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_event.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_state.dart';
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_fail_page.dart';
import 'package:hl_lieferservice/feature/loading/model/loading_group.dart';
import 'package:hl_lieferservice/feature/loading/util/loading_order.dart';
import 'package:hl_lieferservice/feature/loading/widget/article_row.dart';
import 'package:hl_lieferservice/feature/loading/widget/hold_selection_dialog.dart';
import 'package:hl_lieferservice/feature/loading/widget/reason_picker_dialog.dart';
import 'package:hl_lieferservice/feature/scan/presentation/scanner.dart';
import 'package:hl_lieferservice/feature/settings/bloc/settings_bloc.dart';
import 'package:hl_lieferservice/feature/settings/bloc/settings_state.dart';
import 'package:hl_lieferservice/model/article.dart';
import 'package:hl_lieferservice/model/delivery.dart';
import 'package:hl_lieferservice/model/tour.dart';
import 'package:hl_lieferservice/widget/home/presentation/home_drawer.dart';
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';

/// Detail-Ansicht für genau einen Kunden während der Beladen-Phase.
///
/// Aufgaben:
/// * Scanner-Widget oben — alle erkannten Barcodes werden direkt der
///   aktuellen Lieferung zugeordnet (keine Kunden-Auswahl mehr, da bereits
///   pro Kunde gefiltert).
/// * Übergangs-Dialog "Alle gescannt → Übersicht / Tour starten" pro
///   Kunde, einmalig.
/// * Aktions-Menü (im Customer-Header) zum Abbrechen / Zurückhalten von
///   Artikeln.
/// * Navigation: ein einziges Zurück-Symbol oben rechts in der AppBar
///   (führt per Pop zurück auf die Übersicht). Keine Pfeil-Navigation,
///   kein Phasen-Stepper.
///
/// Hold-State:
/// Solange das Backend für `reportItemHeld` nur ein Stub ist, lebt der
/// Hold-Zustand ausschließlich im lokalen State dieses Widgets. Die UI
/// blendet betroffene Positionen entsprechend aus. Bei einem echten
/// Backend würde der Stream das Hold-Flag in der Delivery-Datenstruktur
/// mitliefern und dieser lokale Cache fiele weg.
class LoadingCustomerPage extends StatefulWidget {
  const LoadingCustomerPage({
    super.key,
    this.initialIndex = 0,
  });

  /// Index in der Beladereihenfolge, mit dem die Page öffnet. Wird typischer-
  /// weise von der Overview-Page mit dem getappten Kunden-Index gesetzt.
  final int initialIndex;

  @override
  State<LoadingCustomerPage> createState() => _LoadingCustomerPageState();
}

class _LoadingCustomerPageState extends State<LoadingCustomerPage> {
  /// Index des aktuell sichtbaren Kunden innerhalb der Beladereihenfolge.
  late int _currentIndex;

  /// Trackt Kunden, für die der "alle gescannt"-Dialog bereits gezeigt
  /// wurde — verhindert erneutes Auftauchen beim Re-Besuch.
  final Set<String> _completedCustomersShown = <String>{};

  /// Hardware-Scanner-Buffer (analog zur alten ScanPage).
  final FocusNode _focusNode = FocusNode();
  String _buffer = '';
  Timer? _bufferTimer;

  /// Lokaler Hold-Cache (siehe Klassen-Doc). Schlüssel über [HoldKey].
  /// Aufgeteilt nach Delivery-ID, damit beim Wechsel zwischen Kunden nichts
  /// vermischt wird.
  final Map<String, Set<String>> _heldKeys = <String, Set<String>>{};

  /// Aktuell gewähltes Fahrzeug. Wird über den CarSelectBloc synchronisiert,
  /// einmalig in initState bevor erster build.
  int? _selectedCarId;

  /// Erkennt den Übergang "Lieferung läuft → abgeschlossen", damit der
  /// Listener auch dann robust reagiert, wenn der TourBloc zwischendurch
  /// rebuilded (z. B. wegen pendingScanRequests).
  bool? _lastCompletionFlag;

  @override
  void initState() {
    super.initState();
    _currentIndex = widget.initialIndex;

    WidgetsBinding.instance.addPostFrameCallback((_) => _focusNode.requestFocus());

    final carState = context.read<CarSelectBloc>().state;
    if (carState is CarSelectComplete) {
      _selectedCarId = carState.selectedCar.id;
    }
  }

  @override
  void dispose() {
    _focusNode.dispose();
    _bufferTimer?.cancel();
    super.dispose();
  }

  // ---------------------------------------------------------------------------
  // Scanner-Eingang
  // ---------------------------------------------------------------------------

  void _handleKey(KeyEvent event) {
    if (event is! KeyDownEvent) return;
    if (event.logicalKey == LogicalKeyboardKey.enter) {
      _bufferTimer?.cancel();
      if (_buffer.isNotEmpty) {
        _handleBarcodeScanned(_buffer);
        _buffer = '';
      }
    } else {
      final character = event.character;
      if (character != null && character.isNotEmpty) {
        _buffer += character;
        _bufferTimer?.cancel();
        _bufferTimer = Timer(const Duration(milliseconds: 1000), () {
          if (_buffer.isNotEmpty) {
            _handleBarcodeScanned(_buffer);
            _buffer = '';
          }
        });
      }
    }
  }

  /// Extrahiert die Artikelnummer aus einem Barcode der Form
  /// `<artikelnummer>;<kundennummer>;<belegnummer>`. Liefert null bei
  /// ungültigem Format. Konsistent mit der Logik aus der alten ScanPage.
  String? _extractArticleNumber(String barcode) {
    debugPrint("QR CODE: $barcode");
    final parts = barcode.split(';');
    if (parts.length != 3) return null;
    final articleNumber = parts[0].trim();
    if (articleNumber.isEmpty) return null;
    return articleNumber;
  }

  void _handleBarcodeScanned(String barcode) {
    if (!mounted) return;
    if (_selectedCarId == null) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Kein Fahrzeug ausgewählt"),
          );
      return;
    }

    final articleNumber = _extractArticleNumber(barcode);
    if (articleNumber == null) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Ungültiger Barcode: $barcode"),
          );
      return;
    }

    final tourState = context.read<TourBloc>().state;
    if (tourState is! TourLoaded) return;

    // Wir richten den Scan immer an den aktuell sichtbaren Kunden — anders
    // als in der alten ScanPage gibt es keine kundenübergreifende
    // Disambiguierung mehr, weil die Page kundenfokussiert ist.
    final groups = _buildLoadingGroups(tourState);
    if (_currentIndex < 0 || _currentIndex >= groups.length) return;
    final current = groups[_currentIndex];
    final delivery = current.delivery;

    if (delivery.state == DeliveryState.canceled) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Lieferung wurde abgebrochen"),
          );
      return;
    }

    // 1) Komponenten-Match zuerst (Stückliste).
    final parent = delivery.findParentOfComponent(articleNumber);
    if (parent != null) {
      final comp = parent.findComponent(articleNumber);
      if (comp == null) return;
      final heldSet = _heldKeys[delivery.id] ?? const <String>{};
      if (heldSet.contains(HoldKey.component(parent, comp))) {
        context.read<OperationBloc>().add(
              FailOperation(message: "Komponente ist zurückgehalten"),
            );
        return;
      }
      if (comp.isFullyScanned) {
        context.read<OperationBloc>().add(
              FailOperation(message: "Komponente bereits vollständig gescannt"),
            );
        return;
      }
      context.read<TourBloc>().add(ScanComponentEvent(
            componentArticleNumber: articleNumber,
            carId: _selectedCarId!.toString(),
            deliveryId: delivery.id,
          ));
      return;
    }

    // 2) Regulärer Artikel-Scan auf den aktuellen Kunden.
    final article = delivery.articles.firstWhereOrNull(
      (a) => a.articleNumber == articleNumber && !a.isParent,
    );
    if (article == null) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Artikel gehört nicht zu diesem Kunden"),
          );
      return;
    }
    final heldSet = _heldKeys[delivery.id] ?? const <String>{};
    if (heldSet.contains(HoldKey.article(article))) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Artikel ist zurückgehalten"),
          );
      return;
    }
    if (article.scannedAmount + article.scannedRemovedAmount >=
        article.amount) {
      context.read<OperationBloc>().add(
            FailOperation(message: "Artikel bereits vollständig gescannt"),
          );
      return;
    }
    context.read<TourBloc>().add(ScanArticleEvent(
          articleNumber: articleNumber,
          carId: _selectedCarId!.toString(),
          deliveryId: delivery.id,
        ));
  }

  // ---------------------------------------------------------------------------
  // Datenaufbau
  // ---------------------------------------------------------------------------

  String? _lookupCarPlate(int? carId, Tour tour) {
    if (carId == null) return null;
    return tour.driver.cars.firstWhereOrNull((c) => c.id == carId)?.plate;
  }

  /// Beladereihenfolge inkl. abgebrochener Lieferungen für die UI-Anzeige
  /// (sichtbar, ausgegraut). Pfeil-Navigation darf sie durchscrollen.
  List<LoadingGroup> _buildLoadingGroups(TourLoaded state) {
    final carIdStr = _selectedCarId?.toString() ?? "";
    final orderedIds = LoadingOrder.computeForCar(
      state: state,
      carIdStr: carIdStr,
    );

    final byId = {for (final d in state.tour.deliveries) d.id: d};
    final groups = <LoadingGroup>[];
    for (final id in orderedIds) {
      final delivery = byId[id];
      if (delivery == null) continue;
      if (delivery.state == DeliveryState.finished) continue;
      final scannable =
          delivery.articles.where((a) => a.scannable).toList(growable: false);
      if (scannable.isEmpty && delivery.state != DeliveryState.canceled) {
        continue;
      }
      groups.add(LoadingGroup(
        delivery: delivery,
        articles: scannable,
        carPlate: _lookupCarPlate(delivery.carId, state.tour),
      ));
    }
    return groups;
  }

  /// `true`, wenn die Artikel-Position aus dem Standardlager kommt
  /// (warehouseNr `null` oder `"0"`). Außenlager-Artikel werden hier nicht
  /// betrachtet, weil sie nicht in der Belade-Halle scannbar sind — der
  /// Fahrer holt sie erst beim Kundenbesuch ab.
  bool _isStandardWarehouse(Article a) {
    final nr = a.warehouseNr;
    return nr == null || nr.isEmpty || nr == "0";
  }

  /// Aktive Artikel = Standardlager-Artikel, die NICHT zurückgehalten
  /// sind. Außenlager-Artikel werden grundsätzlich ausgeschlossen, weil
  /// sie nicht in der Beladen-Phase scannbar sind.
  List<Article> _activeArticlesOf(LoadingGroup g) {
    final held = _heldKeys[g.delivery.id] ?? const <String>{};
    return g.articles.where((a) {
      if (!_isStandardWarehouse(a)) return false;
      if (held.contains(HoldKey.article(a))) return false;
      // Komponenten-Hold deaktiviert den Artikel nicht komplett — wir
      // werten in [_isLogicallyComplete] über die nicht-gehaltenen
      // Komponenten aus.
      return true;
    }).toList(growable: false);
  }

  /// `true`, wenn — unter Ausschluss der zurückgehaltenen Positionen UND
  /// der Außenlager-Artikel — alle scannbaren Einheiten der Lieferung
  /// gescannt sind. Berücksichtigt Komponenten-Holds individuell.
  bool _isLogicallyComplete(LoadingGroup g) {
    final held = _heldKeys[g.delivery.id] ?? const <String>{};
    final standardArticles =
        g.articles.where(_isStandardWarehouse).toList(growable: false);

    // Edge-Case 1: Lieferung hat überhaupt keine Standardlager-Artikel
    // (alles extern) → in der Beladen-Phase nichts zu tun → fertig.
    if (standardArticles.isEmpty) return g.articles.isNotEmpty;

    final actives = _activeArticlesOf(g);
    if (actives.isEmpty) {
      // Edge-Case 2: alle Standardlager-Artikel sind zurückgehalten → der
      // Fahrer hat alles gemeldet, hier ist nichts mehr zu scannen.
      return true;
    }
    for (final a in actives) {
      if (a.isParent && a.components.isNotEmpty) {
        for (final c in a.components) {
          if (held.contains(HoldKey.component(a, c))) continue;
          if (!c.isFullyScanned) return false;
        }
      } else {
        if (!a.isFullyScanned) return false;
      }
    }
    return true;
  }

  /// Zähler-Tupel "x von y Artikeln gescannt" — bezieht sich auf das
  /// Standardlager und ignoriert zurückgehaltene Positionen, damit der
  /// Fortschritt für den Fahrer realistisch bleibt.
  ({int done, int total}) _progressOf(LoadingGroup g) {
    final held = _heldKeys[g.delivery.id] ?? const <String>{};
    int done = 0;
    int total = 0;
    for (final a in g.articles) {
      if (!_isStandardWarehouse(a)) continue;
      if (a.isParent && a.components.isNotEmpty) {
        for (final c in a.components) {
          if (held.contains(HoldKey.component(a, c))) continue;
          total += 1;
          if (c.isFullyScanned) done += 1;
        }
      } else {
        if (held.contains(HoldKey.article(a))) continue;
        total += 1;
        if (a.isFullyScanned) done += 1;
      }
    }
    return (done: done, total: total);
  }

  // ---------------------------------------------------------------------------
  // Navigation
  // ---------------------------------------------------------------------------

  void _openOverview() {
    // Die Übersicht ist der Root-Render der Beladen-Phase (siehe home.dart).
    // Aus dem Vollbild-Kunden kehrt der Fahrer deshalb per pop dorthin
    // zurück — kein erneuter Push, damit der Stack flach bleibt.
    Navigator.of(context).pop();
  }

  void _startTour() {
    final carState = context.read<CarSelectBloc>().state;
    if (carState is CarSelectComplete) {
      context.read<PhaseBloc>().add(
            PhaseSet(
              carId: carState.selectedCar.id.toString(),
              phase: DeliveryPhase.ausliefern,
            ),
          );
    }
  }

  // ---------------------------------------------------------------------------
  // Dialoge & Aktionen
  // ---------------------------------------------------------------------------

  Future<void> _maybeShowCompletionDialog(
    LoadingGroup current,
    List<LoadingGroup> allGroups,
  ) async {
    if (_completedCustomersShown.contains(current.delivery.id)) return;

    _completedCustomersShown.add(current.delivery.id);

    // "Tour starten"-Variante zeigen, wenn nach Abschluss dieses Kunden
    // alle anderen aktiven Lieferungen ebenfalls fertig sind. Abgebrochene
    // Lieferungen zählen nicht. Wir prüfen das auf Basis der gebauten
    // Gruppen, weil _heldKeys lokal lebt und in _isLogicallyComplete
    // berücksichtigt wird.
    final allDone = allGroups.every((g) =>
        g.delivery.state == DeliveryState.canceled ||
        _isLogicallyComplete(g));

    final navigator = Navigator.of(context, rootNavigator: true);

    // Wir warten einen Frame, damit der TourBloc-Listener zuende ist und
    // dann der Dialog im stabilen UI-Zustand erscheint.
    await Future<void>.delayed(Duration.zero);
    if (!mounted) return;

    if (allDone) {
      final choice = await showDialog<_CompletionChoice>(
        context: navigator.context,
        builder: (ctx) => AlertDialog(
          title: const Text("Beladung abgeschlossen"),
          content: const Text(
            "Alle Lieferungen sind verladen. Tour jetzt starten?",
          ),
          actions: [
            TextButton(
              onPressed: () =>
                  Navigator.of(ctx).pop(_CompletionChoice.overview),
              child: const Text("Übersicht"),
            ),
            FilledButton(
              onPressed: () =>
                  Navigator.of(ctx).pop(_CompletionChoice.startTour),
              child: const Text("Tour starten"),
            ),
          ],
        ),
      );
      if (!mounted || choice == null) return;
      switch (choice) {
        case _CompletionChoice.overview:
          _openOverview();
          break;
        case _CompletionChoice.startTour:
          _startTour();
          break;
      }
    } else {
      final choice = await showDialog<_CompletionChoice>(
        context: navigator.context,
        builder: (ctx) => AlertDialog(
          title: const Text("Alle Artikel gescannt"),
          content: Text(
            "Alle Artikel für ${current.delivery.customer.name} wurden "
            "gescannt. Zurück zur Übersicht?",
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(ctx).pop(null),
              child: const Text("Bleiben"),
            ),
            FilledButton(
              onPressed: () =>
                  Navigator.of(ctx).pop(_CompletionChoice.overview),
              child: const Text("Zur Übersicht"),
            ),
          ],
        ),
      );
      if (!mounted || choice == null) return;
      if (choice == _CompletionChoice.overview) {
        _openOverview();
      }
    }
  }

  Future<void> _cancelDeliveryFlow(LoadingGroup current) async {
    final reason = await ReasonPickerDialog.show(
      context,
      title: "Lieferung abbrechen",
      subtitle: current.delivery.customer.name,
    );
    if (!mounted || reason == null) return;

    // CancelDeliveryEvent feuert den bestehenden tourRepository.cancelDelivery
    // Aufruf. Parallel der ProcessRepository-Stub für die Audit-Spur des
    // Grunds — bewusst nicht über den TourBloc geleitet, weil der Bloc den
    // Grund aktuell nicht kennt und sich der Stub-Aufruf nicht in den Tour-
    // Stream einklinkt. Sobald ein realer Endpoint existiert, kann das in
    // einen erweiterten Event-Handler gewandert werden.
    final tourBloc = context.read<TourBloc>();
    tourBloc.add(CancelDeliveryEvent(deliveryId: current.delivery.id));

    final processRepository = tourBloc.processRepository;
    unawaited(processRepository.reportDeliveryCancelled(
      deliveryId: current.delivery.id,
      reason: reason,
    ));
  }

  Future<void> _holdItemsFlow(LoadingGroup current) async {
    final alreadyHeld =
        _heldKeys[current.delivery.id] ?? const <String>{};
    final selected = await HoldSelectionDialog.show(
      context,
      customerName: current.delivery.customer.name,
      articles: current.articles,
      alreadyHeld: alreadyHeld,
    );
    if (!mounted || selected == null || selected.isEmpty) return;

    final reason = await ReasonPickerDialog.show(
      context,
      title: "Artikel zurückhalten",
      subtitle:
          "${selected.length} Position(en) für ${current.delivery.customer.name}",
    );
    if (!mounted || reason == null) return;

    final processRepository = context.read<TourBloc>().processRepository;
    final newHeld = {...alreadyHeld};
    for (final item in selected) {
      unawaited(processRepository.reportItemHeld(
        deliveryId: current.delivery.id,
        articleId: item.article.internalId.toString(),
        componentId: item.component?.articleNumber,
        reason: reason,
      ));
      newHeld.add(item.key);
    }
    setState(() {
      _heldKeys[current.delivery.id] = newHeld;
      // Hold-Status kann logische Vollständigkeit auslösen → Erkennung neu.
      _lastCompletionFlag = null;
    });
  }

  // ---------------------------------------------------------------------------
  // UI
  // ---------------------------------------------------------------------------

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<CarSelectBloc, CarSelectState>(
      listener: (context, carState) {
        if (carState is CarSelectComplete) {
          setState(() => _selectedCarId = carState.selectedCar.id);
        }
      },
      builder: (context, carState) {
        return BlocConsumer<TourBloc, TourState>(
          listener: (context, tourState) {
            if (tourState is! TourLoaded) return;
            final groups = _buildLoadingGroups(tourState);
            if (_currentIndex >= groups.length && groups.isNotEmpty) {
              _currentIndex = groups.length - 1;
            }
            if (groups.isEmpty) return;

            final current = groups[_currentIndex.clamp(0, groups.length - 1)];
            if (current.delivery.state == DeliveryState.canceled) {
              _lastCompletionFlag = null;
              return;
            }

            final isComplete = _isLogicallyComplete(current);
            // Übergang false → true erkannt → Dialog (einmalig pro Kunde).
            if (_lastCompletionFlag == false && isComplete) {
              _maybeShowCompletionDialog(current, groups);
            }
            _lastCompletionFlag = isComplete;
          },
          builder: (context, tourState) {
            if (tourState is TourLoadingFailed) {
              return const DeliveryLoadingFailedPage();
            }
            if (tourState is! TourLoaded) {
              return const Scaffold(
                body: Center(child: CircularProgressIndicator()),
              );
            }

            final settingsState = context.read<SettingsBloc>().state;
            final useHardwareScanner = settingsState is AppSettingsLoaded &&
                settingsState.settings.useHardwareScanner;

            final groups = _buildLoadingGroups(tourState);
            return _buildScaffold(
              tourState: tourState,
              groups: groups,
              useHardwareScanner: useHardwareScanner,
            );
          },
        );
      },
    );
  }

  Widget _buildScaffold({
    required TourLoaded tourState,
    required List<LoadingGroup> groups,
    required bool useHardwareScanner,
  }) {
    final hasGroups = groups.isNotEmpty;
    final safeIndex = hasGroups ? _currentIndex.clamp(0, groups.length - 1) : 0;
    final current = hasGroups ? groups[safeIndex] : null;
    final isCanceled = current?.delivery.state == DeliveryState.canceled;

    final theme = Theme.of(context);
    return Scaffold(
      drawer: const HomeAppDrawer(),
      appBar: AppBar(
        backgroundColor: theme.primaryColor,
        foregroundColor: theme.colorScheme.onPrimary,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          tooltip: "Zurück",
          onPressed: () => Navigator.of(context).pop(),
        ),
        title: const Text(
          "Lieferdetails",
          style: TextStyle(fontWeight: FontWeight.w600),
        ),
        actions: const [
          _AppBarPlateBadge(),
          SizedBox(width: 8),
        ],
      ),
      body: KeyboardListener(
        focusNode: _focusNode,
        onKeyEvent: _handleKey,
        child: SafeArea(
          top: false,
          child: !hasGroups
              ? const _EmptyState()
              : _buildCustomerView(
                  tourState: tourState,
                  groups: groups,
                  safeIndex: safeIndex,
                  current: current!,
                  isCanceled: isCanceled,
                  useHardwareScanner: useHardwareScanner,
                ),
        ),
      ),
    );
  }

  Widget _buildCustomerView({
    required TourLoaded tourState,
    required List<LoadingGroup> groups,
    required int safeIndex,
    required LoadingGroup current,
    required bool isCanceled,
    required bool useHardwareScanner,
  }) {
    final progress = _progressOf(current);
    final heldSet = _heldKeys[current.delivery.id] ?? const <String>{};
    final theme = Theme.of(context);

    return Column(
      children: [
        if (tourState.pendingScanRequests > 0) const LinearProgressIndicator(),
        _ScannerSlot(
          isCanceled: isCanceled,
          useHardwareScanner: useHardwareScanner,
          onBarcode: _handleBarcodeScanned,
        ),
        _CustomerHeader(
          position: safeIndex + 1,
          total: groups.length,
          current: current,
          progress: progress,
          isCanceled: isCanceled,
          actionMenuBuilder: (ctx) => _buildAppBarMenu(ctx, current),
        ),
        const Divider(height: 1),
        Expanded(
          child: Opacity(
            opacity: isCanceled ? 0.45 : 1.0,
            child: ListView(
              padding: const EdgeInsets.only(top: 4, bottom: 16),
              children: [
                for (final section in _groupArticlesByWarehouse(current.articles)) ...[
                  _WarehouseSectionHeader(
                    label: section.label,
                    isExternal: section.isExternal,
                  ),
                  for (final article in section.articles)
                    ArticleRow(
                      article: article,
                      isHeld: heldSet.contains(HoldKey.article(article)),
                      disabled: isCanceled,
                      heldComponents: heldSet,
                    ),
                ],
                if (isCanceled)
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Container(
                      padding: const EdgeInsets.all(12),
                      decoration: BoxDecoration(
                        color: Colors.red.withValues(alpha: 0.06),
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(
                            color: Colors.red.withValues(alpha: 0.3)),
                      ),
                      child: Row(
                        children: [
                          const Icon(Icons.cancel_outlined, color: Colors.red),
                          const SizedBox(width: 12),
                          Expanded(
                            child: Text(
                              "Diese Lieferung wurde abgebrochen und wird "
                              "heute nicht ausgeliefert.",
                              style: theme.textTheme.bodyMedium,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  PopupMenuButton<_MenuAction> _buildAppBarMenu(
    BuildContext context,
    LoadingGroup current,
  ) {
    return PopupMenuButton<_MenuAction>(
      icon: const Icon(Icons.more_vert),
      tooltip: "Weitere Aktionen",
      onSelected: (action) async {
        switch (action) {
          case _MenuAction.cancel:
            await _cancelDeliveryFlow(current);
            break;
          case _MenuAction.hold:
            await _holdItemsFlow(current);
            break;
        }
      },
      itemBuilder: (ctx) => [
        PopupMenuItem(
          value: _MenuAction.hold,
          enabled: current.delivery.state != DeliveryState.canceled,
          child: const ListTile(
            dense: true,
            contentPadding: EdgeInsets.zero,
            leading: Icon(Icons.pause_circle_outline),
            title: Text("Artikel nicht heute liefern"),
          ),
        ),
        PopupMenuItem(
          value: _MenuAction.cancel,
          enabled: current.delivery.state != DeliveryState.canceled,
          child: const ListTile(
            dense: true,
            contentPadding: EdgeInsets.zero,
            leading: Icon(Icons.cancel_outlined, color: Colors.red),
            title: Text(
              "Lieferung komplett abbrechen",
              style: TextStyle(color: Colors.red),
            ),
          ),
        ),
      ],
    );
  }
}

// ---------------------------------------------------------------------------
// Helper-Widgets
// ---------------------------------------------------------------------------

class _EmptyState extends StatelessWidget {
  const _EmptyState();

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.inbox_outlined, size: 64, color: scheme.onSurfaceVariant),
          const SizedBox(height: 12),
          Text(
            "Keine Lieferungen zum Beladen",
            style: Theme.of(context).textTheme.titleMedium,
          ),
          const SizedBox(height: 6),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 32),
            child: Text(
              "Für das ausgewählte Fahrzeug ist die Beladereihenfolge leer.",
              textAlign: TextAlign.center,
              style: TextStyle(color: scheme.onSurfaceVariant),
            ),
          ),
        ],
      ),
    );
  }
}

class _ScannerSlot extends StatelessWidget {
  const _ScannerSlot({
    required this.isCanceled,
    required this.useHardwareScanner,
    required this.onBarcode,
  });

  final bool isCanceled;
  final bool useHardwareScanner;
  final void Function(String) onBarcode;

  @override
  Widget build(BuildContext context) {
    if (isCanceled) {
      return Container(
        height: 110,
        margin: const EdgeInsets.fromLTRB(12, 8, 12, 4),
        decoration: BoxDecoration(
          color: Colors.grey.withValues(alpha: 0.15),
          borderRadius: BorderRadius.circular(8),
          border: Border.all(color: Colors.grey.withValues(alpha: 0.4)),
        ),
        child: const Center(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 16),
            child: Text(
              "Diese Lieferung wurde abgebrochen.",
              style: TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.w600,
                  color: Colors.black54),
              textAlign: TextAlign.center,
            ),
          ),
        ),
      );
    }
    if (useHardwareScanner) {
      // Hardware-Scanner liefert Eingaben über den KeyboardListener.
      // Wir zeigen einen kompakten Hinweis-Bereich statt der Kamera.
      return Container(
        height: 60,
        margin: const EdgeInsets.fromLTRB(12, 8, 12, 4),
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: Theme.of(context).colorScheme.surfaceContainerHighest,
          borderRadius: BorderRadius.circular(8),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: const [
            Icon(Icons.qr_code_scanner_outlined, size: 18),
            SizedBox(width: 8),
            Text("Bereit für Hardware-Scan"),
          ],
        ),
      );
    }
    return Padding(
      padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
      child: BarcodeScannerWidget(onBarcodeDetected: onBarcode),
    );
  }
}

class _CustomerHeader extends StatelessWidget {
  const _CustomerHeader({
    required this.position,
    required this.total,
    required this.current,
    required this.progress,
    required this.isCanceled,
    required this.actionMenuBuilder,
  });

  final int position;
  final int total;
  final LoadingGroup current;
  final ({int done, int total}) progress;
  final bool isCanceled;
  final Widget Function(BuildContext) actionMenuBuilder;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final color = isCanceled ? Colors.red.shade400 : theme.colorScheme.primary;

    return Padding(
      padding: const EdgeInsets.fromLTRB(16, 6, 4, 8),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          CircleAvatar(
            backgroundColor: color,
            foregroundColor: theme.colorScheme.onPrimary,
            child: Text(
              "$position",
              style: const TextStyle(fontWeight: FontWeight.bold),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Row(
                  children: [
                    Expanded(
                      child: Text(
                        current.delivery.customer.name,
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                          decoration: isCanceled
                              ? TextDecoration.lineThrough
                              : TextDecoration.none,
                        ),
                      ),
                    ),
                    Text(
                      "Kunde $position/$total",
                      style: theme.textTheme.bodySmall?.copyWith(
                        color: theme.colorScheme.onSurfaceVariant,
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 2),
                Text(
                  current.delivery.customer.address.toString(),
                  style: TextStyle(
                    fontSize: 12,
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                ),
                const SizedBox(height: 6),
                Text(
                  isCanceled
                      ? "Lieferung abgebrochen"
                      : "${progress.done}/${progress.total} Artikel gescannt",
                  style: TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w600,
                    color: isCanceled
                        ? Colors.red.shade700
                        : (progress.done == progress.total && progress.total > 0
                            ? Colors.green.shade700
                            : theme.colorScheme.onSurface),
                  ),
                ),
              ],
            ),
          ),
          actionMenuBuilder(context),
        ],
      ),
    );
  }
}

/// Eine Lager-Sektion in der Artikel-Liste — Header + zugehörige Artikel.
class _WarehouseSection {
  const _WarehouseSection({
    required this.label,
    required this.isExternal,
    required this.articles,
  });

  final String label;
  final bool isExternal;
  final List<Article> articles;
}

/// Gruppiert Artikel nach Lager. Standardlager (Nummer "0" oder leer/null)
/// landet IMMER an erster Stelle — auch wenn keine Artikel dort liegen,
/// taucht der Header eines aktiven Außenlagers darunter konsistent
/// auf. Außenlager folgen alphabetisch nach Label.
List<_WarehouseSection> _groupArticlesByWarehouse(List<Article> articles) {
  const standardKey = "_STD";
  final Map<String, List<Article>> byKey = {};
  final Map<String, String> labels = {};

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

  for (final a in articles) {
    final external = isExternal(a.warehouseNr);
    final key = external ? a.warehouseNr! : standardKey;
    final label = external
        ? ((a.warehouseName?.isNotEmpty ?? false)
            ? a.warehouseName!
            : "Lager ${a.warehouseNr}")
        : "Standardlager";
    byKey.putIfAbsent(key, () => <Article>[]).add(a);
    labels[key] = label;
  }

  final keys = byKey.keys.toList();
  keys.sort((a, b) {
    // Standardlager IMMER ganz oben.
    if (a == standardKey) return -1;
    if (b == standardKey) return 1;
    return labels[a]!.compareTo(labels[b]!);
  });

  return [
    for (final k in keys)
      _WarehouseSection(
        label: labels[k]!,
        isExternal: k != standardKey,
        articles: byKey[k]!,
      ),
  ];
}

/// Voller-Breite-Header über einer Lager-Sektion. Standardlager neutral,
/// Außenlager in deutlichem Orange-Akzent — damit der Fahrer beim Scrollen
/// sofort sieht, wo er noch hinfahren muss.
class _WarehouseSectionHeader extends StatelessWidget {
  const _WarehouseSectionHeader({
    required this.label,
    required this.isExternal,
  });

  final String label;
  final bool isExternal;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final Color bg;
    final Color fg;
    final IconData icon;

    if (isExternal) {
      bg = Colors.deepOrange.withValues(alpha: 0.15);
      fg = Colors.deepOrange.shade800;
      icon = Icons.warehouse_outlined;
    } else {
      bg = theme.colorScheme.surfaceContainerHighest;
      fg = theme.colorScheme.onSurfaceVariant;
      icon = Icons.home_work_outlined;
    }

    return Container(
      margin: const EdgeInsets.fromLTRB(0, 8, 0, 4),
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        color: bg,
        border: isExternal
            ? Border(
                left: BorderSide(color: Colors.deepOrange.shade700, width: 4),
              )
            : null,
      ),
      child: Row(
        children: [
          Icon(icon, size: 18, color: fg),
          const SizedBox(width: 8),
          Expanded(
            child: Text(
              isExternal ? "Außenlager: $label" : label,
              style: TextStyle(
                fontSize: 13,
                fontWeight: FontWeight.w700,
                color: fg,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

/// Plate-Badge für die AppBar — liest das aktiv gewählte Fahrzeug aus dem
/// [CarSelectBloc]. Nutzt einen halbtransparenten Hintergrund, damit das
/// Badge auch auf der Primary-Color-AppBar gut lesbar bleibt.
class _AppBarPlateBadge extends StatelessWidget {
  const _AppBarPlateBadge();

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CarSelectBloc, CarSelectState>(
      builder: (context, state) {
        if (state is! CarSelectComplete) return const SizedBox.shrink();
        final onPrimary = Theme.of(context).colorScheme.onPrimary;
        return Center(
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
            decoration: BoxDecoration(
              color: onPrimary.withValues(alpha: 0.18),
              borderRadius: BorderRadius.circular(8),
            ),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.local_shipping, size: 16, color: onPrimary),
                const SizedBox(width: 6),
                Text(
                  state.selectedCar.plate,
                  style: TextStyle(
                    color: onPrimary,
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

enum _MenuAction { cancel, hold }

enum _CompletionChoice { overview, startTour }
