import { useState, useEffect, useRef } from 'react';
import { json } from '@remix-run/node';
import { useRouteError } from '@remix-run/react';
import { boundary } from '@shopify/shopify-app-remix/server';
import { authenticate } from '../shopify.server.js';

// Anmeldung nur hier im Route-Loader (nicht in root/Layout): gültige Shopify-Sitzung ist Pflicht.
export async function loader({ request }: { request: Request }) {
  await authenticate.admin(request);
  return json({ ok: true });
}

import { shopifyQuery } from '../lib/shopify-client';
import { preOpenPrintWindow } from '../lib/print';
import {
  backend,
  BackendError,
  type ApiHistoryEntry,
  type ApiOrder,
  type ApiOrderItem,
  type ImportReport,
  type MetafieldSources,
  type OrderStatus,
  type ReceivingProgress,
  type ReceivingResult,
} from '../lib/backend';

// Polaris-Webkomponenten unter React 18: onChange wird nur für echte input/select/textarea gebunden.
// Diese Hilfe hängt das native "change"-Ereignis per ref an und ruft immer den neuesten Handler auf.
type ChangeHandler = (e: any) => void;
const listen = (type: string, handler: ChangeHandler) => (el: HTMLElement | null) => {
  if (!el) return;
  const holder = ((el as any).__handlers ??= {}) as Record<string, ChangeHandler>;
  if (!holder[type]) el.addEventListener(type, (e) => (el as any).__handlers[type](e));
  holder[type] = handler;
};


const toApiItem = (item: OrderItem): ApiOrderItem => {
  const out: ApiOrderItem = { sku: item.sku, quantity: item.quantity, title: item.title };
  if (item.lagerstandort) out.lagerstandort = item.lagerstandort;
  return out;
};

// Alle Backend-Aufrufe laufen über dieses Objekt (Namen wie deleteOrder/startReceiving gibt es auch in der Komponente).

let isProcessingReceiving = false;
let idempotencyCounter = 0;

const generateIdempotencyKey = (): string => {
  idempotencyCounter += 1;
  return `wareneingang-${Date.now()}-${idempotencyCounter}-${Math.random()
    .toString(36)
    .substring(2, 12)}`;
};

interface OrderItem {
  sku: string;
  quantity: number;
  title: string;
  lagerstandort?: string;
  // Diese beiden Felder werden nur im Arbeitsspeicher gehalten und bei Bedarf
  // per SKU aus Shopify nachgeladen. Sie werden derzeit NICHT im Metafield gespeichert.
  inventoryItemId?: string;
  imageUrl?: string;
}

interface PurchaseOrder {
  id: string;
  date: string;
  items: OrderItem[];
  status: 'open' | 'processing' | 'completed';
  trackingNumber?: string;
  verbuchtAm?: string;
}

interface ReceivingItem extends OrderItem {
  receivedQuantity: number;
  orderId: string;
}

interface PriceUpdateResult {
  sku: string;
  variantId: string;
  title: string;
  currentPrice: string;
  newPrice: string;
  status: 'unchanged' | 'changed' | 'error' | 'not_in_excel';
  errorMessage?: string;
}

interface HistoryEntry {
  datum: string;
  uhrzeit: string;
  ereignis: string;
  bestelldatum: string;
  sku: string;
  bezeichnung: string;
  mengeBestellt: number;
  mengeVerbucht: number;
  details: string;
}

type View =
  | 'menu'
  | 'order'
  | 'receiving'
  | 'receiving-details'
  | 'completed-orders'
  | 'view-orders'
  | 'search-orders'
  | 'history'
  | 'export'
  | 'inventory'
  | 'price-update'
  | 'migration';

