/// Vom Treiber ausgelöstes Scan-Ereignis, bevor es serverseitig
/// angewendet wurde.
///
/// `clientScanId` ist ein vom Client generierter UUID-Schlüssel und dient
/// als **Idempotenz-Anker**: der Server speichert ihn beim ersten Apply
/// und antwortet auf jeden weiteren Request mit derselben Id mit
/// `duplicate` statt einer zweiten Anwendung. So bleibt Network-Retry
/// (z. B. nach Verbindungsabbruch beim ersten POST) bedeutungslos.
///
/// `clientScannedAt` ist die Wall-Clock-Zeit am Gerät zum Zeitpunkt des
/// Scans — der Server nutzt das nur als Audit-Spur, sortiert aber selbst
/// nach Server-Empfangszeit, sodass eine schiefe Uhr am Phone die
/// Reihenfolge nicht durcheinanderbringt.
class ScanIntent {
  const ScanIntent({
    required this.clientScanId,
    required this.clientScannedAt,
    required this.deliveryItemId,
    required this.action,
    this.actorCarId,
    this.reason,
    this.quantity,
    this.manual = false,
  });

  final String clientScanId;
  final DateTime clientScannedAt;
  final String deliveryItemId;
  final ScanAction action;

  /// `true`, wenn der Fahrer die Position manuell als geladen bestätigt hat
  /// (Fallback ohne Barcode). Reine Audit-Information; Default `false`.
  final bool manual;

  /// Menge für `remove` / `unremove` (Mengen-Gutschrift): wie viele Stück
  /// der Belegzeile gutgeschrieben bzw. wiederhergestellt werden. `null` =
  /// ganze Restmenge. Bei `scan`/`unscan`/`hold`/`unhold` ignoriert.
  final int? quantity;

  /// Fahrzeug, mit dem gescannt wurde — Audit-Spur. Optional, aber die
  /// App schickt ihn in der Loading-Phase immer mit, weil das Auto zu
  /// dem Zeitpunkt definitiv gewählt ist.
  final String? actorCarId;

  /// Klartext-Begründung. Bei `unscan` / `hold` / `remove` vom Backend
  /// erwartet, bei `scan` / `unhold` ignoriert.
  final String? reason;
}

/// Auswirkung eines Scan-Ereignisses auf die Pipeline eines Items.
/// Spiegel des Backend-Enums `AuditAction`.
///
/// `unremove` ist die Umkehrung von `remove`: setzt ein `Removed`-Item
/// zurück auf `InProgress` (oder `Done`, falls die Soll-Menge schon
/// erreicht war). Der ursprüngliche `remove`-Audit-Eintrag bleibt
/// erhalten — `unremove` erzeugt einen eigenen Eintrag, sodass die
/// Historie der Korrektur vollständig nachvollziehbar bleibt.
enum ScanAction { scan, unscan, hold, unhold, remove, unremove }

/// Ergebnis eines Apply-Versuchs vom Server.
class ScanOutcome {
  const ScanOutcome({
    required this.clientScanId,
    required this.status,
    this.deliveryItemId,
    this.reason,
  });

  final String clientScanId;
  final ScanOutcomeStatus status;

  /// Bei `applied` und `duplicate` immer gesetzt, bei `rejected` häufig
  /// `null` (z. B. wenn die Id beim Server gar nicht ankam).
  final String? deliveryItemId;

  /// Bei `rejected` die Server-Begründung — Standard-Text in der UI.
  final String? reason;
}

enum ScanOutcomeStatus {
  /// Server hat den Scan angewendet — `scannedQuantity` ist hochgezählt
  /// oder Status hat sich geändert.
  applied,

  /// Server hat denselben `clientScanId` schon einmal verarbeitet —
  /// kein Effekt, aber auch kein Fehler.
  duplicate,

  /// Server hat den Scan abgelehnt (z. B. Item gehört zu fremder
  /// Lieferung, Soll-Menge schon voll, Item ist auf `removed`). UI muss
  /// optimistische Mutation zurückrollen.
  rejected,
}