export default function Bestellverwaltung() {
  const [view, setView] = useState<View>('menu');
  const [purchaseOrders, setPurchaseOrders] = useState<PurchaseOrder[]>([]);
  const [shopId, setShopId] = useState<string>('');
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string>('');
  const [success, setSuccess] = useState<string>('');

  const [orderInput, setOrderInput] = useState<string>('');

  const [importedItems, setImportedItems] = useState<OrderItem[]>([]);
  const [importWarning, setImportWarning] = useState<string>('');

  const [selectedOrderId, setSelectedOrderId] = useState<string>('');
  const [receivingReturnView, setReceivingReturnView] = useState<'receiving' | 'completed-orders'>(
    'receiving',
  );
  const [receivingItems, setReceivingItems] = useState<ReceivingItem[]>([]);
  const [alreadyBookedBySku, setAlreadyBookedBySku] = useState<{ [sku: string]: number }>({});
  const [applyPreviousBookings, setApplyPreviousBookings] = useState<boolean>(false);
  const [receivingPreparing, setReceivingPreparing] = useState<boolean>(false);
  const [showReceivingConfirm, setShowReceivingConfirm] = useState<boolean>(false);
  const [showIdempotencyWarning, setShowIdempotencyWarning] = useState<boolean>(false);
  // Betriebszustand (Diagnose des Servers): hängende Buchungen, Ergebnis der Überwachung, letztes Backup
  const [systemStatus, setSystemStatus] = useState<any>(null);
  // Standort für Bestandsbuchungen: gespeicherte Einstellung (settings/location_id) mit Rückfall auf den
  // ersten aktiven Standort, wie es die Vorgänger-App fest gemacht hat.
  const [availableLocations, setAvailableLocations] = useState<Array<{ id: string; name: string }>>([]);
  const [configuredLocationId, setConfiguredLocationId] = useState<string>('');
  const [locationSaving, setLocationSaving] = useState<boolean>(false);
  // Artikel einer unterbrochenen Buchung, bei denen unklar ist, ob Shopify den Bestand schon erhöht hat.
  const [unclearSkus, setUnclearSkus] = useState<Array<{ sku: string; receivedQuantity: number }>>([]);
  const [processing, setProcessing] = useState<boolean>(false);
  const [receivingProgress, setReceivingProgress] = useState<ReceivingProgress>({
    current: 0,
    total: 0,
    sku: '',
    phase: '',
  });
  // Ergebnis einer Buchung, dessen Historie noch nicht gespeichert werden konnte (erneut sendbar).
  const [pendingFinish, setPendingFinish] = useState<{
    orderId: string;
    bookingId: string;
    results: ReceivingResult[];
  } | null>(null);

  const [migrationBusy, setMigrationBusy] = useState<boolean>(false);
  const [migrationLog, setMigrationLog] = useState<string[]>([]);
  const [migrationSources, setMigrationSources] = useState<MetafieldSources | null>(null);
  const [migrationBackupUrl, setMigrationBackupUrl] = useState<string>('');
  const [migrationReport, setMigrationReport] = useState<ImportReport | null>(null);

  const [expandedOrders, setExpandedOrders] = useState<string[]>([]);
  const [editingOrders, setEditingOrders] = useState<{ [key: string]: PurchaseOrder }>({});
  const [newItemInputs, setNewItemInputs] = useState<{
    [key: string]: { sku: string; qty: string };
  }>({});

  const [exportData, setExportData] = useState<string>('');
  const [inventoryData, setInventoryData] = useState<any[]>([]);
  const [selectedLocation, setSelectedLocation] = useState<string>('');
  const [showNoLocation, setShowNoLocation] = useState<boolean>(false);
  const [hideZeroStock, setHideZeroStock] = useState<boolean>(false);
  const [locations, setLocations] = useState<string[]>([]);

  const [searchQuery, setSearchQuery] = useState<string>('');
  const [searchResults, setSearchResults] = useState<any[]>([]);
  const [searchOrderCount, setSearchOrderCount] = useState<number>(0);
  const [searchExecuted, setSearchExecuted] = useState<boolean>(false);
  const [confirmDeleteOrderId, setConfirmDeleteOrderId] = useState<string | null>(null);

  const getTodayDisplay = (): string => {
    const now = new Date();
    return `${String(now.getDate()).padStart(2, '0')}.${String(now.getMonth() + 1).padStart(2, '0')}.${now.getFullYear()}`;
  };

  const getTodayISO = (): string => {
    const now = new Date();
    return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
  };

  const [orderDateDisplay, setOrderDateDisplay] = useState<string>(getTodayDisplay());
  const [orderDate, setOrderDate] = useState<string>(getTodayISO());


  const [printLink, setPrintLink] = useState<string>('');

  const [priceUpdateStep, setPriceUpdateStep] = useState<1 | 2>(1);
  const [priceUpdateFile, setPriceUpdateFile] = useState<File | null>(null);
  const [priceUpdateResults, setPriceUpdateResults] = useState<PriceUpdateResult[]>([]);
  const [priceUpdateSelected, setPriceUpdateSelected] = useState<string[]>([]);
  const [priceUpdateLoading, setPriceUpdateLoading] = useState<boolean>(false);
  const [priceUpdateSuccess, setPriceUpdateSuccess] = useState<string>('');

  const [historyData, setHistoryData] = useState<HistoryEntry[]>([]);
  const [historyYear, setHistoryYear] = useState<string>(new Date().getFullYear().toString());
  const [historyYears, setHistoryYears] = useState<string[]>([]);
  const [historyLoading, setHistoryLoading] = useState<boolean>(false);

  const [inventoryEditMode, setInventoryEditMode] = useState<boolean>(false);
  const [inventoryEdits, setInventoryEdits] = useState<{
    [sku: string]: { newStock?: number; newLagerstandort?: string };
  }>({});
  const [inventorySaving, setInventorySaving] = useState<boolean>(false);
  const [inventoryFreeTextRows, setInventoryFreeTextRows] = useState<{
    [sku: string]: boolean;
  }>({});
  const [inventoryLoaded, setInventoryLoaded] = useState<boolean>(false);

  useEffect(() => {
    initializeApp();
  }, []);

  const initializeApp = async (): Promise<void> => {
    setLoading(true);
    setError('');

    try {
      const shopQuery = `query { shop { id } }`;
      const { data: shopData, errors: shopErrors } = await shopifyQuery(shopQuery);

      if (shopErrors?.length > 0) {
        setError(shopErrors.map((e: any) => e.message).join(', '));
        setLoading(false);
        return;
      }

      const sid = shopData.shop.id;
      setShopId(sid);

      // Betriebszustand im Hintergrund laden; ein Fehler dabei blockiert nichts.
      backend
        .getDiag()
        .then(setSystemStatus)
        .catch(() => setSystemStatus(null));
      void loadAvailableLocations();

      try {
        const orders = await backend.listOrders('open,processing');
        setPurchaseOrders(orders as PurchaseOrder[]);
      } catch (backendError: any) {
        setError(
          `Bestelldaten konnten nicht geladen werden: ${backendError?.message || 'Backend nicht erreichbar'}. Bitte Seite neu laden.`,
        );
      }
    } catch (err: any) {
      setError(err.message || 'Fehler beim Initialisieren der App');
    }

    setLoading(false);
  };

  // Zentrale Sperre gegen Mehrfachklicks: Solange eine Aktion läuft, wird jede weitere ignoriert
  // (das Ref greift sofort, noch vor dem nächsten Render) und die Schaltflächen zeigen den Ladezustand.
  const [busyAction, setBusyAction] = useState<string>('');
  const activeActionRef = useRef<string>('');
  const runExclusive = async (name: string, action: () => Promise<void>): Promise<void> => {
    if (activeActionRef.current) return;
    activeActionRef.current = name;
    setBusyAction(name);
    try {
      await action();
    } finally {
      activeActionRef.current = '';
      setBusyAction('');
    }
  };
  const busyProps = (name: string) => ({
    disabled: busyAction ? true : undefined,
    loading: busyAction === name ? true : undefined,
  });

  /**
   * Standort für Bestandsbuchungen: zuerst die gespeicherte Einstellung (settings/location_id), sonst wie
   * bisher der erste aktive Standort. Gibt { id, error } zurück statt zu werfen, damit die Aufrufer ihre
   * bestehende Fehlerbehandlung unverändert weiterverwenden können.
   */
  const resolveActiveLocation = async (): Promise<{ id: string; error: string }> => {
    try {
      const setting = await backend.getSetting('location_id');
      if (setting?.value) return { id: setting.value, error: '' };
    } catch {
      // Ohne gespeicherte Einstellung (oder bei einem Backend-Fehler) auf den ersten aktiven Standort zurückfallen.
    }
    const { data, errors } = await shopifyQuery(
      `query { locations(first: 1, query: "active:true") { edges { node { id name } } } }`,
    );
    if (errors && errors.length > 0) {
      return { id: '', error: `Standort-Abfrage fehlgeschlagen: ${errors.map((e: any) => e.message).join(', ')}` };
    }
    const id = data?.locations?.edges?.[0]?.node?.id || '';
    return id ? { id, error: '' } : { id: '', error: 'Kein aktiver Lagerstandort gefunden.' };
  };

  const loadAvailableLocations = async (): Promise<void> => {
    try {
      const [{ data }, setting] = await Promise.all([
        shopifyQuery(`query { locations(first: 50, query: "active:true") { edges { node { id name } } } }`),
        backend.getSetting('location_id').catch(() => null),
      ]);
      setAvailableLocations((data?.locations?.edges || []).map((e: any) => e.node));
      setConfiguredLocationId(setting?.value || '');
    } catch {
      // Die Einstellung bleibt dann leer; die Buchungen fallen weiterhin auf den ersten aktiven Standort zurück.
    }
  };

  const saveConfiguredLocation = (id: string): void => {
    void runExclusive('saveLocation', async () => {
      setLocationSaving(true);
      try {
        await backend.setSetting('location_id', id);
        setConfiguredLocationId(id);
        setSuccess('Standort für Bestandsbuchungen gespeichert.');
      } catch (e: any) {
        setError(`Standort konnte nicht gespeichert werden: ${e?.message || 'Backend-Fehler'}`);
      } finally {
        setLocationSaving(false);
      }
    });
  };

  const parseOrderInput = async (): Promise<void> => {
    setImportWarning('');
    setError('');

    const lines = orderInput
      .trim()
      .split('\n')
      .filter((l) => l.trim());
    const items: OrderItem[] = [];
    const notFoundSkus: string[] = [];
    const invalidLines: string[] = [];

    for (const line of lines) {
      const lineLower = line.trim().toLowerCase();
      if (lineLower.includes('artikel') || lineLower.includes('menge')) {
        continue;
      }

      const parts = line.trim().split(/\s+/);
      if (parts.length < 3) {
        invalidLines.push(line);
        continue;
      }

      const sku = parts[0];
      const units = parseInt(parts[1]);
      const piecesPerUnit = parseInt(parts[2]);

      if (isNaN(units) || isNaN(piecesPerUnit)) {
        invalidLines.push(line);
        continue;
      }

      const quantity = units * piecesPerUnit;

      const query = `query ProductVariantBySku($sku: String!) {
        productVariants(first: 1, query: $sku) {
          edges {
            node {
              id
              sku
              title
              displayName
              inventoryItem {
                id
              }
              media(first: 1) {
                edges {
                  node {
                    ... on MediaImage {
                      image {
                        url
                      }
                    }
                  }
                }
              }
              product {
                id
                title
                featuredMedia {
                  preview {
                    image {
                      url
                    }
                  }
                }
                lagerstandort: metafield(namespace: "custom", key: "lagerstandort") {
                  value
                }
              }
            }
          }
        }
      }`;

      const { data, errors } = await shopifyQuery(query, {
        variables: { sku: `sku:${sku}` },
      });

      if (errors?.length > 0) {
        setError(errors.map((e: any) => e.message).join(', '));
        return;
      }

      if (data?.productVariants?.edges?.length > 0) {
        const variant = data.productVariants.edges[0].node;
        const imageUrl =
          variant.media?.edges?.[0]?.node?.image?.url ||
          variant.product?.featuredMedia?.preview?.image?.url ||
          '';
        items.push({
          sku: variant.sku,
          quantity,
          inventoryItemId: variant.inventoryItem.id,
          title: variant.displayName || variant.title,
          imageUrl,
          lagerstandort: variant.product?.lagerstandort?.value || '',
        });
      } else {
        notFoundSkus.push(sku);
      }
    }

    if (invalidLines.length > 0) {
      setImportWarning('Ungültige Zeilen übersprungen');
    }

    if (notFoundSkus.length > 0) {
      setImportWarning(
        (prev) =>
          (prev ? prev + '; ' : '') +
          `Folgende SKUs wurden nicht gefunden: ${notFoundSkus.join(', ')}`,
      );
    }

    setImportedItems(items);
  };

  const saveOrder = async (): Promise<void> => {
    setError('');
    setSuccess('');

    if (!orderDate) {
      setError('Bitte Bestelldatum eingeben (TT.MM.JJJJ)');
      return;
    }

    if (importedItems.length === 0) {
      setError('Keine gültigen Artikel gefunden');
      return;
    }

    const generateId = (): string => {
      return `order-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
    };

    const newOrder: PurchaseOrder = {
      id: generateId(),
      date: orderDate,
      items: importedItems,
      status: 'open',
    };

    let created: ApiOrder;
    try {
      // Das Backend speichert die Bestellung und schreibt die Historie („Bestellung erfasst") in einer Transaktion.
      created = await backend.createOrder({
        id: newOrder.id,
        date: newOrder.date,
        items: newOrder.items.map(toApiItem),
      });
    } catch (saveError: any) {
      setError(
        `Bestellung konnte nicht gespeichert werden: ${saveError?.message || 'Unbekannter Fehler'}`,
      );
      return;
    }

    // Bild-URL und inventoryItemId bleiben nur im Arbeitsspeicher.
    setPurchaseOrders([...purchaseOrders, { ...(created as PurchaseOrder), items: newOrder.items }]);

    setSuccess('Bestellung erfolgreich gespeichert');
    setOrderInput('');
    setOrderDate(getTodayISO());
    setOrderDateDisplay(getTodayDisplay());
    setImportedItems([]);
    setImportWarning('');
    setTimeout(() => {
      setView('menu');
      setSuccess('');
    }, 1500);
  };

  // Lädt inventoryItemId, Bild-URL und Lagerstandort per SKU nach. Diese Felder werden
  // nicht im Metafield gespeichert und müssen deshalb bei Bedarf ergänzt werden.
  const enrichOrderItems = async (items: OrderItem[]): Promise<OrderItem[]> => {
    const uniqueSkus = Array.from(new Set(items.map((i) => i.sku).filter((s) => !!s)));
    if (uniqueSkus.length === 0) return items;

    const variantBySku: {
      [sku: string]: { inventoryItemId: string; imageUrl: string; lagerstandort: string };
    } = {};

    const enrichWarnings: string[] = [];

    const enrichBatchSize = 15;
    for (let i = 0; i < uniqueSkus.length; i += enrichBatchSize) {
      const batch = uniqueSkus.slice(i, i + enrichBatchSize);
      const batchQuery = batch.map((s) => `sku:"${s}"`).join(' OR ');

      const query = `query VariantsBySkuBatch($query: String!) {
  productVariants(first: 25, query: $query) {
    edges {
      node {
        sku
        inventoryItem {
          id
        }
        product {
          featuredMedia {
            preview {
              image {
                url
              }
            }
          }
          lagerstandort: metafield(
            namespace: "custom",
            key: "lagerstandort"
          ) {
            value
          }
        }
      }
    }
  }
}`;

      // Jede Teilabfrage wird einzeln abgesichert. Schlägt sie fehl (z. B. Zeitüberschreitung
      // oder API-Limit), wird sie einmal wiederholt und danach übersprungen, damit der
      // Ladevorgang niemals stehen bleibt.
      let batchLoaded: boolean = false;
      let batchErrorMessage = '';

      for (let attempt = 1; attempt <= 2 && !batchLoaded; attempt++) {
        try {
          const { data, errors } = await shopifyQuery(query, {
            variables: { query: batchQuery },
          });

          if (errors?.length > 0) {
            batchErrorMessage = errors.map((e: any) => e.message).join(', ');
            continue;
          }

          for (const edge of data?.productVariants?.edges ?? []) {
            const node = edge.node;
            if (!node?.sku) continue;
            variantBySku[node.sku] = {
              inventoryItemId: node.inventoryItem?.id || '',
              imageUrl:
                node.media?.edges?.[0]?.node?.image?.url ||
                node.product?.featuredMedia?.preview?.image?.url ||
                '',
              lagerstandort: node.product?.lagerstandort?.value || '',
            };
          }

          batchLoaded = true;
        } catch (batchError: any) {
          batchErrorMessage = batchError?.message || 'Unbekannter Fehler';
        }
      }

      if (!batchLoaded && batchErrorMessage) {
        enrichWarnings.push(batchErrorMessage);
      }
    }

    if (enrichWarnings.length > 0) {
      setError(
        `Einige Artikeldaten (Bilder/Standorte) konnten nicht geladen werden: ${Array.from(
          new Set(enrichWarnings),
        ).join(', ')}. Das Verbuchen ist trotzdem möglich.`,
      );
    }

    return items.map((item) => {
      const info = variantBySku[item.sku];
      if (!info) return item;
      return {
        ...item,
        inventoryItemId: info.inventoryItemId || item.inventoryItemId,
        imageUrl: info.imageUrl || item.imageUrl,
        lagerstandort: item.lagerstandort || info.lagerstandort,
      };
    });
  };

  const resolveInventoryItemIdBySku = async (sku: string): Promise<string> => {
    const query = `query ProductVariantBySku($sku: String!) {
      productVariants(first: 1, query: $sku) {
        edges {
          node {
            inventoryItem { id }
          }
        }
      }
    }`;
    const { data } = await shopifyQuery(query, { variables: { sku: `sku:${sku}` } });
    return data?.productVariants?.edges?.[0]?.node?.inventoryItem?.id || '';
  };

  // Liefert null, wenn die bereits verbuchten Mengen nicht geladen werden konnten. Dann wird die
  // Maske nicht geöffnet, damit nicht versehentlich die volle Menge erneut vorbelegt wird.
  const loadBookedQuantitiesForOrder = async (
    orderId: string,
  ): Promise<{ [sku: string]: number } | null> => {
    try {
      return await backend.getBookedQuantities(orderId);
    } catch (bookedError: any) {
      setError(
        `Bereits verbuchte Mengen konnten nicht geladen werden: ${bookedError?.message || 'Unbekannter Fehler'}. Die Maske wurde nicht geöffnet.`,
      );
      return null;
    }
  };

  const logReceivingAbort = async (orderDateIso: string, errorText: string): Promise<void> => {
    try {
      await backend.addHistoryEvents([
        {
          ereignis: 'Wareneingang abgebrochen',
          bestelldatum: orderDateIso ? formatDateDisplay(orderDateIso) : '',
          details: errorText,
        },
      ]);
    } catch {
      // Ein Fehler beim Protokollieren darf den Abbruch nicht zusätzlich stören.
    }
  };

  const startReceiving = async (
    orderId: string,
    returnView: 'receiving' | 'completed-orders' = 'receiving',
  ): Promise<void> => {
    const order = purchaseOrders.find((o) => o.id === orderId);
    if (!order) return;

    setError('');
    setSuccess('');
    setReceivingPreparing(true);
    setReceivingReturnView(returnView);
    setSelectedOrderId(orderId);

    const useHistoryPrefill: boolean = returnView === 'completed-orders';
    let bookedBySku: { [sku: string]: number } = {};

    // Bei Problemfällen zuerst bereits gebuchte Mengen laden.
    if (useHistoryPrefill) {
      const loadedBooked = await loadBookedQuantitiesForOrder(order.id);
      if (!loadedBooked) {
        setReceivingPreparing(false);
        return;
      }
      bookedBySku = loadedBooked;
    }

    // Unterbrochene Buchung? Dann ist bei einzelnen Artikeln unklar, ob Shopify den Bestand schon erhöht hat.
    // Diese Artikel werden mit 0 vorbelegt (sicherer Standard) und im Hinweis genannt.
    let unclear: Array<{ sku: string; receivedQuantity: number }> = [];
    if (order.status !== 'open') {
      try {
        unclear = await backend.getUnclearReceiving(order.id);
      } catch {
        // Ohne diese Angabe bleibt die bisherige Vorbelegung; die Historie schützt weiterhin vor Doppelbuchung.
      }
    }
    setUnclearSkus(unclear);
    const unclearSet = new Set(unclear.map((u) => u.sku));

    // Die Maske sofort mit den gespeicherten Bestelldaten öffnen.
    const initialReceivingItems: ReceivingItem[] = order.items.map((item) => ({
      ...item,
      receivedQuantity: unclearSet.has(item.sku)
        ? 0
        : useHistoryPrefill
          ? Math.max(0, item.quantity - (bookedBySku[item.sku] || 0))
          : item.quantity,
      orderId,
    }));

    setAlreadyBookedBySku(bookedBySku);
    setApplyPreviousBookings(useHistoryPrefill);
    setReceivingItems(initialReceivingItems);
    setReceivingPreparing(false);
    setShowReceivingConfirm(false);
    setShowIdempotencyWarning(!!order.verbuchtAm || order.status !== 'open');

    // Nicht auf Zusatzdaten warten.
    setView('receiving-details');

    // Bilder, Lagerstandorte und Inventar-IDs danach im Hintergrund nachladen.
    try {
      const enrichedItems = await enrichOrderItems(order.items);

      setPurchaseOrders((prev) =>
        prev.map((existingOrder) =>
          existingOrder.id === orderId ? { ...existingOrder, items: enrichedItems } : existingOrder,
        ),
      );

      // Bereits eingegebene Mengen niemals überschreiben.
      setReceivingItems((prev) =>
        prev.map((receivingItem) => {
          const enrichedItem = enrichedItems.find((item) => item.sku === receivingItem.sku);

          if (!enrichedItem) return receivingItem;

          return {
            ...receivingItem,
            ...enrichedItem,
            receivedQuantity: receivingItem.receivedQuantity,
            orderId,
          };
        }),
      );
    } catch (prepareError: any) {
      setError(
        `Zusätzliche Artikeldaten konnten nicht vollständig geladen werden: ${
          prepareError?.message || 'Unbekannter Fehler'
        }. Das Verbuchen ist trotzdem möglich.`,
      );
    }
  };

  const logDoubleBookingAttempt = async (attemptOrder: PurchaseOrder): Promise<void> => {
    if (!attemptOrder.verbuchtAm) return;

    try {
      await backend.addHistoryEvents([
        {
          ereignis: 'Doppelbuchungsversuch',
          bestelldatum: formatDateDisplay(attemptOrder.date),
          details: `Bereits verbucht am ${formatDateTimeDisplay(attemptOrder.verbuchtAm)}`,
          orderId: attemptOrder.id,
        },
      ]);
    } catch {
      // Ein Fehler beim Protokollieren darf die Entscheidung nicht blockieren.
    }
  };

  const checkAndProcessReceiving = (): void => {
    const hasDifferences = receivingItems.some((item) => item.receivedQuantity !== item.quantity);
    if (hasDifferences) {
      setShowReceivingConfirm(true);
    } else {
      handleProcessReceiving();
    }
  };

  const runProcessReceiving = async (): Promise<void> => {
    if (isProcessingReceiving) return;
    isProcessingReceiving = true;
    setProcessing(true);
    setError('');
    setSuccess('');

    const receivingOrder = purchaseOrders.find((o) => o.id === selectedOrderId);
    const receivingOrderDate = receivingOrder ? receivingOrder.date : '';

    // SPERRE: Das Backend setzt den Status "in Verarbeitung" und schreibt den Startvermerk in einer
    // Transaktion, BEVOR Bestandsanpassungen erfolgen. Das verhindert Doppelbuchungen, falls die Seite
    // mitten im Vorgang neu lädt. Die bookingId macht Wiederholungen bei Verbindungsproblemen harmlos.
    const bookingId = backend.newBookingId();
    const totalAcceptedQuantity = receivingItems.reduce(
      (sum, item) => sum + item.receivedQuantity,
      0,
    );
    const itemsToBook = receivingItems.filter((item) => item.receivedQuantity > 0);

    setReceivingProgress({
      current: 0,
      total: itemsToBook.length,
      sku: '',
      phase: 'Buchung wird vorbereitet (Sperre wird gesetzt)...',
    });

    try {
      const started = await backend.startReceiving(selectedOrderId, {
        bookingId,
        positions: receivingItems.length,
        acceptedQuantity: totalAcceptedQuantity,
      });
      setPurchaseOrders((prev) =>
        prev.map((o) =>
          o.id === selectedOrderId
            ? { ...o, status: started.order.status, verbuchtAm: started.order.verbuchtAm }
            : o,
        ),
      );
    } catch (startError: any) {
      const lockErrorText = `Bestellstatus konnte vor der Buchung nicht gespeichert werden (${startError?.message || 'Backend-Fehler'}). Es wurden keine Bestände verändert.`;
      await logReceivingAbort(receivingOrderDate, lockErrorText);
      setError(lockErrorText);
      setProcessing(false);
      isProcessingReceiving = false;
      return;
    }

    setReceivingProgress((prev) => ({ ...prev, phase: 'Lagerstandort wird ermittelt...' }));

    const { id: locationId, error: locationError } = await resolveActiveLocation();
    if (!locationId) {
      const noLocationText = locationError || 'Kein aktiver Lagerstandort gefunden. Buchung abgebrochen.';
      await logReceivingAbort(receivingOrderDate, noLocationText);
      setError(noLocationText);
      setProcessing(false);
      isProcessingReceiving = false;
      return;
    }
    setReceivingProgress((prev) => ({ ...prev, phase: 'Bestände werden aktualisiert...' }));
    const skippedSkus: string[] = [];
    let lastErrorMsg = '';
    const skuErrorMap: { [sku: string]: string } = {};

    for (let itemIndex = 0; itemIndex < itemsToBook.length; itemIndex++) {
      const item = itemsToBook[itemIndex];

      // "current" zählt die bereits abgeschlossenen Artikel, "sku" ist der Artikel in Arbeit.
      setReceivingProgress((prev) => ({
        ...prev,
        current: itemIndex,
        sku: item.sku,
        phase: 'Bestand wird aktualisiert...',
      }));

      let currentInventoryItemId = item.inventoryItemId || '';

      if (!currentInventoryItemId) {
        currentInventoryItemId = await resolveInventoryItemIdBySku(item.sku);
      }

      if (!currentInventoryItemId) {
        skippedSkus.push(item.sku);
        skuErrorMap[item.sku] = 'inventoryItemId konnte nicht ermittelt werden';
        lastErrorMsg = `SKU ${item.sku}: inventoryItemId konnte nicht ermittelt werden`;
        continue;
      }

      const availQuery = `query GetAvailable($id: ID!) {
        node(id: $id) {
          ... on InventoryItem {
            inventoryLevels(first: 10) {
              edges {
                node {
                  location { id }
                  quantities(names: ["available"]) {
                    name
                    quantity
                  }
                }
              }
            }
          }
        }
      }`;

      const adjustMutation = `mutation inventoryAdjustQuantities($input: InventoryAdjustQuantitiesInput!, $idempotencyKey: String!) {
        inventoryAdjustQuantities(input: $input) @idempotent(key: $idempotencyKey) {
          inventoryAdjustmentGroup {
            createdAt
            reason
          }
          userErrors {
            field
            message
          }
        }
      }`;

      // Ein stabiler Idempotenz-Schlüssel pro Artikel. Wiederholungsversuche (Retry)
      // desselben Artikels nutzen denselben Schlüssel, damit keine Doppelbuchung entsteht.
      const itemIdempotencyKey = generateIdempotencyKey();

      const fetchAvailable = async (invItemId: string): Promise<number> => {
        const { data: availData } = await shopifyQuery(availQuery, {
          variables: { id: invItemId },
        });
        const levels = availData?.node?.inventoryLevels?.edges ?? [];
        const match = levels.find((e: any) => e.node.location.id === locationId);
        return match?.node?.quantities?.find((q: any) => q.name === 'available')?.quantity ?? 0;
      };

      // Absicht auf dem Server festhalten, BEVOR Shopify den Bestand ändert. Ohne diese Zusage keine Buchung.
      try {
        await backend.recordReceivingProgress(selectedOrderId, {
          bookingId,
          sku: item.sku,
          title: item.title,
          quantity: item.quantity,
          receivedQuantity: item.receivedQuantity,
          status: 'pending',
        });
      } catch (intentError: any) {
        skippedSkus.push(item.sku);
        skuErrorMap[item.sku] =
          `Fortschritt konnte nicht gespeichert werden (${intentError?.message || 'Backend-Fehler'}); Bestand wurde NICHT verändert`;
        lastErrorMsg = skuErrorMap[item.sku];
        continue;
      }

      let itemSkipped = true;
      let firstErrorMsg = '';
      let retryErrorMsg = '';
      const MAX_ATTEMPTS = 3;

      for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
        const changeFromQuantity = await fetchAvailable(currentInventoryItemId);

        const { data, errors } = await shopifyQuery(adjustMutation, {
          variables: {
            idempotencyKey: itemIdempotencyKey,
            input: {
              reason: 'received',
              name: 'available',
              changes: [
                {
                  delta: item.receivedQuantity,
                  inventoryItemId: currentInventoryItemId,
                  locationId,
                  changeFromQuantity,
                },
              ],
            },
          },
        });

        const errorMsg =
          errors?.map((e: any) => e.message).join('; ') ||
          data?.inventoryAdjustQuantities?.userErrors
            ?.map((e: any) => `[${e.field}] ${e.message}`)
            .join('; ') ||
          '';

        if (attempt === 1) firstErrorMsg = errorMsg;
        else retryErrorMsg = errorMsg;

        if (!errorMsg) {
          itemSkipped = false;
          break;
        }

        lastErrorMsg = errorMsg;
      }

      // Falls nach MAX_ATTEMPTS weiterhin fehlerhaft: einmal mehr mit frischer
      // inventoryItemId aus der SKU-Suche versuchen.
      if (itemSkipped) {
        const skuQuery = `query ProductVariantBySku($sku: String!) {
          productVariants(first: 1, query: $sku) {
            edges {
              node {
                inventoryItem { id }
              }
            }
          }
        }`;
        const { data: skuData } = await shopifyQuery(skuQuery, {
          variables: { sku: `sku:${item.sku}` },
        });
        if (skuData?.productVariants?.edges?.length > 0) {
          currentInventoryItemId = skuData.productVariants.edges[0].node.inventoryItem.id;
          const changeFromQuantityFinal = await fetchAvailable(currentInventoryItemId);
          const { data: finalData, errors: finalErrors } = await shopifyQuery(adjustMutation, {
            variables: {
              idempotencyKey: generateIdempotencyKey(),
              input: {
                reason: 'received',
                name: 'available',
                changes: [
                  {
                    delta: item.receivedQuantity,
                    inventoryItemId: currentInventoryItemId,
                    locationId,
                    changeFromQuantity: changeFromQuantityFinal,
                  },
                ],
              },
            },
          });
          const finalErrorMsg =
            finalErrors?.map((e: any) => e.message).join('; ') ||
            finalData?.inventoryAdjustQuantities?.userErrors
              ?.map((e: any) => `[${e.field}] ${e.message}`)
              .join('; ') ||
            '';
          retryErrorMsg = finalErrorMsg;
          if (!finalErrorMsg) {
            itemSkipped = false;
          } else {
            lastErrorMsg = finalErrorMsg;
          }
        }
      }

      if (itemSkipped) {
        skippedSkus.push(item.sku);
      }
      skuErrorMap[item.sku] =
        `1.Versuch: ${firstErrorMsg || '?'} | Retry: ${retryErrorMsg || 'n/a'}`;

      // Ergebnis sofort festhalten (nicht erst am Ende): schützt bei Browser-Absturz vor Doppelbuchung.
      // Schlägt das fehl, sendet der Abschluss unten dasselbe Ergebnis noch einmal.
      try {
        await backend.recordReceivingProgress(selectedOrderId, {
          bookingId,
          sku: item.sku,
          title: item.title,
          quantity: item.quantity,
          receivedQuantity: item.receivedQuantity,
          status: itemSkipped ? 'skipped' : 'booked',
          error: itemSkipped ? skuErrorMap[item.sku] : undefined,
        });
      } catch {
        // wird beim Abschluss nachgereicht
      }
    }

    setReceivingProgress({
      current: itemsToBook.length,
      total: itemsToBook.length,
      sku: '',
      phase: 'Ergebnis wird in der Historie gespeichert...',
    });

    // Ergebnis je Artikel. Das Backend schreibt die Historie und setzt den Status in einer Transaktion:
    // "completed", wenn alles gebucht wurde, sonst bleibt die Bestellung "in Verarbeitung".
    const results: ReceivingResult[] = itemsToBook.map((item) => {
      const skipped = skippedSkus.includes(item.sku);
      return {
        sku: item.sku,
        title: item.title,
        quantity: item.quantity,
        receivedQuantity: item.receivedQuantity,
        outcome: skipped ? ('skipped' as const) : ('booked' as const),
        error: skipped ? skuErrorMap[item.sku] || 'Fehler bei Bestandsanpassung' : undefined,
      };
    });

    const bookingSucceeded: boolean = skippedSkus.length === 0;
    let finishError = '';

    try {
      const finished = await backend.finishReceiving(selectedOrderId, { bookingId, results });
      setPurchaseOrders((prev) =>
        prev.map((o) =>
          o.id === selectedOrderId
            ? { ...o, status: finished.order.status, verbuchtAm: finished.order.verbuchtAm }
            : o,
        ),
      );
      setPendingFinish(null);
      setUnclearSkus([]);
    } catch (finishErr: any) {
      finishError = finishErr?.message || 'Backend nicht erreichbar';
      // Die Bestände sind bei Shopify bereits gebucht. Das Ergebnis bleibt erhalten und kann erneut gesendet werden.
      setPendingFinish({ orderId: selectedOrderId, bookingId, results });
    }

    if (!bookingSucceeded) {
      setError(
        `Folgende SKUs konnten nicht verbucht werden: ${skippedSkus.join(', ')} — Fehler: ${lastErrorMsg}. Die Bestellung bleibt als "in Verarbeitung" erhalten und kann über "Bestellungen mit Verbuchungsproblemen" geprüft oder erneut gebucht werden.${
          finishError ? ` Zusätzlicher Historie-Fehler: ${finishError}` : ''
        }`,
      );
    } else if (finishError) {
      setError(
        `Wareneingang bei Shopify verbucht, aber die Historie konnte nicht gespeichert werden: ${finishError}. Bitte "Historie erneut speichern" verwenden, nicht erneut buchen.`,
      );
    } else {
      setReceivingProgress((prev) => ({ ...prev, phase: 'Fertig.' }));
      setSuccess('Wareneingang erfolgreich verbucht');
      setTimeout(() => {
        setView('menu');
        setSuccess('');
      }, 2000);
    }

    setProcessing(false);
    isProcessingReceiving = false;
  };

  const handleProcessReceiving = async (): Promise<void> => {
    try {
      await runProcessReceiving();
    } catch (err: any) {
      const abortMessage = err?.message || 'Unbekannter Fehler beim Verbuchen';
      const abortOrder = purchaseOrders.find((o) => o.id === selectedOrderId);
      await logReceivingAbort(abortOrder ? abortOrder.date : '', abortMessage);
      setError(
        `Wareneingang abgebrochen: ${abortMessage}. Die Bestellung bleibt als "in Verarbeitung" erhalten und kann über "Bestellungen mit Verbuchungsproblemen" geprüft oder erneut gebucht werden.`,
      );
      setProcessing(false);
      isProcessingReceiving = false;
    }
  };

  const toggleOrderExpansion = async (orderId: string): Promise<void> => {
    if (expandedOrders.includes(orderId)) {
      setExpandedOrders(expandedOrders.filter((id) => id !== orderId));
      return;
    }

    setExpandedOrders([...expandedOrders, orderId]);

    const order = purchaseOrders.find((o) => o.id === orderId);
    if (!order) return;

    if (!editingOrders[orderId]) {
      setEditingOrders((prev) => ({
        ...prev,
        [orderId]: JSON.parse(JSON.stringify(order)),
      }));
    }

    // Bilder werden nicht gespeichert und beim Aufklappen per SKU nachgeladen.
    const needsImages: boolean = order.items.some((item) => !item.imageUrl);
    if (!needsImages) return;

    const enrichedItems = await enrichOrderItems(order.items);

    setPurchaseOrders((prev) =>
      prev.map((o) => (o.id === orderId ? { ...o, items: enrichedItems } : o)),
    );

    setEditingOrders((prev) => {
      const currentEditingOrder = prev[orderId];
      if (!currentEditingOrder) return prev;
      const mergedItems = currentEditingOrder.items.map((editedItem) => {
        const match = enrichedItems.find((enriched) => enriched.sku === editedItem.sku);
        if (!match) return editedItem;
        return {
          ...editedItem,
          imageUrl: editedItem.imageUrl || match.imageUrl,
          inventoryItemId: editedItem.inventoryItemId || match.inventoryItemId,
        };
      });
      return { ...prev, [orderId]: { ...currentEditingOrder, items: mergedItems } };
    });
  };

  const loadOpenCustomerOrdersBySku = async (
    purchaseOrderDate: string,
  ): Promise<{ [sku: string]: { name: string; quantity: number; isNew: boolean }[] }> => {
    const skuMap: { [sku: string]: { name: string; quantity: number; isNew: boolean }[] } = {};
    const cutoffTime = new Date(`${purchaseOrderDate}T23:59:59Z`).getTime();

    const ordersQuery = `query OpenCustomerOrders($first: Int!, $after: String, $query: String!) {
      orders(first: $first, after: $after, sortKey: CREATED_AT, query: $query) {
        edges {
          node {
            id
            name
            createdAt
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }`;

    const lineItemsQuery = `query OrderLineItems($id: ID!, $after: String) {
      node(id: $id) {
        ... on Order {
          lineItems(first: 250, after: $after) {
            edges {
              node {
                sku
                quantity
                unfulfilledQuantity
              }
            }
            pageInfo {
              hasNextPage
              endCursor
            }
          }
        }
      }
    }`;

    const addLineItemToMap = (orderName: string, isNew: boolean, lineItemNode: any): void => {
      const lineSku = lineItemNode.sku || '';
      if (!lineSku) return;
      const openQty =
        lineItemNode.unfulfilledQuantity && lineItemNode.unfulfilledQuantity > 0
          ? lineItemNode.unfulfilledQuantity
          : lineItemNode.quantity || 0;
      if (openQty <= 0) return;
      if (!skuMap[lineSku]) skuMap[lineSku] = [];
      const existingEntry = skuMap[lineSku].find((entry) => entry.name === orderName);
      if (existingEntry) {
        existingEntry.quantity += openQty;
      } else {
        skuMap[lineSku].push({ name: orderName, quantity: openQty, isNew });
      }
    };

    const collectLineItems = async (
      orderId: string,
      orderName: string,
      isNew: boolean,
    ): Promise<boolean> => {
      let lineItemCursor: string | null = null;
      let hasMoreLineItems: boolean = true;
      let lineItemPageCount = 0;
      const MAX_LINE_ITEM_PAGES = 20;

      while (hasMoreLineItems && lineItemPageCount < MAX_LINE_ITEM_PAGES) {
        const { data, errors } = await shopifyQuery(lineItemsQuery, {
          variables: { id: orderId, after: lineItemCursor },
        });

        if (errors?.length > 0) {
          setError(errors.map((e: any) => e.message).join(', '));
          return false;
        }

        const lineItemEdges = data?.node?.lineItems?.edges ?? [];
        for (const lineItemEdge of lineItemEdges) {
          addLineItemToMap(orderName, isNew, lineItemEdge.node);
        }

        hasMoreLineItems = data?.node?.lineItems?.pageInfo?.hasNextPage || false;
        lineItemCursor = data?.node?.lineItems?.pageInfo?.endCursor || null;
        lineItemPageCount += 1;
      }

      return true;
    };

    const filter =
      '(fulfillment_status:unfulfilled OR fulfillment_status:partial) AND status:open AND -cancelled_at:*';

    let ordersCursor: string | null = null;
    let hasMoreOrders: boolean = true;
    let pageCount = 0;
    const MAX_PAGES = 10;

    while (hasMoreOrders && pageCount < MAX_PAGES) {
      const { data, errors } = await shopifyQuery(ordersQuery, {
        variables: { first: 50, after: ordersCursor, query: filter },
      });

      if (errors?.length > 0) {
        setError(errors.map((e: any) => e.message).join(', '));
        return {};
      }

      const edges = data?.orders?.edges ?? [];
      for (const edge of edges) {
        const orderName = String(edge.node.name || '').replace(/^#/, '');
        const createdTime = edge.node.createdAt ? new Date(edge.node.createdAt).getTime() : 0;
        const isNewOrder: boolean = createdTime > cutoffTime;
        const lineItemsLoaded = await collectLineItems(edge.node.id, orderName, isNewOrder);
        if (!lineItemsLoaded) return {};
      }

      hasMoreOrders = data?.orders?.pageInfo?.hasNextPage || false;
      ordersCursor = data?.orders?.pageInfo?.endCursor || null;
      pageCount += 1;
    }

    Object.keys(skuMap).forEach((key) => {
      skuMap[key].sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
    });

    return skuMap;
  };

  const escapeHtml = (value: string): string =>
    String(value ?? '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');

  const buildOrderPrintHtml = (
    printDate: string,
    printItems: {
      sku: string;
      lagerstandort: string;
      title: string;
      quantity: number;
      customerOrders: { name: string; quantity: number; isNew: boolean }[];
    }[],
  ): string => {
    const rows = printItems
      .map((item) => {
        let cumulated = 0;
        const orderEntries = item.customerOrders
          .map((customerOrder) => {
            cumulated += customerOrder.quantity;
            const exceeds: boolean = cumulated > item.quantity;
            const label = escapeHtml(
              `${customerOrder.isNew ? `${customerOrder.name} N` : customerOrder.name}: ${customerOrder.quantity}`,
            );
            return exceeds
              ? `<div><span class="exceeded">${label}</span></div>`
              : `<div>${label}</div>`;
          })
          .join('');
        return `<tr><td>${escapeHtml(item.sku)}</td><td>${escapeHtml(
          item.lagerstandort,
        )}</td><td>${escapeHtml(item.title)}</td><td>${item.quantity}</td><td>${orderEntries}</td></tr>`;
      })
      .join('');

    const heading = `Bestellung vom ${escapeHtml(formatDateDisplay(printDate))}`;

    return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>${heading}</title>
<style>
  body { font-family: Arial, Helvetica, sans-serif; font-size: 12px; margin: 16px; }
  h1 { font-size: 16px; margin: 0 0 12px 0; }
  table { border-collapse: collapse; width: 100%; }
  th, td { border: 1px solid #999; padding: 4px 6px; text-align: left; vertical-align: top; }
  th { background-color: #f1f1f1; }
  tr { page-break-inside: avoid; }
  thead { display: table-header-group; }
  .exceeded { background-color: #c00000; color: #ffffff; font-weight: bold; padding: 1px 5px; border-radius: 2px; }
  @media print {
    body { margin: 0; }
    table { width: 100%; font-size: 11pt; }
  }
</style>
</head>
<body>
<h1>${heading}</h1>
<table>
<thead><tr><th>SKU</th><th>Standort</th><th>Bezeichnung</th><th>Bestellt</th><th>Bestellungen</th></tr></thead>
<tbody>${rows}</tbody>
</table>
</body>
</html>`;
  };

  const buildInventoryPrintHtml = (heading: string, rows: any[]): string => {
    const body = rows
      .map(
        (item) =>
          `<tr><td>${escapeHtml(String(item.sku ?? ''))}</td><td>${escapeHtml(String(item.title ?? ''))}</td><td>${escapeHtml(
            String(item.lagerstandort ?? ''),
          )}</td><td class="num">${escapeHtml(String(item.stock ?? ''))}</td><td class="num">${escapeHtml(
            String(item.ve ?? ''),
          )}</td></tr>`,
      )
      .join('');
    const stand = new Date().toLocaleDateString('de-DE');
    return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>${escapeHtml(heading)}</title>
<style>
  body { font-family: Arial, Helvetica, sans-serif; font-size: 12px; margin: 16px; }
  h1 { font-size: 16px; margin: 0 0 2px 0; }
  .stand { color: #555; margin: 0 0 10px 0; }
  table { border-collapse: collapse; width: 100%; }
  th, td { border: 1px solid #999; padding: 4px 6px; text-align: left; vertical-align: top; }
  th { background-color: #f1f1f1; }
  td.num { text-align: right; }
  tr { page-break-inside: avoid; }
  thead { display: table-header-group; }
  @media print { table { font-size: 11pt; } }
</style>
</head>
<body>
<h1>${escapeHtml(heading)}</h1>
<p class="stand">Stand: ${stand}</p>
<table>
<thead><tr><th>SKU</th><th>Bezeichnung</th><th>Lagerstandort</th><th>Verfügbar</th><th>VE</th></tr></thead>
<tbody>${body}</tbody>
</table>
</body>
</html>`;
  };

  // Druckansichten liegen kurz auf dem eigenen Server (/print/<id>) und öffnen sich in einem normalen
  // neuen Fenster (kein blob:/data:-Link). Das Fenster wird beim Klick sofort geöffnet, damit der
  // Popup-Blocker nicht greift; wird es dennoch blockiert, erscheint stattdessen ein Link.
  const deliverPrintHtml = async (html: string, win: Window | null): Promise<void> => {
    const { path } = await backend.createPrint(html);
    const url = new URL(path, window.location.origin).toString();
    if (win && !win.closed) {
      win.location.href = url;
      setPrintLink('');
    } else {
      setPrintLink(url);
    }
  };

  const runPrint = async (build: () => Promise<string>): Promise<void> => {
    if (activeActionRef.current) return; // läuft bereits eine Aktion, keine zweite Druckansicht öffnen
    // Das Fenster muss direkt im Klick geöffnet werden (vor jeder Wartezeit), sonst blockiert es der Browser.
    const win = preOpenPrintWindow();
    await runExclusive('print', async () => {
      setError('');
      setPrintLink('');
      try {
        const html = await build();
        await deliverPrintHtml(html, win);
      } catch (printError: any) {
        try {
          win?.close();
        } catch {
          // Fenster bereits geschlossen
        }
        setError(`Druckansicht konnte nicht erstellt werden: ${printError?.message || 'Unbekannter Fehler'}`);
      }
    });
  };

  const prepareOrderPrint = async (order: PurchaseOrder): Promise<string> => {
    const skus = order.items.map((i) => i.sku);
    const lagerstandortMap: { [key: string]: string } = {};

    const batchSize = 50;
    for (let i = 0; i < skus.length; i += batchSize) {
      const batch = skus.slice(i, i + batchSize);
      const skuQuery = batch.map((s) => `sku:${s}`).join(' OR ');
      const query = `query ProductVariantsBySku($query: String!) {
        productVariants(first: 50, query: $query) {
          edges {
            node {
              sku
              product {
                lagerstandort: metafield(namespace: "custom", key: "lagerstandort") {
                  value
                }
              }
            }
          }
        }
      }`;
      const { data } = await shopifyQuery(query, { variables: { query: skuQuery } });
      if (data?.productVariants?.edges) {
        for (const edge of data.productVariants.edges) {
          lagerstandortMap[edge.node.sku] = edge.node.product?.lagerstandort?.value || '';
        }
      }
    }

    const customerOrdersBySku = await loadOpenCustomerOrdersBySku(order.date);

    const sortedItems = [...order.items]
      .sort((a, b) => a.sku.localeCompare(b.sku))
      .map((item) => ({
        sku: item.sku,
        lagerstandort: lagerstandortMap[item.sku] ?? item.lagerstandort ?? '',
        title: item.title,
        quantity: item.quantity,
        customerOrders: customerOrdersBySku[item.sku] ?? [],
      }));

    return buildOrderPrintHtml(order.date, sortedItems);
  };

  const prepareOrderPrintWithoutGlazures = async (order: PurchaseOrder): Promise<string> => {
    const skus = order.items.map((i) => i.sku);
    const lagerstandortMap: { [key: string]: string } = {};
    const productTypeMap: { [key: string]: string } = {};

    const batchSize = 50;
    for (let i = 0; i < skus.length; i += batchSize) {
      const batch = skus.slice(i, i + batchSize);
      const skuQuery = batch.map((s) => `sku:${s}`).join(' OR ');
      const query = `query ProductVariantsBySku($query: String!) {
        productVariants(first: 50, query: $query) {
          edges {
            node {
              sku
              product {
                productType
                lagerstandort: metafield(namespace: "custom", key: "lagerstandort") {
                  value
                }
              }
            }
          }
        }
      }`;
      const { data } = await shopifyQuery(query, { variables: { query: skuQuery } });
      if (data?.productVariants?.edges) {
        for (const edge of data.productVariants.edges) {
          lagerstandortMap[edge.node.sku] = edge.node.product?.lagerstandort?.value || '';
          productTypeMap[edge.node.sku] = edge.node.product?.productType || '';
        }
      }
    }

    const customerOrdersBySku = await loadOpenCustomerOrdersBySku(order.date);

    const sortedItems = [...order.items]
      .filter((item) => {
        const pt = productTypeMap[item.sku] || '';
        return pt.toLowerCase() !== 'glasuren';
      })
      .sort((a, b) => a.sku.localeCompare(b.sku))
      .map((item) => ({
        sku: item.sku,
        lagerstandort: lagerstandortMap[item.sku] ?? item.lagerstandort ?? '',
        title: item.title,
        quantity: item.quantity,
        customerOrders: customerOrdersBySku[item.sku] ?? [],
      }));

    return buildOrderPrintHtml(order.date, sortedItems);
  };

  const handlePrintOrder = (order: PurchaseOrder): Promise<void> => runPrint(() => prepareOrderPrint(order));

  const handlePrintOrderWithoutGlazures = (order: PurchaseOrder): Promise<void> =>
    runPrint(() => prepareOrderPrintWithoutGlazures(order));

  const ensureLagerstandortAllowed = async (newValue: string): Promise<boolean> => {
    if (locations.includes(newValue)) return true;

    const defQuery = `query {
      metafieldDefinitions(first: 10, ownerType: PRODUCT, namespace: "custom", key: "lagerstandort") {
        edges {
          node {
            id
            name
            validations { name value }
          }
        }
      }
    }`;

    const { data: defData, errors: defErrors } = await shopifyQuery(defQuery);

    // Wenn keine Definition existiert oder die Abfrage fehlschlägt, direkt versuchen zu speichern.
    if (defErrors?.length > 0 || !defData?.metafieldDefinitions?.edges?.length) return true;

    const definition = defData.metafieldDefinitions.edges[0].node;
    const defName = definition.name;

    // Wenn keine 'choices'-Validierung existiert, gibt es keine Einschränkung.
    const choicesValidation = definition.validations?.find((v: any) => v.name === 'choices');
    if (!choicesValidation) return true;

    let currentChoices: string[] = [];
    try {
      currentChoices = JSON.parse(choicesValidation.value || '[]');
    } catch {
      currentChoices = [];
    }

    if (currentChoices.includes(newValue)) return true;

    const updatedChoices = [...currentChoices, newValue].sort();

    const updateMutation = `mutation MetafieldDefinitionUpdate($definition: MetafieldDefinitionUpdateInput!) {
      metafieldDefinitionUpdate(definition: $definition) {
        updatedDefinition { id }
        userErrors { field message code }
      }
    }`;

    // Versuche die Definition zu aktualisieren, damit der neue Wert erlaubt wird.
    await shopifyQuery(updateMutation, {
      variables: {
        definition: {
          key: 'lagerstandort',
          namespace: 'custom',
          ownerType: 'PRODUCT',
          name: defName,
          validations: [{ name: 'choices', value: JSON.stringify(updatedChoices) }],
        },
      },
    });

    // Unabhängig vom Ergebnis der Definitionsaktualisierung immer den Speicherversuch
    // zulassen. Schlägt der eigentliche metafieldsSet-Aufruf fehl, wird dessen konkrete
    // Fehlermeldung angezeigt.
    return true;
  };

  const deleteOrder = async (orderId: string): Promise<void> => {
    try {
      // Das Backend schreibt „Bestellung gelöscht" je Position in die Historie und löscht die Bestellung.
      await backend.deleteOrder(orderId);
    } catch (deleteError: any) {
      setError(
        `Bestellung konnte nicht gelöscht werden: ${deleteError?.message || 'Unbekannter Fehler'}`,
      );
      return;
    }

    setPurchaseOrders(purchaseOrders.filter((o) => o.id !== orderId));
    setExpandedOrders(expandedOrders.filter((id) => id !== orderId));
    const { [orderId]: _, ...rest } = editingOrders;
    setEditingOrders(rest);
  };

  const addItemToOrder = async (orderId: string, sku: string, quantity: number): Promise<void> => {
    if (!sku || quantity <= 0) {
      setError('Bitte SKU und Menge eingeben');
      return;
    }

    const query = `query ProductVariantBySku($sku: String!) {
      productVariants(first: 1, query: $sku) {
        edges {
          node {
            id
            sku
            title
            displayName
            inventoryItem {
              id
            }
            media(first: 1) {
              edges {
                node {
                  ... on MediaImage {
                    image {
                      url
                    }
                  }
                }
              }
            }
            product {
              featuredMedia {
                preview {
                  image {
                    url
                  }
                }
              }
              lagerstandort: metafield(namespace: "custom", key: "lagerstandort") {
                value
              }
            }
          }
        }
      }
    }`;

    const { data, errors } = await shopifyQuery(query, {
      variables: { sku: `sku:${sku}` },
    });

    if (errors?.length > 0) {
      setError(errors.map((e: any) => e.message).join(', '));
      return;
    }

    if (!data?.productVariants?.edges?.length) {
      setError('SKU nicht gefunden');
      return;
    }

    const variant = data.productVariants.edges[0].node;
    const imageUrl =
      variant.media?.edges?.[0]?.node?.image?.url ||
      variant.product?.featuredMedia?.preview?.image?.url ||
      '';
    const newItem: OrderItem = {
      sku: variant.sku,
      quantity,
      inventoryItemId: variant.inventoryItem.id,
      title: variant.displayName || variant.title,
      imageUrl,
      lagerstandort: variant.product?.lagerstandort?.value || '',
    };

    const editingOrder = editingOrders[orderId];
    if (editingOrder) {
      editingOrder.items.push(newItem);
      setEditingOrders({ ...editingOrders, [orderId]: editingOrder });
    }
  };

  const saveOrderChanges = async (): Promise<void> => {
    setError('');
    const failed: string[] = [];
    let updatedOrders = purchaseOrders;

    for (const id of Object.keys(editingOrders)) {
      const edited = editingOrders[id];
      const original = purchaseOrders.find((o) => o.id === id);
      if (!edited || !original || original.status !== 'open') continue;

      try {
        const saved = await backend.updateOrder(id, {
          items: edited.items.map(toApiItem),
          trackingNumber: edited.trackingNumber || '',
        });
        updatedOrders = updatedOrders.map((o) =>
          o.id === id ? { ...edited, status: saved.status, verbuchtAm: saved.verbuchtAm } : o,
        );
      } catch (saveError: any) {
        failed.push(
          `Bestellung vom ${formatDateDisplay(original.date)}: ${saveError?.message || 'Fehler'}`,
        );
      }
    }

    setPurchaseOrders(updatedOrders);

    if (failed.length > 0) {
      setError(`Änderungen konnten nicht gespeichert werden: ${failed.join('; ')}`);
    } else {
      setSuccess('Änderungen gespeichert');
      setTimeout(() => setSuccess(''), 2000);
    }
  };

  const generateExport = async (): Promise<void> => {
    setLoading(true);
    setError('');

    const allVariants: any[] = [];
    let cursor: string | null = null;
    let hasNextPage: boolean = true;

    while (hasNextPage) {
      const query = `query ProductVariantsPaginated($first: Int!, $after: String) {
        productVariants(first: $first, after: $after) {
          edges {
            node {
              id
              sku
              title
              displayName
              inventoryItem {
                id
                inventoryLevels(first: 10) {
                  nodes {
                    location {
                      id
                      name
                    }
                    quantities(names: ["available", "on_hand", "committed", "incoming"]) {
                      name
                      quantity
                    }
                  }
                }
              }
            }
          }
          pageInfo {
            hasNextPage
            endCursor
          }
        }
      }`;

      const { data, errors } = await shopifyQuery(query, {
        variables: { first: 250, after: cursor },
      });

      if (errors?.length > 0) {
        setError(errors.map((e: any) => e.message).join(', '));
        setLoading(false);
        return;
      }

      if (data?.productVariants?.edges) {
        allVariants.push(...data.productVariants.edges.map((e: any) => e.node));
      }

      hasNextPage = data?.productVariants?.pageInfo?.hasNextPage || false;
      cursor = data?.productVariants?.pageInfo?.endCursor || null;
    }

    if (allVariants.length === 0) {
      setError('Keine Produkte gefunden');
      setLoading(false);
      return;
    }

    const { id: activeLocationId } = await resolveActiveLocation();

    const csvLines: string[] = [
      'SKU,Artikelbezeichnung,Verfügbar,Auf Lager,Zugesagt,Eingehend,Bestellt',
    ];

    for (const variant of allVariants) {
      const sku = variant.sku || '';
      const title = variant.displayName || variant.title || '';

      let available = 0;
      let onHand = 0;
      let committed = 0;
      let incoming = 0;

      if (variant.inventoryItem?.inventoryLevels?.nodes) {
        for (const level of variant.inventoryItem.inventoryLevels.nodes) {
          if (level.location.id === activeLocationId) {
            for (const q of level.quantities) {
              if (q.name === 'available') available = q.quantity;
              else if (q.name === 'on_hand') onHand = q.quantity;
              else if (q.name === 'committed') committed = q.quantity;
              else if (q.name === 'incoming') incoming = q.quantity;
            }
            break;
          }
        }
      }

      let ordered = 0;
      for (const order of purchaseOrders) {
        if (order.status === 'open') {
          for (const item of order.items) {
            if (item.sku === sku) {
              ordered += item.quantity;
            }
          }
        }
      }

      csvLines.push(`${sku},"${title}",${available},${onHand},${committed},${incoming},${ordered}`);
    }

    const csvContent = csvLines.join('\n');
    setExportData(`data:text/csv;charset=utf-8,${encodeURIComponent(csvContent)}`);
    setLoading(false);
  };

  const loadLocationsList = async (): Promise<void> => {
    setLoading(true);
    setError('');

    const query = `query {
      shop {
        metafield(namespace: "custom", key: "lagerstandort_liste") {
          value
        }
      }
    }`;

    const { data, errors } = await shopifyQuery(query);

    if (errors?.length > 0) {
      setError(errors.map((e: any) => e.message).join(', '));
      setLoading(false);
      return;
    }

    const locationsList = data?.shop?.metafield?.value ? JSON.parse(data.shop.metafield.value) : [];
    setLocations(locationsList);
    setInventoryData([]);
    setInventoryLoaded(false);
    setLoading(false);
  };

  const loadInventory = async (): Promise<void> => {
    setLoading(true);
    setError('');

    const allVariants: any[] = [];
    let cursor: string | null = null;
    let hasNextPage: boolean = true;

    while (hasNextPage) {
      const query = `query ProductVariantsPaginated($first: Int!, $after: String) {
        productVariants(first: $first, after: $after) {
          edges {
            node {
              id
              sku
              title
              displayName
              inventoryItem {
                inventoryLevels(first: 10) {
                  nodes {
                    location {
                      id
                      name
                    }
                    quantities(names: ["available"]) {
                      name
                      quantity
                    }
                  }
                }
              }
              product {
                lagerstandort: metafield(namespace: "custom", key: "lagerstandort") {
                  value
                }
                tags
              }
            }
          }
          pageInfo {
            hasNextPage
            endCursor
          }
        }
      }`;

      const { data, errors } = await shopifyQuery(query, {
        variables: { first: 250, after: cursor },
      });

      if (errors?.length > 0) {
        setError(errors.map((e: any) => e.message).join(', '));
        setLoading(false);
        return;
      }

      if (data?.productVariants?.edges) {
        allVariants.push(...data.productVariants.edges.map((e: any) => e.node));
      }

      hasNextPage = data?.productVariants?.pageInfo?.hasNextPage || false;
      cursor = data?.productVariants?.pageInfo?.endCursor || null;
    }

    if (allVariants.length === 0) {
      setError('Keine Produkte gefunden');
      setLoading(false);
      return;
    }

    const { id: activeLocationId } = await resolveActiveLocation();

    const inventory: any[] = [];
    const uniqueLocations = new Set<string>();

    for (const variant of allVariants) {
      const sku = variant.sku || '';
      const title = variant.displayName || variant.title || '';
      const lagerstandort = variant.product?.lagerstandort?.value || '';

      if (lagerstandort) {
        uniqueLocations.add(lagerstandort);
      }

      let stock = 0;
      if (variant.inventoryItem?.inventoryLevels?.nodes) {
        for (const level of variant.inventoryItem.inventoryLevels.nodes) {
          if (level.location.id === activeLocationId) {
            const qty = level.quantities.find((q: any) => q.name === 'available');
            stock = qty?.quantity || 0;
            break;
          }
        }
      }

      let ve = '';
      if (variant.product?.tags) {
        for (const tag of variant.product.tags) {
          if (tag.startsWith('VE')) {
            const match = tag.match(/VE(\d+)/);
            if (match) {
              ve = match[1];
              break;
            }
          }
        }
      }

      inventory.push({ sku, title, lagerstandort, stock, ve });
    }

    const sortedLocations = Array.from(uniqueLocations).sort();
    setLocations(sortedLocations);
    setInventoryData(inventory);
    setInventoryLoaded(true);

    // Save locations list to shop metafield for use in Order Printer templates
    if (sortedLocations.length > 0 && shopId) {
      const saveLocationsMutation = `mutation MetafieldsSet($metafields: [MetafieldsSetInput!]!) {
        metafieldsSet(metafields: $metafields) {
          metafields {
            id
          }
          userErrors {
            field
            message
          }
        }
      }`;

      await shopifyQuery(saveLocationsMutation, {
        variables: {
          metafields: [
            {
              ownerId: shopId,
              namespace: 'custom',
              key: 'lagerstandort_liste',
              type: 'list.single_line_text_field',
              value: JSON.stringify(sortedLocations),
            },
          ],
        },
      });
      // Silently ignore errors - this is a background operation
    }

    setLoading(false);
  };

  const formatDateDisplay = (isoDate: string): string => {
    const [year, month, day] = isoDate.split('-');
    return `${day}.${month}.${year}`;
  };

  const formatDateTimeDisplay = (isoTimestamp: string): string => {
    const date = new Date(isoTimestamp);
    const day = String(date.getDate()).padStart(2, '0');
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const year = date.getFullYear();
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    return `${day}.${month}.${year} um ${hours}:${minutes}`;
  };

  const handleAnalyzePrices = async (): Promise<void> => {
    if (!priceUpdateFile) {
      setError('Bitte eine CSV-Datei auswählen');
      return;
    }

    setPriceUpdateLoading(true);
    setError('');

    try {
      const text = await priceUpdateFile.text();
      const lines = text.split('\n').filter((l) => l.trim());

      const priceMap: { [sku: string]: string } = {};
      const prefixMap: { prefix: string; price: string }[] = [];

      for (let i = 1; i < lines.length; i++) {
        const cols = lines[i].split(';');
        if (cols.length < 4) continue;
        const rawSku = (cols[0] || '').trim().replace(/^"|"$/g, '');
        const rawPrice = (cols[3] || '').trim().replace(/^"|"$/g, '').replace(',', '.');
        if (!rawSku || !rawPrice) continue;
        const price = parseFloat(rawPrice);
        if (isNaN(price)) continue;
        if (rawSku.endsWith('...')) {
          prefixMap.push({ prefix: rawSku.slice(0, -3), price: price.toString() });
        } else {
          priceMap[rawSku] = price.toString();
        }
      }

      const allVariants: any[] = [];
      let cursor: string | null = null;
      let hasNextPage: boolean = true;

      while (hasNextPage) {
        const query = `query ProductVariantsPaginated($first: Int!, $after: String) {
          productVariants(first: $first, after: $after) {
            edges {
              node {
                id
                sku
                displayName
                einkaufspreis: metafield(namespace: "custom", key: "einkaufspreis") {
                  id
                  value
                }
              }
            }
            pageInfo {
              hasNextPage
              endCursor
            }
          }
        }`;

        const { data, errors } = await shopifyQuery(query, {
          variables: { first: 250, after: cursor },
        });

        if (errors?.length > 0) {
          setError(errors.map((e: any) => e.message).join(', '));
          setPriceUpdateLoading(false);
          return;
        }

        if (data?.productVariants?.edges) {
          allVariants.push(...data.productVariants.edges.map((e: any) => e.node));
        }

        hasNextPage = data?.productVariants?.pageInfo?.hasNextPage || false;
        cursor = data?.productVariants?.pageInfo?.endCursor || null;
      }

      const results: PriceUpdateResult[] = [];

      for (const variant of allVariants) {
        const sku = variant.sku || '';
        const currentPrice = variant.einkaufspreis?.value || '';
        let newPrice = priceMap[sku] || '';

        if (!newPrice) {
          for (const { prefix, price } of prefixMap) {
            if (sku.startsWith(prefix)) {
              newPrice = price;
              break;
            }
          }
        }

        if (!newPrice) {
          results.push({
            sku,
            variantId: variant.id,
            title: variant.displayName || '',
            currentPrice,
            newPrice: '',
            status: 'not_in_excel',
          });
        } else if (currentPrice === newPrice) {
          results.push({
            sku,
            variantId: variant.id,
            title: variant.displayName || '',
            currentPrice,
            newPrice,
            status: 'unchanged',
          });
        } else {
          results.push({
            sku,
            variantId: variant.id,
            title: variant.displayName || '',
            currentPrice,
            newPrice,
            status: 'changed',
          });
        }
      }

      setPriceUpdateResults(results);
      setPriceUpdateSelected(results.filter((r) => r.status === 'changed').map((r) => r.sku));
      setPriceUpdateStep(2);
    } catch (err: any) {
      setError(err.message || 'Fehler beim Analysieren der Datei');
    }

    setPriceUpdateLoading(false);
  };

  const handleUpdatePrices = async (): Promise<void> => {
    setPriceUpdateLoading(true);
    setError('');
    setPriceUpdateSuccess('');

    const mutation = `mutation MetafieldsSet($metafields: [MetafieldsSetInput!]!) {
      metafieldsSet(metafields: $metafields) {
        metafields {
          id
          value
        }
        userErrors {
          field
          message
        }
      }
    }`;

    let updatedCount = 0;

    for (const sku of priceUpdateSelected) {
      const result = priceUpdateResults.find((r) => r.sku === sku);
      if (!result) continue;

      const { data, errors } = await shopifyQuery(mutation, {
        variables: {
          metafields: [
            {
              ownerId: result.variantId,
              namespace: 'custom',
              key: 'einkaufspreis',
              type: 'number_decimal',
              value: result.newPrice,
            },
          ],
        },
      });

      if (errors?.length > 0 || data?.metafieldsSet?.userErrors?.length > 0) {
        const errMsg =
          errors?.map((e: any) => e.message).join(', ') ||
          data?.metafieldsSet?.userErrors?.map((e: any) => e.message).join(', ');
        setPriceUpdateResults((prev) =>
          prev.map((r) => (r.sku === sku ? { ...r, status: 'error', errorMessage: errMsg } : r)),
        );
      } else {
        updatedCount++;
        setPriceUpdateResults((prev) =>
          prev.map((r) =>
            r.sku === sku ? { ...r, currentPrice: r.newPrice, status: 'unchanged' } : r,
          ),
        );
      }
    }

    setPriceUpdateSelected([]);
    setPriceUpdateSuccess(`${updatedCount} Preise erfolgreich aktualisiert`);
    setPriceUpdateLoading(false);
  };

  const handleSaveInventoryEdits = async (): Promise<void> => {
    setInventorySaving(true);
    setError('');
    setSuccess('');

    // Varianten ohne SKU können nicht per SKU gefunden werden und werden nicht gespeichert.
    const skusToProcess = Object.keys(inventoryEdits).filter((sku) => sku !== '');
    if (skusToProcess.length === 0) {
      setInventoryEditMode(false);
      setInventorySaving(false);
      return;
    }

    const { id: locationId, error: locationError } = await resolveActiveLocation();
    if (!locationId) {
      setError(locationError || 'Kein aktiver Lagerstandort gefunden');
      setInventorySaving(false);
      return;
    }

    const failedSkus: string[] = [];
    let updatedCount = 0;

    for (const sku of skusToProcess) {
      let failedReason = '';
      const edits = inventoryEdits[sku];
      const currentItem = inventoryData.find((i) => i.sku === sku);
      if (!currentItem) continue;

      const lagerstandortChanged =
        edits.newLagerstandort !== undefined &&
        edits.newLagerstandort !== null &&
        edits.newLagerstandort !== currentItem.lagerstandort;
      const stockChanged =
        edits.newStock !== undefined &&
        !isNaN(edits.newStock) &&
        edits.newStock !== currentItem.stock;

      if (!lagerstandortChanged && !stockChanged) continue;

      const variantQuery = `query ProductVariantBySku($sku: String!) {
        productVariants(first: 1, query: $sku) {
          edges {
            node {
              id
              inventoryItem {
                id
              }
              product {
                id
              }
            }
          }
        }
      }`;

      const { data: variantData, errors: variantErrors } = await shopifyQuery(variantQuery, {
        variables: { sku: `sku:${sku}` },
      });

      if (variantErrors?.length > 0 || !variantData?.productVariants?.edges?.length) {
        failedSkus.push(sku);
        continue;
      }

      const variant = variantData.productVariants.edges[0].node;
      const productId = variant.product?.id;
      const inventoryItemId = variant.inventoryItem?.id;

      let itemFailed = false;

      if (lagerstandortChanged && productId && edits.newLagerstandort) {
        await ensureLagerstandortAllowed(edits.newLagerstandort);
        {
          const metafieldMutation = `mutation MetafieldsSet($metafields: [MetafieldsSetInput!]!) {
            metafieldsSet(metafields: $metafields) {
              metafields {
                id
              }
              userErrors {
                field
                message
              }
            }
          }`;

          const { data: mfData, errors: mfErrors } = await shopifyQuery(metafieldMutation, {
            variables: {
              metafields: [
                {
                  ownerId: productId,
                  namespace: 'custom',
                  key: 'lagerstandort',
                  type: 'single_line_text_field',
                  value: edits.newLagerstandort,
                },
              ],
            },
          });

          if (mfErrors?.length > 0 || mfData?.metafieldsSet?.userErrors?.length > 0) {
            failedReason =
              mfErrors?.map((e: any) => e.message).join(', ') ||
              mfData?.metafieldsSet?.userErrors?.map((e: any) => e.message).join(', ') ||
              'Unbekannter Fehler';
            itemFailed = true;
          }
        }
      }

      if (stockChanged && inventoryItemId && !itemFailed) {
        const delta = (edits.newStock as number) - currentItem.stock;
        if (delta !== 0) {
          const adjustMutation = `mutation inventoryAdjustQuantities($input: InventoryAdjustQuantitiesInput!, $idempotencyKey: String!) {
            inventoryAdjustQuantities(input: $input) @idempotent(key: $idempotencyKey) {
              inventoryAdjustmentGroup {
                createdAt
              }
              userErrors {
                field
                message
              }
            }
          }`;

          const { data: adjData, errors: adjErrors } = await shopifyQuery(adjustMutation, {
            variables: {
              idempotencyKey: generateIdempotencyKey(),
              input: {
                reason: 'correction',
                name: 'available',
                changes: [
                  {
                    delta,
                    inventoryItemId,
                    locationId,
                  },
                ],
              },
            },
          });

          if (adjErrors?.length > 0 || adjData?.inventoryAdjustQuantities?.userErrors?.length > 0) {
            itemFailed = true;
          }
        }
      }

      if (itemFailed) {
        failedSkus.push(failedReason ? `${sku} (${failedReason})` : sku);
      } else {
        updatedCount++;
      }
    }

    if (failedSkus.length > 0) {
      setError(`Folgende SKUs konnten nicht aktualisiert werden: ${failedSkus.join(', ')}`);
    }

    if (updatedCount > 0) {
      setSuccess(`${updatedCount} Artikel erfolgreich aktualisiert`);
      setTimeout(() => setSuccess(''), 3000);
    }

    const editsSnapshot = inventoryEdits;
    const failedSet = new Set(failedSkus.map((s) => s.split(' (')[0]));

    const updatedInventoryData = inventoryData.map((item) => {
      const edits = editsSnapshot[item.sku];
      if (!edits || failedSet.has(item.sku)) return item;
      const nextItem = { ...item };
      if (
        edits.newLagerstandort !== undefined &&
        edits.newLagerstandort !== '' &&
        edits.newLagerstandort !== item.lagerstandort
      ) {
        nextItem.lagerstandort = edits.newLagerstandort;
      }
      if (edits.newStock !== undefined && edits.newStock !== item.stock) {
        nextItem.stock = edits.newStock;
      }
      return nextItem;
    });
    setInventoryData(updatedInventoryData);

    const mergedLocSet = new Set<string>(locations);
    Object.entries(editsSnapshot).forEach(([sku, edits]) => {
      if (failedSet.has(sku)) return;
      if (edits.newLagerstandort && edits.newLagerstandort.trim() !== '') {
        mergedLocSet.add(edits.newLagerstandort);
      }
    });
    const updatedLocationsList = Array.from(mergedLocSet).sort();
    setLocations(updatedLocationsList);

    // Save updated locations list to shop metafield for use in Order Printer templates
    if (updatedLocationsList.length > 0 && shopId) {
      const saveLocationsMutation = `mutation MetafieldsSet($metafields: [MetafieldsSetInput!]!) {
        metafieldsSet(metafields: $metafields) {
          metafields {
            id
          }
          userErrors {
            field
            message
          }
        }
      }`;

      await shopifyQuery(saveLocationsMutation, {
        variables: {
          metafields: [
            {
              ownerId: shopId,
              namespace: 'custom',
              key: 'lagerstandort_liste',
              type: 'list.single_line_text_field',
              value: JSON.stringify(updatedLocationsList),
            },
          ],
        },
      });
      // Silently ignore errors - this is a background operation
    }

    setInventoryEdits({});
    setInventoryFreeTextRows({});
    setInventoryEditMode(false);
    setInventorySaving(false);
  };

  const handleSearchOrders = (): void => {
    setSearchExecuted(true);
    const query = searchQuery.trim().toLowerCase();
    if (!query) {
      setSearchResults([]);
      setSearchOrderCount(0);
      return;
    }

    const openOrders = purchaseOrders.filter((o) => o.status === 'open');
    const results: any[] = [];
    const matchedOrderIds = new Set<string>();

    for (const order of openOrders) {
      for (const item of order.items) {
        if (item.sku.toLowerCase().includes(query)) {
          results.push({
            orderDate: order.date,
            sku: item.sku,
            title: item.title,
            quantity: item.quantity,
          });
          matchedOrderIds.add(order.id);
        }
      }
    }

    setSearchResults(results);
    setSearchOrderCount(matchedOrderIds.size);
  };

  const loadHistory = async (): Promise<void> => {
    setHistoryLoading(true);
    setError('');

    try {
      // Nur Jahre anbieten, für die es Einträge gibt (ohne Einträge: das laufende Jahr).
      const stats = await backend.getStats();
      const withData = Object.keys(stats?.historyByYear ?? {}).sort((a, b) => b.localeCompare(a));
      const years = withData.length > 0 ? withData : [String(new Date().getFullYear())];
      const year = years.includes(historyYear) ? historyYear : years[0];
      setHistoryYears(years);
      setHistoryYear(year);
      setHistoryData(await backend.getHistory(year));
    } catch (historyError: any) {
      setError(`Historie konnte nicht geladen werden: ${historyError?.message || 'Unbekannter Fehler'}`);
    }
    setHistoryLoading(false);
  };

  // Historie einer abgeschlossenen Buchung erneut senden (die Bestände sind bei Shopify bereits gebucht).
  const retryPendingFinish = async (): Promise<void> => {
    if (!pendingFinish) return;
    setError('');
    try {
      const finished = await backend.finishReceiving(pendingFinish.orderId, {
        bookingId: pendingFinish.bookingId,
        results: pendingFinish.results,
      });
      setPurchaseOrders((prev) =>
        prev.map((o) =>
          o.id === pendingFinish.orderId
            ? { ...o, status: finished.order.status, verbuchtAm: finished.order.verbuchtAm }
            : o,
        ),
      );
      setPendingFinish(null);
      setSuccess('Historie wurde gespeichert.');
      setTimeout(() => setSuccess(''), 3000);
    } catch (retryError: any) {
      setError(
        `Historie konnte weiterhin nicht gespeichert werden: ${retryError?.message || 'Backend nicht erreichbar'}`,
      );
    }
  };

  // Zeigt, in welche Datenbank die App gerade schreibt (schützt vor Verwechslung von Test-/Produktiv-Datei).
  const describeDatabase = async (): Promise<string[]> => {
    try {
      const d = await backend.getDiag();
      const lines = [
        `Aktive Datenbank: ${d.dbPath} – ${d.orders} Bestellungen, ${d.history} Historieneinträge${d.importDone ? ', Import bereits ausgeführt (gesperrt)' : ''}`,
      ];
      if (d.sameFileAsSessions) {
        lines.push('ACHTUNG: Die Datenbank ist dieselbe Datei wie der Session-Speicher. DB_PATH und SESSION_DB_PATH müssen verschieden sein.');
      }
      return lines;
    } catch (diagError: any) {
      return [`Aktive Datenbank: nicht ermittelbar (${diagError?.message || 'Fehler'})`];
    }
  };

  const checkBackend = async (): Promise<void> => {
    setMigrationBusy(true);
    setError('');
    const log: string[] = [];
    try {
      const health = await fetch("/healthz");
      log.push(`Erreichbarkeit: HTTP ${health.status} ${await health.text()}`);
    } catch (checkError: any) {
      log.push(`Erreichbarkeit: FEHLGESCHLAGEN (${checkError?.message || 'Netzwerkfehler'})`);
    }
    try {
      const stats = await backend.getStats();
      log.push(
        `Anmeldung mit Shopify-Token: OK – Bestellungen ${JSON.stringify(stats.ordersByStatus)}, Historie ${JSON.stringify(stats.historyByYear)}`,
      );
    } catch (checkError: any) {
      log.push(`Anmeldung mit Shopify-Token: FEHLGESCHLAGEN (${checkError?.message || 'Fehler'})`);
    }
    log.push(...(await describeDatabase()));
    setMigrationLog(log);
    setMigrationBusy(false);
  };

  const readMigrationSources = async (): Promise<void> => {
    setMigrationBusy(true);
    setError('');
    setMigrationReport(null);
    try {
      const sources = await backend.readMetafieldSources();
      setMigrationSources(sources);
      if (migrationBackupUrl) URL.revokeObjectURL(migrationBackupUrl);
      setMigrationBackupUrl(backend.makeRawBackupUrl(sources));
      setMigrationLog([
        ...(await describeDatabase()),
        `Bestellungen im Metafeld: ${sources.orders.length}`,
        ...sources.historySources.map(
          (source) => `Historie "${source.name}": ${source.entries.length} Einträge`,
        ),
      ]);
    } catch (readError: any) {
      setError(readError?.message || 'Metafelder konnten nicht gelesen werden');
    }
    setMigrationBusy(false);
  };

  const runMigration = async (dryRun: boolean): Promise<void> => {
    if (!migrationSources) return;
    setMigrationBusy(true);
    setError('');
    setSuccess('');
    try {
      const report = await backend.migrateFromMetafields(migrationSources, dryRun);
      setMigrationReport(report);
      if (!dryRun) {
        setPurchaseOrders((await backend.listOrders('open,processing')) as PurchaseOrder[]);
        setSuccess('Import abgeschlossen. Bitte die Zahlen unten mit den Erwartungen vergleichen.');
      }
    } catch (migrationError: any) {
      setError(migrationError?.message || 'Import fehlgeschlagen');
    }
    setMigrationBusy(false);
  };

  if (loading && view === 'menu') {
    return (
      <s-page heading="Bestellverwaltung">
        <s-section>
          <s-spinner />
        </s-section>
      </s-page>
    );
  }

  if (view === 'menu') {
    return (
      <s-page heading="Bestellverwaltung">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}
          {systemStatus && (systemStatus.stuckBookings > 0 || systemStatus.unclearItems > 0) && (
            <s-banner tone="critical">
              Unterbrochene Wareneingangs-Buchung erkannt ({systemStatus.stuckBookings} unfertig, {systemStatus.unclearItems}{' '}
              Artikel mit unklarem Stand). Bitte unter „Bestellungen mit Verbuchungsproblemen" prüfen, nicht blind erneut
              buchen.
            </s-banner>
          )}
          {systemStatus?.monitor && systemStatus.monitor.problems?.length > 0 && (
            <s-banner tone="critical">
              <s-stack direction="block" gap="small">
                <s-text type="strong">Die Überwachung meldet Probleme:</s-text>
                {systemStatus.monitor.problems.map((p: any) => (
                  <s-text key={p.key}>
                    {p.level}: {p.subject}
                  </s-text>
                ))}
              </s-stack>
            </s-banner>
          )}
          {systemStatus?.monitor &&
            systemStatus.monitor.lastRun &&
            Date.now() - Date.parse(systemStatus.monitor.lastRun) > 3 * 3600 * 1000 && (
              <s-banner tone="warning">
                Die Überwachung hat sich seit {formatDateTimeDisplay(systemStatus.monitor.lastRun)} nicht gemeldet
                (Cron-Job prüfen).
              </s-banner>
            )}
          {systemStatus &&
            !systemStatus.monitor && (
              <s-banner tone="info">
                Die automatische Überwachung ist noch nicht eingerichtet (Cron-Job monitor.js, siehe Betriebshandbuch).
              </s-banner>
            )}
          {systemStatus?.lastBackup && Date.now() - Date.parse(systemStatus.lastBackup.at) > 26 * 3600 * 1000 && (
            <s-banner tone="warning">
              Das letzte Backup ist vom {formatDateTimeDisplay(systemStatus.lastBackup.at)} – älter als 26 Stunden.
            </s-banner>
          )}

      <s-section heading="Standort für Bestandsbuchungen">
        <s-stack direction="block" gap="base">
          <s-text>
            Wareneingang, Lagerbestand und CSV-Export verwenden diesen Standort. Ohne Auswahl gilt wie bisher der
            erste aktive Standort im Shop.
          </s-text>
          <s-select
            label="Standort"
            ref={listen('change', (e: any) => saveConfiguredLocation(e.currentTarget.value))}
            {...(configuredLocationId ? { value: configuredLocationId } : {})}
            {...(locationSaving ? { disabled: true } : {})}
          >
            <s-option value="">Automatisch (erster aktiver Standort)</s-option>
            {availableLocations.map((loc) => (
              <s-option key={loc.id} value={loc.id}>
                {loc.name}
              </s-option>
            ))}
          </s-select>
        </s-stack>
      </s-section>
          {pendingFinish && (
            <s-banner tone="warning">
              <s-stack direction="block" gap="small">
                <s-text>
                  Ein Wareneingang wurde bei Shopify gebucht, aber die Historie ist noch nicht
                  gespeichert. Nicht erneut buchen.
                </s-text>
                <s-button onClick={() => runExclusive('retryFinish', retryPendingFinish)} {...busyProps('retryFinish')}>
                  Historie erneut speichern
                </s-button>
              </s-stack>
            </s-banner>
          )}
          <s-stack gap="base" direction="block">
            <s-button variant="primary" onClick={() => setView('order')}>
              Bestellung erfassen
            </s-button>
            <s-button onClick={() => setView('receiving')}>Wareneingang verbuchen</s-button>
            <s-button onClick={() => setView('completed-orders')}>
              Bestellungen mit Verbuchungsproblemen
            </s-button>
            <s-stack direction="inline" gap="base">
              <s-button onClick={() => setView('view-orders')}>
                Bestellungen anzeigen/drucken
              </s-button>
              <s-button onClick={() => setView('search-orders')}>
                Bestellte Artikel abfragen
              </s-button>
            </s-stack>
            <s-button
              onClick={() => {
                setView('export');
                generateExport();
              }}
            >
              Export Inventar mit offenen Bestellungen
            </s-button>
            <s-button
              onClick={() => {
                setView('history');
                loadHistory();
              }}
            >
              Bestellhistorie
            </s-button>
            <s-button
              onClick={() => {
                setPriceUpdateStep(1);
                setPriceUpdateResults([]);
                setPriceUpdateSelected([]);
                setPriceUpdateSuccess('');
                setView('price-update');
              }}
            >
              Einkaufspreise mit Keramix-Preisliste aktualisieren
            </s-button>
            <s-button
              onClick={() => {
                setView('inventory');
                loadLocationsList();
              }}
            >
              Lagerbestand anzeigen (pro Lagerstandort)
            </s-button>
            <s-button onClick={() => setView('migration')}>Datenmigration (einmalig)</s-button>
          </s-stack>
        </s-section>
      </s-page>
    );
  }

  if (view === 'order') {
    return (
      <s-page heading="Bestellung erfassen">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}
          {importWarning && <s-banner tone="warning">{importWarning}</s-banner>}

          <s-text-area
            label="Bestelldaten eingeben"
            placeholder={`Artikel Menge Stk/Karton\nANTI 4 12\nBD 2 12\nBERTA 3 8`}
            rows={10}
            value={orderInput}
            onInput={(e: any) => setOrderInput(e.currentTarget.value)}
          />

          <s-button onClick={() => runExclusive('import', parseOrderInput)} {...busyProps('import')}>
            Importieren
          </s-button>

          {importedItems.length > 0 && (
            <>
              <s-section padding="none">
                <s-table>
                  <s-table-header-row>
                    <s-table-header listSlot="primary">Bild</s-table-header>
                    <s-table-header listSlot="labeled">SKU</s-table-header>
                    <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                    <s-table-header listSlot="labeled">Bestellte Menge</s-table-header>
                  </s-table-header-row>
                  <s-table-body>
                    {importedItems.map((item, idx) => (
                      <s-table-row key={idx}>
                        <s-table-cell>
                          {item.imageUrl && <s-thumbnail src={item.imageUrl} alt={item.title} />}
                        </s-table-cell>
                        <s-table-cell>{item.sku}</s-table-cell>
                        <s-table-cell>{item.title}</s-table-cell>
                        <s-table-cell>{item.quantity}</s-table-cell>
                      </s-table-row>
                    ))}
                  </s-table-body>
                </s-table>
              </s-section>

              <s-text-field
                label="Bestelldatum (TT.MM.JJJJ)"
                placeholder="01.01.2024"
                value={orderDateDisplay}
                onInput={(e: any) => {
                  const val = e.currentTarget.value;
                  setOrderDateDisplay(val);
                  if (/^\d{2}\.\d{2}\.\d{4}$/.test(val)) {
                    const [dd, mm, yyyy] = val.split('.');
                    setOrderDate(`${yyyy}-${mm}-${dd}`);
                  } else {
                    setOrderDate('');
                  }
                }}
              />

              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button onClick={() => setView('menu')}>Zurück</s-button>
                <s-button variant="primary" onClick={() => runExclusive('saveOrder', saveOrder)} {...busyProps('saveOrder')}>
                  Speichern
                </s-button>
              </s-stack>
            </>
          )}

          {importedItems.length === 0 && (
            <s-button onClick={() => setView('menu')}>Zurück</s-button>
          )}
        </s-section>
      </s-page>
    );
  }

  if (view === 'receiving') {
    const openOrders = purchaseOrders.filter((o) => o.status === 'open');

    return (
      <s-page heading="Wareneingang - Bestellung auswählen">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {openOrders.length === 0 && <s-text>Keine offenen Bestellungen vorhanden</s-text>}
          {receivingPreparing && (
            <s-stack direction="block" gap="small">
              <s-text color="subdued">Artikeldaten werden geladen...</s-text>
              <s-spinner />
            </s-stack>
          )}
          <s-stack gap="base" direction="block">
            {openOrders.map((order) => (
              <s-button
                key={order.id}
                disabled={(receivingPreparing) || undefined}
                onClick={() => startReceiving(order.id)}
              >
                Bestellung vom {formatDateDisplay(order.date)} - {order.items.length} Artikel
              </s-button>
            ))}
            <s-button onClick={() => setView('menu')}>Zurück</s-button>
          </s-stack>
        </s-section>
      </s-page>
    );
  }

  if (view === 'receiving-details') {
    const order = purchaseOrders.find((o) => o.id === selectedOrderId);
    if (!order) {
      return (
        <s-page heading="Wareneingang">
          <s-section>
            <s-text>Bestellung nicht gefunden</s-text>
            <s-button onClick={() => setView('menu')}>Zurück</s-button>
          </s-section>
        </s-page>
      );
    }

    if (showIdempotencyWarning) {
      return (
        <s-page heading="Wareneingang">
          <s-section>
            {unclearSkus.length > 0 && (
              <s-banner tone="critical">
                <s-stack direction="block" gap="small">
                  <s-text type="strong">Unterbrochene Buchung erkannt</s-text>
                  <s-text>
                    Bei diesen Artikeln ist unklar, ob der Bestand bei Shopify bereits erhöht wurde:{' '}
                    {unclearSkus.map((u) => `${u.sku} (+${u.receivedQuantity})`).join(', ')}.
                  </s-text>
                  <s-text>
                    Bitte im Shopify-Admin den Bestandsverlauf dieser Artikel prüfen. Die angenommene Menge steht
                    deshalb auf 0 – nur dann erhöhen, wenn die Buchung nachweislich nicht erfolgt ist.
                  </s-text>
                </s-stack>
              </s-banner>
            )}
            <s-banner tone="warning">
              Wareneingang für diese Bestellung wurde bereits am{' '}
              {formatDateTimeDisplay(order.verbuchtAm!)} verbucht (oder der Vorgang wurde
              unterbrochen). Was möchten Sie tun?
            </s-banner>
            <s-stack direction="block" gap="base">
              <s-text>
                Option 1: Abbrechen — zurück zur Bestellauswahl (empfohlen wenn Buchung erfolgreich
                war)
              </s-text>
              <s-text>
                Option 2: Erneut verbuchen — Wareneingang nochmals buchen (nur wenn Buchung
                unvollständig war)
              </s-text>
              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button onClick={() => setView(receivingReturnView)}>Abbrechen</s-button>
                <s-button
                  variant="primary"
                  onClick={async () => {
                    await logDoubleBookingAttempt(order);
                    setShowIdempotencyWarning(false);
                  }}
                >
                  Ja, erneut verbuchen
                </s-button>
              </s-stack>
            </s-stack>
          </s-section>
        </s-page>
      );
    }

    const differingItems = receivingItems.filter((item) => item.receivedQuantity !== item.quantity);

    return (
      <s-page heading="Wareneingang verbuchen">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}
          {unclearSkus.length > 0 && (
              <s-banner tone="critical">
                <s-stack direction="block" gap="small">
                  <s-text type="strong">Unterbrochene Buchung erkannt</s-text>
                  <s-text>
                    Bei diesen Artikeln ist unklar, ob der Bestand bei Shopify bereits erhöht wurde:{' '}
                    {unclearSkus.map((u) => `${u.sku} (+${u.receivedQuantity})`).join(', ')}.
                  </s-text>
                  <s-text>
                    Bitte im Shopify-Admin den Bestandsverlauf dieser Artikel prüfen. Die angenommene Menge steht
                    deshalb auf 0 – nur dann erhöhen, wenn die Buchung nachweislich nicht erfolgt ist.
                  </s-text>
                </s-stack>
              </s-banner>
            )}

          {pendingFinish && !processing && (
            <s-banner tone="warning">
              <s-stack direction="block" gap="small">
                <s-text type="strong">
                  Bei Shopify gebucht, Historie noch nicht gespeichert – bitte NICHT erneut buchen.
                </s-text>
                <s-text>
                  Gebucht:{' '}
                  {pendingFinish.results
                    .filter((r) => r.outcome === 'booked')
                    .map((r) => `${r.sku} ${r.receivedQuantity}`)
                    .join(', ')}
                </s-text>
                <s-button onClick={() => runExclusive('retryFinish', retryPendingFinish)} {...busyProps('retryFinish')}>
                  Historie erneut speichern
                </s-button>
              </s-stack>
            </s-banner>
          )}

          {processing && (
            <s-banner tone="info">
              <s-stack direction="block" gap="small">
                <s-text type="strong">Wareneingang wird verbucht – bitte nicht schließen…</s-text>
                <s-text>{receivingProgress.phase}</s-text>
                {receivingProgress.total > 0 && (
                  <s-text>
                    {'█'.repeat(Math.round((receivingProgress.current / receivingProgress.total) * 20))}
                    {'░'.repeat(
                      20 - Math.round((receivingProgress.current / receivingProgress.total) * 20),
                    )}{' '}
                    {Math.round((receivingProgress.current / receivingProgress.total) * 100)} % –{' '}
                    {receivingProgress.current} von {receivingProgress.total} Artikeln abgeschlossen
                  </s-text>
                )}
                {receivingProgress.sku && (
                  <s-text color="subdued">Aktuelle SKU: {receivingProgress.sku}</s-text>
                )}
                <s-spinner />
              </s-stack>
            </s-banner>
          )}

          <s-stack gap="none" direction="block">
            <s-text color="subdued">
              Erklärung: Nur die Zahl im Feld "angenommene Menge" wird als Zugang verbucht.
            </s-text>
            <s-text color="subdued">
              - Bei Lieferung kaputter Artikel/zu wenig Stück -&gt; angenommene Menge entsprechend
              verringern.
            </s-text>
            <s-text color="subdued">
              - Bei Mehrlieferung angenommene Menge entsprechend erhöhen.
            </s-text>
            <s-text color="subdued">
              - Wurden zusätzliche SKU geliefert, müssen diese zuerst über "Bestellungen
              anzeigen/drucken" ergänzt werden.
            </s-text>
          </s-stack>

          <s-box padding="base" />

          <s-text>Bestellung vom {formatDateDisplay(order.date)}</s-text>

          {applyPreviousBookings && (
            <s-banner tone="info">
              Die angezeigten Mengen berücksichtigen bereits verbuchte Zugänge laut Bestellhistorie.
              Bitte prüfen Sie die Mengen vor dem Buchen.
            </s-banner>
          )}

          {showReceivingConfirm && (
            <>
              <s-banner tone="warning">Folgende Artikel weichen von der Bestellmenge ab:</s-banner>
              <s-section padding="none">
                <s-table>
                  <s-table-header-row>
                    <s-table-header listSlot="primary">SKU</s-table-header>
                    <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                    <s-table-header listSlot="labeled">Bestellt</s-table-header>
                    <s-table-header listSlot="labeled">Angenommen</s-table-header>
                    <s-table-header listSlot="labeled">Differenz</s-table-header>
                  </s-table-header-row>
                  <s-table-body>
                    {differingItems.map((item, idx) => (
                      <s-table-row key={idx}>
                        <s-table-cell>{item.sku}</s-table-cell>
                        <s-table-cell>{item.title}</s-table-cell>
                        <s-table-cell>{item.quantity}</s-table-cell>
                        <s-table-cell>{item.receivedQuantity}</s-table-cell>
                        <s-table-cell>{item.receivedQuantity - item.quantity}</s-table-cell>
                      </s-table-row>
                    ))}
                  </s-table-body>
                </s-table>
              </s-section>
              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button disabled={(processing) || undefined} onClick={() => setShowReceivingConfirm(false)}>
                  Abbrechen
                </s-button>
                <s-button variant="primary" disabled={(processing) || undefined} onClick={handleProcessReceiving}>
                  Ja, trotzdem buchen
                </s-button>
              </s-stack>
            </>
          )}

          {!showReceivingConfirm && (
            <>
              <s-section padding="none">
                <s-table>
                  <s-table-header-row>
                    <s-table-header listSlot="primary">Bild</s-table-header>
                    <s-table-header listSlot="labeled">SKU</s-table-header>
                    <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                    <s-table-header listSlot="labeled">Bestellte Menge</s-table-header>
                    {applyPreviousBookings && (
                      <s-table-header listSlot="labeled">Bereits verbucht</s-table-header>
                    )}
                    <s-table-header listSlot="labeled">Angenommene Menge</s-table-header>
                  </s-table-header-row>
                  <s-table-body>
                    {receivingItems.map((item, idx) => {
                      const bookedQuantity = alreadyBookedBySku[item.sku] || 0;
                      const fullyBooked: boolean =
                        applyPreviousBookings && bookedQuantity >= item.quantity;
                      return (
                        <s-table-row key={idx}>
                          <s-table-cell>
                            {item.imageUrl && <s-thumbnail src={item.imageUrl} alt={item.title} />}
                          </s-table-cell>
                          <s-table-cell>{item.sku}</s-table-cell>
                          <s-table-cell>{item.title}</s-table-cell>
                          <s-table-cell>{item.quantity}</s-table-cell>
                          {applyPreviousBookings && (
                            <s-table-cell>
                              {fullyBooked ? (
                                <s-badge tone="success">
                                  bereits verbucht ({bookedQuantity})
                                </s-badge>
                              ) : (
                                <s-text color="subdued">{bookedQuantity}</s-text>
                              )}
                            </s-table-cell>
                          )}
                          <s-table-cell>
                            <s-number-field
                              label=""
                              labelAccessibilityVisibility="exclusive"
                              value={String(item.receivedQuantity)}
                              onInput={(e: any) => {
                                const val = parseInt(e.currentTarget.value) || 0;
                                setReceivingItems(
                                  receivingItems.map((ri, i) =>
                                    i === idx ? { ...ri, receivedQuantity: val } : ri,
                                  ),
                                );
                              }}
                            />
                          </s-table-cell>
                        </s-table-row>
                      );
                    })}
                  </s-table-body>
                </s-table>
              </s-section>

              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button disabled={(processing) || undefined} onClick={() => setView(receivingReturnView)}>
                  Zurück
                </s-button>
                <s-button
                  variant="primary"
                  disabled={(processing) || undefined}
                  onClick={checkAndProcessReceiving}
                >
                  {processing ? 'Wird verbucht...' : 'Wareneingang buchen'}
                </s-button>
              </s-stack>
            </>
          )}
        </s-section>
      </s-page>
    );
  }

  if (view === 'completed-orders') {
    const finishedOrders = purchaseOrders.filter((o) => o.status === 'processing');

    return (
      <s-page heading="Bestellungen mit Verbuchungsproblemen">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}
          <s-text color="subdued">
            Bestellungen mit dem Status "Unvollständig / in Verarbeitung" wurden nicht (vollständig)
            verbucht. Über "Prüfen / erneut buchen" können sie nachgebucht werden; bereits verbuchte
            Mengen werden dabei aus der Bestellhistorie berücksichtigt. Mit "Löschen" wird eine
            Bestellung aus dieser Liste entfernt.
          </s-text>
          {finishedOrders.length === 0 && (
            <s-text>Keine Bestellungen mit Verbuchungsproblemen vorhanden</s-text>
          )}
          {receivingPreparing && (
            <s-stack direction="block" gap="small">
              <s-text color="subdued">Artikeldaten werden geladen...</s-text>
              <s-spinner />
            </s-stack>
          )}
        </s-section>

        {finishedOrders.length > 0 && (
          <s-section padding="none">
            <s-table>
              <s-table-header-row>
                <s-table-header listSlot="primary">Bestelldatum</s-table-header>
                <s-table-header listSlot="labeled">Artikel</s-table-header>
                <s-table-header listSlot="labeled">Status</s-table-header>
                <s-table-header listSlot="labeled">Verbucht am</s-table-header>
                <s-table-header listSlot="labeled">Aktion</s-table-header>
              </s-table-header-row>
              <s-table-body>
                {finishedOrders.map((finishedOrder) => (
                  <s-table-row key={finishedOrder.id}>
                    <s-table-cell>{formatDateDisplay(finishedOrder.date)}</s-table-cell>
                    <s-table-cell>{finishedOrder.items.length}</s-table-cell>
                    <s-table-cell>
                      {finishedOrder.status === 'processing' ? (
                        <s-badge tone="caution">Unvollständig / in Verarbeitung</s-badge>
                      ) : (
                        <s-badge tone="success">Verbucht</s-badge>
                      )}
                    </s-table-cell>
                    <s-table-cell>
                      {finishedOrder.verbuchtAm
                        ? formatDateTimeDisplay(finishedOrder.verbuchtAm)
                        : ''}
                    </s-table-cell>
                    <s-table-cell>
                      <s-stack direction="inline" gap="small">
                        <s-button
                          disabled={(receivingPreparing) || undefined}
                          onClick={() => startReceiving(finishedOrder.id, 'completed-orders')}
                        >
                          Prüfen / erneut buchen
                        </s-button>
                        {confirmDeleteOrderId !== finishedOrder.id ? (
                          <s-button
                            tone="critical"
                            onClick={() => setConfirmDeleteOrderId(finishedOrder.id)}
                          >
                            Löschen
                          </s-button>
                        ) : (
                          <>
                            <s-button
                              tone="critical"
                              onClick={() =>
                                runExclusive('delete', async () => {
                                  await deleteOrder(finishedOrder.id);
                                  setConfirmDeleteOrderId(null);
                                })
                              }
                              {...busyProps('delete')}
                            >
                              Wirklich löschen?
                            </s-button>
                            <s-button onClick={() => setConfirmDeleteOrderId(null)}>
                              Abbrechen
                            </s-button>
                          </>
                        )}
                      </s-stack>
                    </s-table-cell>
                  </s-table-row>
                ))}
              </s-table-body>
            </s-table>
          </s-section>
        )}

        <s-stack direction="inline" gap="base" justifyContent="end">
          <s-button onClick={() => setView('menu')}>Zurück</s-button>
        </s-stack>
      </s-page>
    );
  }

  if (view === 'view-orders') {
    const openOrders = purchaseOrders.filter((o) => o.status === 'open');

    return (
      <s-page heading="Bestellungen anzeigen/drucken">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}

          {printLink && (
            <s-banner tone="info">
              <s-stack direction="block" gap="small">
                <s-text>Der Browser hat das neue Fenster blockiert. Druckansicht hier öffnen:</s-text>
                <s-link href={printLink} target="_blank">
                  Druckansicht öffnen
                </s-link>
              </s-stack>
            </s-banner>
          )}

          {openOrders.length === 0 && <s-text>Keine offenen Bestellungen vorhanden</s-text>}

          {openOrders.map((order) => {
            const isExpanded = expandedOrders.includes(order.id);
            const editingOrder = editingOrders[order.id] || order;
            const newSku = newItemInputs[order.id]?.sku || '';
            const newQty = newItemInputs[order.id]?.qty || '';

            const setNewSku = (value: string): void => {
              setNewItemInputs({
                ...newItemInputs,
                [order.id]: { ...newItemInputs[order.id], sku: value, qty: newQty },
              });
            };

            const setNewQty = (value: string): void => {
              setNewItemInputs({
                ...newItemInputs,
                [order.id]: { ...newItemInputs[order.id], sku: newSku, qty: value },
              });
            };

            return (
              <s-box
                key={order.id}
                padding="base"
                borderWidth="base"
                borderColor="base"
                borderRadius="base"
              >
                <s-clickable onClick={() => toggleOrderExpansion(order.id)}>
                  <s-stack
                    direction="inline"
                    gap="base"
                    justifyContent="space-between"
                    alignItems="center"
                  >
                    <s-text type="strong">
                      {isExpanded ? '▼' : '▶'} Bestellung vom {formatDateDisplay(order.date)} -{' '}
                      {order.items.length} Artikel
                    </s-text>
                    <s-stack direction="inline" gap="base">
                      <s-button
                        onClick={(e: any) => {
                          e.stopPropagation();
                          setView('menu');
                        }}
                      >
                        Zurück
                      </s-button>
                      <s-button
                        onClick={(e: any) => {
                          e.stopPropagation();
                          handlePrintOrder(order);
                        }}
                        {...busyProps('print')}
                      >
                        Drucken
                      </s-button>
                      <s-button
                        onClick={(e: any) => {
                          e.stopPropagation();
                          handlePrintOrderWithoutGlazures(order);
                        }}
                        {...busyProps('print')}
                      >
                        Drucken ohne Glasuren
                      </s-button>
                      {confirmDeleteOrderId !== order.id ? (
                        <s-button
                          tone="critical"
                          onClick={(e: any) => {
                            e.stopPropagation();
                            setConfirmDeleteOrderId(order.id);
                          }}
                        >
                          Löschen
                        </s-button>
                      ) : (
                        <>
                          <s-button
                            tone="critical"
                            onClick={(e: any) => {
                              e.stopPropagation();
                              deleteOrder(order.id);
                              setConfirmDeleteOrderId(null);
                            }}
                          >
                            Wirklich löschen?
                          </s-button>
                          <s-button
                            onClick={(e: any) => {
                              e.stopPropagation();
                              setConfirmDeleteOrderId(null);
                            }}
                          >
                            Abbrechen
                          </s-button>
                        </>
                      )}
                    </s-stack>
                  </s-stack>
                </s-clickable>

                {isExpanded && (
                  <s-stack gap="base" direction="block">
                    <s-stack direction="inline" gap="base" alignItems="center">
                      <s-text-field
                        label="Sendungsnummer"
                        value={editingOrder.trackingNumber || ''}
                        onInput={(e: any) => {
                          const updated = {
                            ...editingOrder,
                            trackingNumber: e.currentTarget.value,
                          };
                          setEditingOrders({ ...editingOrders, [order.id]: updated });
                        }}
                      />
                      <s-link
                        href="https://elogistics.dachser.com/shp2s/ShpApplicationPage?3"
                        target="_blank"
                      >
                        Dachser Tracking öffnen
                      </s-link>
                    </s-stack>

                    <s-section padding="none">
                      <s-table>
                        <s-table-header-row>
                          <s-table-header listSlot="primary">Bild</s-table-header>
                          <s-table-header listSlot="labeled">SKU</s-table-header>
                          <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                          <s-table-header listSlot="labeled">Bestellte Menge</s-table-header>
                          <s-table-header listSlot="labeled">Neue Menge</s-table-header>
                        </s-table-header-row>
                        <s-table-body>
                          {editingOrder.items.map((item: OrderItem, idx: number) => (
                            <s-table-row key={idx}>
                              <s-table-cell>
                                {item.imageUrl && (
                                  <s-thumbnail src={item.imageUrl} alt={item.title} />
                                )}
                              </s-table-cell>
                              <s-table-cell>{item.sku}</s-table-cell>
                              <s-table-cell>{item.title}</s-table-cell>
                              <s-table-cell>{item.quantity}</s-table-cell>
                              <s-table-cell>
                                <s-number-field
                                  label=""
                                  labelAccessibilityVisibility="exclusive"
                                  value={String(item.quantity)}
                                  onInput={(e: any) => {
                                    const val = parseInt(e.currentTarget.value) || 0;
                                    const updatedItems = [...editingOrder.items];
                                    updatedItems[idx] = { ...updatedItems[idx], quantity: val };
                                    setEditingOrders({
                                      ...editingOrders,
                                      [order.id]: { ...editingOrder, items: updatedItems },
                                    });
                                  }}
                                />
                              </s-table-cell>
                            </s-table-row>
                          ))}
                        </s-table-body>
                      </s-table>
                    </s-section>

                    <s-stack direction="inline" gap="small" alignItems="center">
                      <s-text>Artikel ergänzen:</s-text>
                      <s-text-field
                        label="SKU"
                        labelAccessibilityVisibility="exclusive"
                        value={newSku}
                        onInput={(e: any) => setNewSku(e.currentTarget.value)}
                      />
                      <s-number-field
                        label="Menge"
                        labelAccessibilityVisibility="exclusive"
                        value={newQty}
                        onInput={(e: any) => setNewQty(e.currentTarget.value)}
                      />
                      <s-button
                        onClick={() => {
                          const qty = parseInt(newQty) || 0;
                          addItemToOrder(order.id, newSku, qty);
                          setNewSku('');
                          setNewQty('');
                        }}
                      >
                        Hinzufügen
                      </s-button>
                    </s-stack>
                  </s-stack>
                )}
              </s-box>
            );
          })}

          <s-stack direction="inline" gap="base" justifyContent="end">
            <s-button onClick={() => setView('menu')}>Zurück</s-button>
            <s-button variant="primary" onClick={() => runExclusive('saveChanges', saveOrderChanges)} {...busyProps('saveChanges')}>
              Änderungen speichern
            </s-button>
          </s-stack>
        </s-section>
      </s-page>
    );
  }

  if (view === 'export') {
    return (
      <s-page heading="Export Inventar">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {loading && <s-spinner />}
          {!loading && exportData && (
            <>
              <s-text>Export bereit zum Download</s-text>
              <s-link href={exportData} download="inventar-export.csv">
                CSV herunterladen
              </s-link>
            </>
          )}
          <s-button onClick={() => setView('menu')}>Zurück</s-button>
        </s-section>
      </s-page>
    );
  }

  if (view === 'inventory') {
    const filteredData = inventoryData
      .filter((item) => {
        if (showNoLocation) {
          if (item.lagerstandort && item.lagerstandort !== '') return false;
        } else if (selectedLocation && item.lagerstandort !== selectedLocation) {
          return false;
        }
        if (hideZeroStock && item.stock === 0) return false;
        return true;
      })
      .sort((a, b) => a.sku.localeCompare(b.sku));

    return (
      <s-page heading="Lagerbestand">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}
          {printLink && (
            <s-banner tone="info">
              <s-stack direction="block" gap="small">
                <s-text>Der Browser hat das neue Fenster blockiert. Druckansicht hier öffnen:</s-text>
                <s-link href={printLink} target="_blank">
                  Druckansicht öffnen
                </s-link>
              </s-stack>
            </s-banner>
          )}
          {loading && <s-spinner />}

          {!loading && (
            <>
              <s-stack direction="inline" gap="base" justifyContent="end">
                {inventoryLoaded && !inventoryEditMode && (
                  <s-button
                    tone="critical"
                    onClick={() => {
                      setInventoryEditMode(true);
                      const initialEdits: {
                        [sku: string]: { newStock?: number; newLagerstandort?: string };
                      } = {};
                      inventoryData.forEach((item) => {
                        if (!item.sku) return;
                        initialEdits[item.sku] = { newStock: item.stock };
                      });
                      setInventoryEdits(initialEdits);
                      setInventoryFreeTextRows({});
                    }}
                  >
                    Bearbeiten
                  </s-button>
                )}
                {inventoryEditMode && (
                  <>
                    <s-button
                      onClick={() => {
                        setInventoryEditMode(false);
                        setInventoryEdits({});
                        setInventoryFreeTextRows({});
                      }}
                      disabled={(inventorySaving) || undefined}
                    >
                      Abbrechen
                    </s-button>
                    <s-button
                      variant="primary"
                      tone="critical"
                      onClick={handleSaveInventoryEdits}
                      disabled={(inventorySaving) || undefined}
                    >
                      {inventorySaving ? 'Speichere...' : 'Speichern'}
                    </s-button>
                  </>
                )}
                <s-button onClick={() => setView('menu')} disabled={(inventorySaving) || undefined}>
                  Zurück
                </s-button>
              </s-stack>

              {inventoryEditMode && locations.length > 0 && (
                <s-text color="subdued">Vorhandene Standorte: {locations.join(', ')}</s-text>
              )}

              <s-stack direction="inline" gap="base">
                {locations.map((loc) => (
                  <s-button
                    key={loc}
                    variant={selectedLocation === loc ? 'primary' : 'secondary'}
                    onClick={() => {
                      setSelectedLocation(selectedLocation === loc ? '' : loc);
                      setShowNoLocation(false);
                      if (!inventoryLoaded) {
                        loadInventory().then(() => setInventoryLoaded(true));
                      }
                    }}
                  >
                    {loc}
                  </s-button>
                ))}
                <s-button
                  variant={showNoLocation ? 'primary' : 'secondary'}
                  onClick={() => {
                    setShowNoLocation(!showNoLocation);
                    setSelectedLocation('');
                    if (!inventoryLoaded) {
                      loadInventory().then(() => setInventoryLoaded(true));
                    }
                  }}
                >
                  Ohne Standort
                </s-button>
                <s-button
                  variant={
                    !selectedLocation && !showNoLocation && inventoryLoaded
                      ? 'primary'
                      : 'secondary'
                  }
                  onClick={() => {
                    setSelectedLocation('');
                    setShowNoLocation(false);
                    if (!inventoryLoaded) {
                      loadInventory().then(() => setInventoryLoaded(true));
                    }
                  }}
                >
                  Alle anzeigen
                </s-button>
              </s-stack>

              {!inventoryLoaded && !loading && (
                <s-text color="subdued">
                  Bitte einen Standort auswählen oder "Alle anzeigen" klicken, um die Artikel zu
                  laden.
                </s-text>
              )}

              {inventoryLoaded && (
                <>
                  <s-switch
                    label="Nullbestände ausblenden"
                    checked={(hideZeroStock) || undefined}
                    ref={listen('change', (e: any) => setHideZeroStock(e.currentTarget.checked))}
                  />

                  <s-section padding="none">
                    <s-table>
                      <s-table-header-row>
                        <s-table-header listSlot="primary">SKU</s-table-header>
                        <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                        <s-table-header listSlot="labeled">Lagerstandort</s-table-header>
                        <s-table-header listSlot="labeled">Verfügbar</s-table-header>
                        <s-table-header listSlot="labeled">VE</s-table-header>
                        {inventoryEditMode && (
                          <s-table-header listSlot="labeled">Neuer Bestand</s-table-header>
                        )}
                        {inventoryEditMode && (
                          <s-table-header listSlot="labeled">Neuer Lagerstandort</s-table-header>
                        )}
                      </s-table-header-row>
                      <s-table-body>
                        {filteredData.map((item, idx) => {
                          const edit = inventoryEdits[item.sku];
                          const newStockValue =
                            edit?.newStock !== undefined
                              ? String(edit.newStock)
                              : String(item.stock);
                          const newLagerstandortValue =
                            edit?.newLagerstandort !== undefined
                              ? edit.newLagerstandort
                              : item.lagerstandort || '';
                          const isFreeText = inventoryFreeTextRows[item.sku] || false;
                          const updateLagerstandort = (val: string): void => {
                            setInventoryEdits((prev) => {
                              const next = { ...prev };
                              const existing = next[item.sku] || {};
                              if (val === (item.lagerstandort || '')) {
                                const { newLagerstandort, ...rest } = existing;
                                if (Object.keys(rest).length === 0) {
                                  delete next[item.sku];
                                } else {
                                  next[item.sku] = rest;
                                }
                              } else {
                                next[item.sku] = {
                                  ...existing,
                                  newLagerstandort: val,
                                };
                              }
                              return next;
                            });
                          };
                          return (
                            <s-table-row key={idx}>
                              <s-table-cell>{item.sku}</s-table-cell>
                              <s-table-cell>{item.title}</s-table-cell>
                              <s-table-cell>{item.lagerstandort}</s-table-cell>
                              <s-table-cell>{item.stock}</s-table-cell>
                              <s-table-cell>{item.ve}</s-table-cell>
                              {inventoryEditMode && item.sku === '' && (
                                <>
                                  <s-table-cell>–</s-table-cell>
                                  <s-table-cell>keine SKU – nicht bearbeitbar</s-table-cell>
                                </>
                              )}
                              {inventoryEditMode && item.sku !== '' && (
                                <s-table-cell>
                                  <s-number-field
                                    label="Neuer Bestand"
                                    labelAccessibilityVisibility="exclusive"
                                    value={newStockValue}
                                    onInput={(e: any) => {
                                      const raw = e.currentTarget.value;
                                      setInventoryEdits((prev) => {
                                        const next = { ...prev };
                                        const existing = next[item.sku] || {};
                                        if (raw === '' || raw === null) {
                                          const { newStock, ...rest } = existing;
                                          if (Object.keys(rest).length === 0) {
                                            delete next[item.sku];
                                          } else {
                                            next[item.sku] = rest;
                                          }
                                        } else {
                                          const parsed = parseInt(raw);
                                          if (!isNaN(parsed)) {
                                            next[item.sku] = { ...existing, newStock: parsed };
                                          }
                                        }
                                        return next;
                                      });
                                    }}
                                  />
                                </s-table-cell>
                              )}
                              {inventoryEditMode && item.sku !== '' && (
                                <s-table-cell>
                                  <s-stack direction="block" gap="small">
                                    {!isFreeText ? (
                                      <s-select
                                        label="Neuer Lagerstandort"
                                        labelAccessibilityVisibility="exclusive"
                                        value={newLagerstandortValue}
                                        ref={listen('change', (e: any) => {
                                          updateLagerstandort(e.currentTarget.value);
                                        })}
                                      >
                                        <s-option value="">— kein Standort —</s-option>
                                        {locations.map((loc) => (
                                          <s-option key={loc} value={loc}>
                                            {loc}
                                          </s-option>
                                        ))}
                                      </s-select>
                                    ) : (
                                      <s-text-field
                                        label="Neuer Lagerstandort"
                                        labelAccessibilityVisibility="exclusive"
                                        value={newLagerstandortValue}
                                        onInput={(e: any) => {
                                          updateLagerstandort(e.currentTarget.value);
                                        }}
                                      />
                                    )}
                                    <s-checkbox
                                      label="Freie Eingabe"
                                      checked={(isFreeText) || undefined}
                                      ref={listen('change', (e: any) => {
                                        // Wert sofort lesen: im verzögert laufenden State-Update wäre currentTarget bereits null.
                                        const checked = !!e.currentTarget?.checked;
                                        setInventoryFreeTextRows((prev) => ({
                                          ...prev,
                                          [item.sku]: checked,
                                        }));
                                      })}
                                    />
                                  </s-stack>
                                </s-table-cell>
                              )}
                            </s-table-row>
                          );
                        })}
                      </s-table-body>
                    </s-table>
                  </s-section>

                  <s-stack direction="inline" gap="base" justifyContent="end">
                    <s-button
                      onClick={() => {
                        const heading = selectedLocation
                          ? `Lagerbestand – ${selectedLocation}`
                          : 'Lagerbestand';
                        void runPrint(async () => buildInventoryPrintHtml(heading, [...filteredData]));
                      }}
                      disabled={(inventoryEditMode) || undefined}
                    >
                      Drucken
                    </s-button>
                  </s-stack>
                </>
              )}

              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button
                  onClick={() => {
                    setInventoryLoaded(false);
                    setView('menu');
                  }}
                  disabled={(inventorySaving) || undefined}
                >
                  Zurück
                </s-button>
              </s-stack>
            </>
          )}
        </s-section>
      </s-page>
    );
  }

  if (view === 'price-update') {
    if (priceUpdateStep === 1) {
      return (
        <s-page heading="Einkaufspreise aktualisieren">
          <s-section>
            {error && <s-banner tone="critical">{error}</s-banner>}
            <s-text>
              CSV-Datei der Keramix-Preisliste hochladen (Semikolon-getrennt, Spalte A = SKU, Spalte
              D = Preis):
            </s-text>
            <s-drop-zone
              label={priceUpdateFile ? priceUpdateFile.name : 'CSV-Datei auswählen (.csv)'}
              accept=".csv"
              ref={listen('change', (e: any) => {
                const files = e.currentTarget?.files;
                if (files && files.length > 0) setPriceUpdateFile(files[0]);
              })}
            />
            <s-stack direction="inline" gap="base">
              <s-button onClick={() => setView('menu')}>Zurück</s-button>
              <s-button
                variant="primary"
                disabled={(priceUpdateLoading || !priceUpdateFile) || undefined}
                onClick={handleAnalyzePrices}
              >
                {priceUpdateLoading ? 'Analysiere...' : 'Analysieren'}
              </s-button>
            </s-stack>
            {priceUpdateLoading && <s-spinner />}
          </s-section>
        </s-page>
      );
    }

    const unchangedCount = priceUpdateResults.filter((r) => r.status === 'unchanged').length;
    const changedCount = priceUpdateResults.filter((r) => r.status === 'changed').length;
    const errorCount = priceUpdateResults.filter(
      (r) => r.status === 'error' || r.status === 'not_in_excel',
    ).length;

    return (
      <s-page heading="Einkaufspreise aktualisieren">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {priceUpdateSuccess && <s-banner tone="success">{priceUpdateSuccess}</s-banner>}

          <s-stack direction="inline" gap="base" justifyContent="space-between">
            <s-text>
              {unchangedCount} unverändert, {changedCount} abweichend, {errorCount} nicht in
              Preisliste/Fehler
            </s-text>
            <s-button
              onClick={() =>
                setPriceUpdateSelected(
                  priceUpdateResults.filter((r) => r.status === 'changed').map((r) => r.sku),
                )
              }
            >
              Alle abweichenden auswählen
            </s-button>
          </s-stack>

          <s-section padding="none">
            <s-table>
              <s-table-header-row>
                <s-table-header listSlot="primary">SKU</s-table-header>
                <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                <s-table-header listSlot="labeled">Preis aktuell</s-table-header>
                <s-table-header listSlot="labeled">Preis neu</s-table-header>
                <s-table-header listSlot="labeled">Differenz</s-table-header>
                <s-table-header listSlot="labeled">Auswählen</s-table-header>
              </s-table-header-row>
              <s-table-body>
                {priceUpdateResults.map((result, idx) => {
                  const currentNum = parseFloat(result.currentPrice) || 0;
                  const newNum = parseFloat(result.newPrice) || 0;
                  const diff = result.status === 'changed' ? (newNum - currentNum).toFixed(2) : '';
                  const statusBadge =
                    result.status === 'unchanged'
                      ? 'success'
                      : result.status === 'changed'
                        ? 'caution'
                        : 'critical';
                  return (
                    <s-table-row key={idx}>
                      <s-table-cell>
                        <s-badge tone={statusBadge}>{result.sku}</s-badge>
                      </s-table-cell>
                      <s-table-cell>{result.title}</s-table-cell>
                      <s-table-cell>{result.currentPrice}</s-table-cell>
                      <s-table-cell>{result.newPrice}</s-table-cell>
                      <s-table-cell>{diff}</s-table-cell>
                      <s-table-cell>
                        {result.status === 'changed' && (
                          <s-checkbox
                            label={result.sku}
                            accessibilityLabel={`${result.sku} auswählen`}
                            checked={(priceUpdateSelected.includes(result.sku)) || undefined}
                            ref={listen('change', (e: any) => {
                              if (e.currentTarget.checked) {
                                setPriceUpdateSelected([...priceUpdateSelected, result.sku]);
                              } else {
                                setPriceUpdateSelected(
                                  priceUpdateSelected.filter((s) => s !== result.sku),
                                );
                              }
                            })}
                          />
                        )}
                      </s-table-cell>
                    </s-table-row>
                  );
                })}
              </s-table-body>
            </s-table>
          </s-section>

          <s-stack direction="inline" gap="base">
            <s-button onClick={() => setPriceUpdateStep(1)}>Zurück zur Analyse</s-button>
            <s-button onClick={() => setView('menu')}>Zurück zum Menü</s-button>
            <s-button
              variant="primary"
              disabled={(priceUpdateLoading || priceUpdateSelected.length === 0) || undefined}
              onClick={handleUpdatePrices}
            >
              {priceUpdateLoading
                ? 'Aktualisiere...'
                : `Ausgewählte Preise aktualisieren (${priceUpdateSelected.length})`}
            </s-button>
          </s-stack>
          {priceUpdateLoading && <s-spinner />}
        </s-section>
      </s-page>
    );
  }

  if (view === 'search-orders') {
    const totalQuantity = searchResults.reduce((sum, r) => sum + r.quantity, 0);

    return (
      <s-page heading="Bestellte Artikel abfragen">
        <s-section>
          <s-text-field
            label="SKU suchen"
            value={searchQuery}
            onInput={(e: any) => {
              setSearchQuery(e.currentTarget.value);
              setSearchExecuted(false);
            }}
          />
          <s-button onClick={handleSearchOrders}>Suchen</s-button>

          {searchResults.length > 0 && (
            <>
              <s-section padding="none">
                <s-table>
                  <s-table-header-row>
                    <s-table-header listSlot="primary">Bestelldatum</s-table-header>
                    <s-table-header listSlot="labeled">SKU</s-table-header>
                    <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                    <s-table-header listSlot="labeled">Menge</s-table-header>
                  </s-table-header-row>
                  <s-table-body>
                    {searchResults.map((result, idx) => (
                      <s-table-row key={idx}>
                        <s-table-cell>{formatDateDisplay(result.orderDate)}</s-table-cell>
                        <s-table-cell>{result.sku}</s-table-cell>
                        <s-table-cell>{result.title}</s-table-cell>
                        <s-table-cell>{result.quantity}</s-table-cell>
                      </s-table-row>
                    ))}
                  </s-table-body>
                </s-table>
              </s-section>
              <s-text>
                Total bestellt: {totalQuantity} Stück in {searchOrderCount} Bestellungen
              </s-text>
            </>
          )}

          {searchExecuted && searchResults.length === 0 && (
            <s-text>SKU "{searchQuery}" ist in keiner offenen Bestellung vorhanden.</s-text>
          )}

          <s-button onClick={() => setView('menu')}>Zurück</s-button>
        </s-section>
      </s-page>
    );
  }

  if (view === 'migration') {
    return (
      <s-page heading="Datenmigration in die neue Datenbank">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {success && <s-banner tone="success">{success}</s-banner>}

          <s-text color="subdued">
            Einmalig: übernimmt Bestellungen und Historie aus den bisherigen Metafeldern in die
            Datenbank auf dem Novatrend-Server. Die Metafelder werden nicht verändert oder gelöscht.
            Der Probelauf ist jederzeit möglich und schreibt nichts. Der echte Import läuft nur einmal und
            nur in eine leere Datenbank (danach ist er gesperrt).
          </s-text>

          <s-stack direction="block" gap="base">
            <s-button disabled={(migrationBusy) || undefined} onClick={checkBackend}>
              0. Verbindung zum Backend prüfen
            </s-button>
            <s-button disabled={(migrationBusy) || undefined} onClick={readMigrationSources}>
              1. Metafelder lesen und Roh-Backup erstellen
            </s-button>
            <s-button
              disabled={(migrationBusy || !migrationSources) || undefined}
              onClick={() => runMigration(true)}
            >
              2. Probelauf (schreibt nichts)
            </s-button>
            <s-button
              variant="primary"
              disabled={(migrationBusy || !migrationSources) || undefined}
              onClick={() => runMigration(false)}
            >
              3. Import ausführen
            </s-button>
          </s-stack>

          {migrationBusy && <s-spinner />}

          {migrationBackupUrl && (
            <s-banner tone="info">
              <s-stack direction="block" gap="small">
                <s-text>
                  Roh-Backup der Metafelder bereit. Datei herunterladen und aufbewahren
                  (Rechtsklick → Link speichern unter, falls der Klick nichts tut).
                </s-text>
                <s-link href={migrationBackupUrl} download="metafeld-backup.json">
                  Roh-Backup herunterladen
                </s-link>
              </s-stack>
            </s-banner>
          )}

          {migrationLog.length > 0 && (
            <s-stack direction="block" gap="none">
              {migrationLog.map((line, idx) => (
                <s-text key={idx}>{line}</s-text>
              ))}
            </s-stack>
          )}

          {migrationReport && (
            <s-banner tone={migrationReport.dryRun ? 'info' : 'success'}>
              <s-stack direction="block" gap="small">
                <s-text type="strong">
                  {migrationReport.dryRun ? 'Probelauf (nichts geschrieben)' : 'Import ausgeführt'}
                </s-text>
                <s-text>
                  Bestellungen: {migrationReport.orders.received} gelesen,{' '}
                  {migrationReport.orders.toInsert} {migrationReport.dryRun ? 'würden übernommen' : 'übernommen'},{' '}
                  {migrationReport.orders.skippedExisting} bereits vorhanden,{' '}
                  {migrationReport.orders.invalid} ungültig
                </s-text>
                <s-text>
                  Historie: {migrationReport.history.received} gelesen,{' '}
                  {migrationReport.history.toInsert} {migrationReport.dryRun ? 'würden übernommen' : 'übernommen'},{' '}
                  {migrationReport.history.duplicatesSkipped} Duplikate übersprungen,{' '}
                  {migrationReport.history.invalid} ungültig
                </s-text>
                <s-text>
                  In der Datenbank insgesamt: {migrationReport.totals.orders} Bestellungen,{' '}
                  {migrationReport.totals.history} Historieneinträge
                </s-text>
                {migrationReport.invalidExamples.length > 0 && (
                  <s-text color="subdued">
                    Ungültige Datensätze (Beispiele): {migrationReport.invalidExamples.join(' | ')}
                  </s-text>
                )}
              </s-stack>
            </s-banner>
          )}

          <s-button onClick={() => setView('menu')}>Zurück</s-button>
        </s-section>
      </s-page>
    );
  }

  if (view === 'history') {
    const currentYear = new Date().getFullYear();
    const years = historyYears.length > 0 ? historyYears : [String(currentYear)];

    const filteredHistory = historyData.filter((h) => h.datum.split('.')[2] === historyYear);

    const csvLines = [
      'Datum;Uhrzeit;Ereignis;Bestelldatum;SKU;Bezeichnung;Bestellt;Verbucht;Details',
      ...filteredHistory.map(
        (h) =>
          `${h.datum};${h.uhrzeit};${h.ereignis};${h.bestelldatum};${h.sku};${h.bezeichnung};${h.mengeBestellt};${h.mengeVerbucht};${h.details}`,
      ),
    ];
    const csvContent = csvLines.join('\n');
    const csvDataUrl = `data:text/csv;charset=utf-8,${encodeURIComponent(csvContent)}`;

    return (
      <s-page heading="Bestellhistorie">
        <s-section>
          {error && <s-banner tone="critical">{error}</s-banner>}
          {historyLoading && <s-spinner />}

          {!historyLoading && (
            <>
              <s-stack direction="inline" gap="base" justifyContent="end">
                <s-button onClick={() => setView('menu')}>Zurück</s-button>
              </s-stack>
              <s-stack direction="inline" gap="base">
                {years.map((year) => (
                  <s-button
                    key={year}
                    variant={historyYear === year ? 'primary' : 'secondary'}
                    onClick={async () => {
                      setHistoryYear(year);
                      setHistoryLoading(true);
                      try {
                        setHistoryData(await backend.getHistory(year));
                      } catch (yearError: any) {
                        setError(
                          `Historie konnte nicht geladen werden: ${yearError?.message || 'Unbekannter Fehler'}`,
                        );
                      }
                      setHistoryLoading(false);
                    }}
                  >
                    {year}
                  </s-button>
                ))}
              </s-stack>

              {filteredHistory.length > 0 && (
                <>
                  <s-section padding="none">
                    <s-table>
                      <s-table-header-row>
                        <s-table-header listSlot="primary">Datum</s-table-header>
                        <s-table-header listSlot="labeled">Uhrzeit</s-table-header>
                        <s-table-header listSlot="labeled">Ereignis</s-table-header>
                        <s-table-header listSlot="labeled">Bestelldatum</s-table-header>
                        <s-table-header listSlot="labeled">SKU</s-table-header>
                        <s-table-header listSlot="labeled">Bezeichnung</s-table-header>
                        <s-table-header listSlot="labeled">Bestellt</s-table-header>
                        <s-table-header listSlot="labeled">Verbucht</s-table-header>
                        <s-table-header listSlot="labeled">Details</s-table-header>
                      </s-table-header-row>
                      <s-table-body>
                        {filteredHistory.map((entry, idx) => (
                          <s-table-row key={idx}>
                            <s-table-cell>{entry.datum}</s-table-cell>
                            <s-table-cell>{entry.uhrzeit}</s-table-cell>
                            <s-table-cell>{entry.ereignis}</s-table-cell>
                            <s-table-cell>{entry.bestelldatum}</s-table-cell>
                            <s-table-cell>{entry.sku}</s-table-cell>
                            <s-table-cell>{entry.bezeichnung}</s-table-cell>
                            <s-table-cell>{entry.mengeBestellt}</s-table-cell>
                            <s-table-cell>{entry.mengeVerbucht}</s-table-cell>
                            <s-table-cell>{entry.details}</s-table-cell>
                          </s-table-row>
                        ))}
                      </s-table-body>
                    </s-table>
                  </s-section>
                  <s-link href={csvDataUrl} download={`bestellhistorie-${historyYear}.csv`}>
                    CSV herunterladen
                  </s-link>
                </>
              )}

              {filteredHistory.length === 0 && (
                <s-text>Keine Einträge für {historyYear} vorhanden</s-text>
              )}

              <s-button onClick={() => setView('menu')}>Zurück</s-button>
            </>
          )}
        </s-section>
      </s-page>
    );
  }

  return null;
}


export function ErrorBoundary() {
  return boundary.error(useRouteError());
}
export const headers = (headersArgs: any) => boundary.headers(headersArgs);
