"use client";

import {
  Box,
  Button,
  Stack,
  TextField,
  Typography,
  Alert,
  CircularProgress,
  Autocomplete,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  Checkbox,
  TablePagination,
  Modal,
} from "@mui/material";
import TestQrCodeProvider from "../QrCodeScannerProvider/TestQrCodeProvider";
import {
  useCallback,
  useEffect,
  useState,
  useRef,
  useMemo,
  startTransition,
} from "react";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
import {
  updateStockOutLineStatus,
  createStockOutLine,
  //applyPickExecutionHoldAndChecked,
  fetchFGPickOrdersByUserIdWorkbench,
  FGPickOrderResponse,
  autoAssignAndReleasePickOrder,
  AutoAssignReleaseResponse,
  checkPickOrderCompletion,
  PickOrderCompletionResponse,
  confirmLotSubstitution,
  updateStockOutLineStatusByQRCodeAndLotNo, // ✅ 添加
} from "@/app/api/pickOrder/actions";
//  修改：使用 Job Order API
import {
  fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench,
  updateJoPickOrderHandledBy,
  JobOrderLotsHierarchicalWorkbenchResponse,
  applyPickExecutionHoldAndChecked,
  PrintPickRecord,
} from "@/app/api/jo/actions";
import { assignJobOrderPickOrderForWorkbench } from "@/app/api/jo/workbenchActions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import { FormProvider, useForm } from "react-hook-form";
import SearchBox, { Criterion } from "../SearchBox";
import { CreateStockOutLine } from "@/app/api/pickOrder/actions";
import {
  updateInventoryLotLineQuantities,
  analyzeQrCode,
  fetchLotDetail,
} from "@/app/api/inventory/actions";
import QrCodeIcon from "@mui/icons-material/QrCode";
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import GoodPickExecutionForm from "../Jodetail/JobPickExecutionForm";
import FGPickOrderCard from "../Jodetail/FGPickOrderCard";
import WorkbenchLotLabelPrintModal from "@/components/DoWorkbench/WorkbenchLotLabelPrintModal";
import LinearProgressWithLabel from "../common/LinearProgressWithLabel";
import ScanStatusAlert from "../common/ScanStatusAlert";
import {
  workbenchBatchScanPick,
  workbenchScanPick,
} from "@/app/api/doworkbench/actions";
import type { PrinterCombo } from "@/app/api/settings/printer";
import { msg, msgError } from "@/components/Swal/CustomAlerts";
import {
  buildPrintPickRecordRequest,
  promptAllFloorsPlasticBoxCartonQty,
  promptPlasticBoxCartonQty,
  type PickRecordFloor,
} from "@/components/Jodetail/pickRecordHelpers";
interface Props {
  filterArgs: Record<string, any>;
  //onSwitchToRecordTab: () => void;
  onBackToList?: () => void;
  printerCombo?: PrinterCombo[];
}

/** 過期批號：與 noLot 類似——單筆/批量預設 0，除非 Issue 改數（對齊 GoodPickExecutiondetail） */
function isLotAvailabilityExpired(lot: any): boolean {
  return String(lot?.lotAvailability || "").toLowerCase() === "expired";
}

/** inventory_lot_line.status = unavailable（API 可能用 lotAvailability 或 lotStatus） */
function isInventoryLotLineUnavailable(lot: any): boolean {
  if (!lot) return false;
  if (lot.lotAvailability === "status_unavailable") return true;
  return String(lot.lotStatus || "").toLowerCase() === "unavailable";
}

/** 同 DO Workbench：多行時優先替換有建議批號的行 */
function pickExpectedLotForSubstitution(
  activeSuggestedLots: any[],
): any | null {
  if (!activeSuggestedLots?.length) return null;
  const withLotNo = activeSuggestedLots.filter(
    (l) => l.lotNo != null && String(l.lotNo).trim() !== "",
  );
  if (withLotNo.length === 1) return withLotNo[0];
  if (withLotNo.length > 1) {
    const pending = withLotNo.find(
      (l) => String(l.stockOutLineStatus || "").toLowerCase() === "pending",
    );
    return pending || withLotNo[0];
  }
  return activeSuggestedLots[0];
}

type PickOrderT = (key: string, options?: Record<string, unknown>) => string;

/** 與 DO Workbench：優先顯示 scan-pick 暫存拒絕訊息 */
function buildLotRejectDisplayMessage(
  lot: any,
  scanRejectBySolId: Record<number, string>,
  t: PickOrderT,
): string | undefined {
  const solId = Number(lot.stockOutLineId) || 0;
  const fromScan = solId > 0 ? scanRejectBySolId[solId]?.trim() : "";
  if (fromScan) return fromScan;

  const fromApi =
    typeof lot.stockOutLineRejectMessage === "string"
      ? lot.stockOutLineRejectMessage.trim()
      : "";
  if (fromApi) return fromApi;

  const st = String(lot.stockOutLineStatus || "").toLowerCase();
  const av = String(lot.lotAvailability || "").toLowerCase();
  const isRejected = st === "rejected" || av === "rejected";
  if (!isRejected) return undefined;

  if (isLotAvailabilityExpired(lot) || av === "expired") {
    return t("Rejected: lot expired or no longer valid.");
  }
  if (av === "insufficient_stock") {
    return t("Rejected: no remaining quantity / empty lot.");
  }
  if (isInventoryLotLineUnavailable(lot) || av === "status_unavailable") {
    return t("Rejected: lot unavailable or not yet putaway.");
  }
  return t("Pick was rejected. Please scan another lot or check stock.");
}

const JO_ISSUE_PICKED_KEY = (pickOrderId: number) =>
  `fpsms-jo-issuePickedQty:${pickOrderId}`;

function loadIssuePickedMapJo(pickOrderId: number): Record<number, number> {
  if (typeof window === "undefined" || !pickOrderId) return {};
  try {
    const raw = sessionStorage.getItem(JO_ISSUE_PICKED_KEY(pickOrderId));
    if (!raw) return {};
    const parsed = JSON.parse(raw) as Record<string, number>;
    const out: Record<number, number> = {};
    Object.entries(parsed).forEach(([k, v]) => {
      const n = Number(v);
      if (!Number.isNaN(n)) out[Number(k)] = n;
    });
    return out;
  } catch {
    return {};
  }
}

function saveIssuePickedMapJo(
  pickOrderId: number,
  map: Record<number, number>,
) {
  if (typeof window === "undefined" || !pickOrderId) return;
  try {
    sessionStorage.setItem(
      JO_ISSUE_PICKED_KEY(pickOrderId),
      JSON.stringify(map),
    );
  } catch {
    // ignore quota / private mode
  }
}

// Manual Lot Confirmation Modal (align with GoodPickExecutiondetail, opened by {2fic})
const ManualLotConfirmationModal: React.FC<{
  open: boolean;
  onClose: () => void;
  onConfirm: (expectedLotNo: string, scannedLotNo: string) => void;
  expectedLot: { lotNo: string; itemCode: string; itemName: string } | null;
  scannedLot: { lotNo: string; itemCode: string; itemName: string } | null;
  isLoading?: boolean;
}> = ({
  open,
  onClose,
  onConfirm,
  expectedLot,
  scannedLot,
  isLoading = false,
}) => {
  const { t } = useTranslation("jo");
  const [expectedLotInput, setExpectedLotInput] = useState<string>("");
  const [scannedLotInput, setScannedLotInput] = useState<string>("");
  const [error, setError] = useState<string>("");

  useEffect(() => {
    if (open) {
      setExpectedLotInput(expectedLot?.lotNo || "");
      setScannedLotInput(scannedLot?.lotNo || "");
      setError("");
    }
  }, [open, expectedLot, scannedLot]);

  const handleConfirm = () => {
    if (!expectedLotInput.trim() || !scannedLotInput.trim()) {
      setError(t("Please enter both expected and scanned lot numbers."));
      return;
    }
    if (expectedLotInput.trim() === scannedLotInput.trim()) {
      setError(t("Expected and scanned lot numbers cannot be the same."));
      return;
    }
    onConfirm(expectedLotInput.trim(), scannedLotInput.trim());
  };

  return (
    <Modal open={open} onClose={onClose}>
      <Box
        sx={{
          position: "absolute",
          top: "50%",
          left: "50%",
          transform: "translate(-50%, -50%)",
          bgcolor: "background.paper",
          p: 3,
          borderRadius: 2,
          minWidth: 500,
        }}
      >
        <Typography variant="h6" gutterBottom color="warning.main">
          {t("Manual Lot Confirmation")}
        </Typography>

        <Box sx={{ mb: 2 }}>
          <Typography variant="body2" gutterBottom>
            <strong>{t("Expected Lot Number")}:</strong>
          </Typography>
          <TextField
            fullWidth
            size="small"
            value={expectedLotInput}
            onChange={(e) => {
              setExpectedLotInput(e.target.value);
              setError("");
            }}
            sx={{ mb: 2 }}
            error={!!error && !expectedLotInput.trim()}
          />
        </Box>

        <Box sx={{ mb: 2 }}>
          <Typography variant="body2" gutterBottom>
            <strong>{t("Scanned Lot Number")}:</strong>
          </Typography>
          <TextField
            fullWidth
            size="small"
            value={scannedLotInput}
            onChange={(e) => {
              setScannedLotInput(e.target.value);
              setError("");
            }}
            sx={{ mb: 2 }}
            error={!!error && !scannedLotInput.trim()}
          />
        </Box>

        {error && (
          <Box
            sx={{ mb: 2, p: 1, backgroundColor: "#ffebee", borderRadius: 1 }}
          >
            <Typography variant="body2" color="error">
              {error}
            </Typography>
          </Box>
        )}

        <Box
          sx={{ mt: 2, display: "flex", justifyContent: "flex-end", gap: 2 }}
        >
          <Button onClick={onClose} variant="outlined" disabled={isLoading}>
            {t("Cancel")}
          </Button>
          <Button
            onClick={handleConfirm}
            variant="contained"
            color="warning"
            disabled={
              isLoading || !expectedLotInput.trim() || !scannedLotInput.trim()
            }
          >
            {isLoading ? t("Processing...") : t("Confirm")}
          </Button>
        </Box>
      </Box>
    </Modal>
  );
};

//  QR Code Modal Component (from GoodPickExecution)
const QrCodeModal: React.FC<{
  open: boolean;
  onClose: () => void;
  lot: any | null;
  onQrCodeSubmit: (lotNo: string) => void;
  combinedLotData: any[];
}> = ({ open, onClose, lot, onQrCodeSubmit, combinedLotData }) => {
  const { t } = useTranslation("jo");
  const {
    values: qrValues,
    isScanning,
    startScan,
    stopScan,
    resetScan,
  } = useQrCodeScannerContext();
  const [manualInput, setManualInput] = useState<string>("");

  const [selectedFloor, setSelectedFloor] = useState<string | null>(null);
  const [manualInputSubmitted, setManualInputSubmitted] =
    useState<boolean>(false);
  const [manualInputError, setManualInputError] = useState<boolean>(false);
  const [isProcessingQr, setIsProcessingQr] = useState<boolean>(false);
  const [qrScanFailed, setQrScanFailed] = useState<boolean>(false);
  const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);

  const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(
    new Set(),
  );
  const [scannedQrResult, setScannedQrResult] = useState<string>("");

  // Process scanned QR codes
  useEffect(() => {
    if (qrValues.length > 0 && lot && !isProcessingQr && !qrScanSuccess) {
      const latestQr = qrValues[qrValues.length - 1];

      if (processedQrCodes.has(latestQr)) {
        console.log("QR code already processed, skipping...");
        return;
      }

      setProcessedQrCodes((prev) => new Set(prev).add(latestQr));

      try {
        const qrData = JSON.parse(latestQr);

        if (qrData.stockInLineId && qrData.itemId) {
          setIsProcessingQr(true);
          setQrScanFailed(false);

          fetchStockInLineInfo(qrData.stockInLineId)
            .then((stockInLineInfo) => {
              console.log("Stock in line info:", stockInLineInfo);
              setScannedQrResult(stockInLineInfo.lotNo || "Unknown lot number");

              if (stockInLineInfo.lotNo === lot.lotNo) {
                console.log(` QR Code verified for lot: ${lot.lotNo}`);
                setQrScanSuccess(true);
                onQrCodeSubmit(lot.lotNo);
                onClose();
                resetScan();
              } else {
                console.log(
                  `❌ QR Code mismatch. Expected: ${lot.lotNo}, Got: ${stockInLineInfo.lotNo}`,
                );
                setQrScanFailed(true);
                setManualInputError(true);
                setManualInputSubmitted(true);
              }
            })
            .catch((error) => {
              console.error("Error fetching stock in line info:", error);
              setScannedQrResult("Error fetching data");
              setQrScanFailed(true);
              setManualInputError(true);
              setManualInputSubmitted(true);
            })
            .finally(() => {
              setIsProcessingQr(false);
            });
        } else {
          const qrContent = latestQr.replace(/[{}]/g, "");
          setScannedQrResult(qrContent);

          if (qrContent === lot.lotNo) {
            setQrScanSuccess(true);
            onQrCodeSubmit(lot.lotNo);
            onClose();
            resetScan();
          } else {
            setQrScanFailed(true);
            setManualInputError(true);
            setManualInputSubmitted(true);
          }
        }
      } catch (error) {
        console.log("QR code is not JSON format, trying direct comparison");
        const qrContent = latestQr.replace(/[{}]/g, "");
        setScannedQrResult(qrContent);

        if (qrContent === lot.lotNo) {
          setQrScanSuccess(true);
          onQrCodeSubmit(lot.lotNo);
          onClose();
          resetScan();
        } else {
          setQrScanFailed(true);
          setManualInputError(true);
          setManualInputSubmitted(true);
        }
      }
    }
  }, [
    qrValues,
    lot,
    onQrCodeSubmit,
    onClose,
    resetScan,
    isProcessingQr,
    qrScanSuccess,
    processedQrCodes,
  ]);

  // Clear states when modal opens
  useEffect(() => {
    if (open) {
      setManualInput("");
      setManualInputSubmitted(false);
      setManualInputError(false);
      setIsProcessingQr(false);
      setQrScanFailed(false);
      setQrScanSuccess(false);
      setScannedQrResult("");
      setProcessedQrCodes(new Set());
    }
  }, [open]);

  useEffect(() => {
    if (lot) {
      setManualInput("");
      setManualInputSubmitted(false);
      setManualInputError(false);
      setIsProcessingQr(false);
      setQrScanFailed(false);
      setQrScanSuccess(false);
      setScannedQrResult("");
      setProcessedQrCodes(new Set());
    }
  }, [lot]);

  // Auto-submit manual input when it matches
  useEffect(() => {
    if (
      manualInput.trim() === lot?.lotNo &&
      manualInput.trim() !== "" &&
      !qrScanFailed &&
      !qrScanSuccess
    ) {
      console.log(" Auto-submitting manual input:", manualInput.trim());

      const timer = setTimeout(() => {
        setQrScanSuccess(true);
        onQrCodeSubmit(lot.lotNo);
        onClose();
        setManualInput("");
        setManualInputError(false);
        setManualInputSubmitted(false);
      }, 200);

      return () => clearTimeout(timer);
    }
  }, [manualInput, lot, onQrCodeSubmit, onClose, qrScanFailed, qrScanSuccess]);

  const handleManualSubmit = () => {
    if (manualInput.trim() === lot?.lotNo) {
      setQrScanSuccess(true);
      onQrCodeSubmit(lot.lotNo);
      onClose();
      setManualInput("");
    } else {
      setQrScanFailed(true);
      setManualInputError(true);
      setManualInputSubmitted(true);
    }
  };

  useEffect(() => {
    if (open) {
      startScan();
    }
  }, [open, startScan]);

  return (
    <Modal open={open} onClose={onClose}>
      <Box
        sx={{
          position: "absolute",
          top: "50%",
          left: "50%",
          transform: "translate(-50%, -50%)",
          bgcolor: "background.paper",
          p: 3,
          borderRadius: 2,
          minWidth: 400,
        }}
      >
        <Typography variant="h6" gutterBottom>
          {t("QR Code Scan for Lot")}: {lot?.lotNo}
        </Typography>

        {isProcessingQr && (
          <Box
            sx={{ mb: 2, p: 2, backgroundColor: "#e3f2fd", borderRadius: 1 }}
          >
            <Typography variant="body2" color="primary">
              {t("Processing QR code...")}
            </Typography>
          </Box>
        )}

        <Box sx={{ mb: 2 }}>
          <Typography variant="body2" gutterBottom>
            <strong>{t("Manual Input")}:</strong>
          </Typography>
          <TextField
            fullWidth
            size="small"
            value={manualInput}
            onChange={(e) => {
              setManualInput(e.target.value);
              if (qrScanFailed || manualInputError) {
                setQrScanFailed(false);
                setManualInputError(false);
                setManualInputSubmitted(false);
              }
            }}
            sx={{ mb: 1 }}
            error={manualInputSubmitted && manualInputError}
            helperText={
              manualInputSubmitted && manualInputError
                ? `${t(
                    "The input is not the same as the expected lot number.",
                  )}`
                : ""
            }
          />

          <Button
            variant="contained"
            onClick={handleManualSubmit}
            disabled={!manualInput.trim() || lot?.noLot === true || !lot?.lotId}
            size="small"
            color="primary"
          >
            {t("Submit")}
          </Button>
        </Box>

        {qrValues.length > 0 && (
          <Box
            sx={{
              mb: 2,
              p: 2,
              backgroundColor: qrScanFailed
                ? "#ffebee"
                : qrScanSuccess
                  ? "#e8f5e8"
                  : "#f5f5f5",
              borderRadius: 1,
            }}
          >
            <Typography
              variant="body2"
              color={
                qrScanFailed
                  ? "error"
                  : qrScanSuccess
                    ? "success"
                    : "text.secondary"
              }
            >
              <strong>{t("QR Scan Result:")}</strong> {scannedQrResult}
            </Typography>

            {qrScanSuccess && (
              <Typography variant="caption" color="success" display="block">
                {t("Verified successfully!")}
              </Typography>
            )}
          </Box>
        )}

        <Box sx={{ mt: 2, textAlign: "right" }}>
          <Button onClick={onClose} variant="outlined">
            {t("Cancel")}
          </Button>
        </Box>
      </Box>
    </Modal>
  );
};

const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList, printerCombo = [] }) => {
  const workbenchMode = true;
  const { t } = useTranslation("jo");
  const { t: tPick } = useTranslation("pickOrder");
  const router = useRouter();
  const { data: session } = useSession() as { data: SessionWithTokens | null };

  const currentUserId = session?.id ? parseInt(session.id) : undefined;

  //  修改：使用 Job Order 数据结构

  const [combinedDataLoading, setCombinedDataLoading] = useState(false);

  //  添加未分配订单状态
  const [unassignedOrders, setUnassignedOrders] = useState<any[]>([]);
  const [isLoadingUnassigned, setIsLoadingUnassigned] = useState(false);

  const {
    values: qrValues,
    isScanning,
    startScan,
    stopScan,
    resetScan,
  } = useQrCodeScannerContext();
  const [expectedLotData, setExpectedLotData] = useState<any>(null);
  const [scannedLotData, setScannedLotData] = useState<any>(null);
  const [isConfirmingLot, setIsConfirmingLot] = useState(false);
  const [qrScanInput, setQrScanInput] = useState<string>("");
  const [qrScanError, setQrScanError] = useState<boolean>(false);
  const [qrScanErrorMsg, setQrScanErrorMsg] = useState<string>("");
  const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
  const [jobOrderData, setJobOrderData] =
    useState<JobOrderLotsHierarchicalWorkbenchResponse | null>(null);
  const a4Printers = useMemo(
    () =>
      (printerCombo || []).filter((p) =>
        String(p.type || "")
          .trim()
          .toUpperCase()
          .includes("A4"),
      ),
    [printerCombo],
  );
  const printerOptions = useMemo(
    () => (a4Printers.length > 0 ? a4Printers : printerCombo || []),
    [a4Printers, printerCombo],
  );
  const isPrinterComboMissing = printerCombo.length === 0;
  const [selectedPrinter, setSelectedPrinter] = useState<PrinterCombo | null>(
    printerOptions.length > 0 ? printerOptions[0] : null,
  );
  const [printQty, setPrintQty] = useState<number>(1);
  const pickRecordPrintInFlightRef = useRef(false);

  useEffect(() => {
    // Keep selected printer valid when combo list changes.
    if (!printerOptions.length) {
      setSelectedPrinter(null);
      return;
    }
    setSelectedPrinter((prev) => {
      if (!prev) return printerOptions[0];
      const stillExists = printerOptions.some((p) => p.id === prev.id);
      return stillExists ? prev : printerOptions[0];
    });
  }, [printerOptions]);

  useEffect(() => {
    console.log("[JO Workbench] printerCombo:", printerCombo);
    console.log("[JO Workbench] a4Printers:", a4Printers);
    console.log("[JO Workbench] printerOptions:", printerOptions);
  }, [printerCombo, a4Printers, printerOptions]);

  const handlePickRecord = useCallback(
    async (floor: PickRecordFloor) => {
      if (pickRecordPrintInFlightRef.current) return;
      try {
        const pickOrderId = jobOrderData?.pickOrder?.id;
        if (!pickOrderId) {
          msgError(t("Pick Order not found"));
          return;
        }
        if (!selectedPrinter) {
          msgError(t("Please select a printer first"));
          return;
        }
        if (!printQty || printQty < 1) {
          msgError(t("Please enter a valid print quantity (at least 1)"));
          return;
        }

        let printRequest;
        if (floor === "ALL") {
          const allFloorsQty = await promptAllFloorsPlasticBoxCartonQty(t, pickOrderId);
          if (allFloorsQty === null) {
            return;
          }
          printRequest = buildPrintPickRecordRequest({
            pickOrderId,
            printerId: selectedPrinter.id,
            printQty,
            floor,
            allFloorsQty,
          });
        } else {
          const plasticBoxCartonQty = await promptPlasticBoxCartonQty(t);
          if (plasticBoxCartonQty === null) {
            return;
          }
          printRequest = buildPrintPickRecordRequest({
            pickOrderId,
            printerId: selectedPrinter.id,
            printQty,
            floor,
            plasticBoxCartonQty,
          });
        }

        pickRecordPrintInFlightRef.current = true;

        const response = await PrintPickRecord(printRequest);

        if (response?.success) {
          msg(t("Printed Successfully."));
        } else {
          msgError(response?.message || t("Print failed"));
        }
      } catch (e) {
        console.error(e);
        msgError(t("An error occurred while printing"));
      } finally {
        pickRecordPrintInFlightRef.current = false;
      }
    },
    [jobOrderData, printQty, selectedPrinter, t],
  );
  const workbenchStoreId = useMemo(() => {
    const po = jobOrderData?.pickOrder as
      | { storeId?: string | null }
      | undefined;
    const s = po?.storeId;
    return typeof s === "string" && s.trim() !== "" ? s.trim() : null;
  }, [jobOrderData]);
  const [pickQtyData, setPickQtyData] = useState<Record<string, number>>({});
  /** 與 DO Workbench 一致：false = 數量只讀；Edit 切換為可輸入「將提交數量」，不開 Issue 表單 */
  const [
    workbenchSubmitQtyFieldEnabledByLotKey,
    setWorkbenchSubmitQtyFieldEnabledByLotKey,
  ] = useState<Record<string, boolean>>({});
  const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
  // issue form 里填的 actualPickQty（用于 submit/batch submit 不补拣到 required）
  const [issuePickedQtyBySolId, setIssuePickedQtyBySolId] = useState<
    Record<number, number>
  >({});
  const [localSolStatusById, setLocalSolStatusById] = useState<
    Record<number, string>
  >({});
  // 防止同一行（以 stockOutLineId/solId 识别）被重复点击提交/完成
  const [actionBusyBySolId, setActionBusyBySolId] = useState<
    Record<number, boolean>
  >({});
  /** DO Workbench：scan-pick 失敗訊息按 SOL 顯示 */
  const [scanRejectMessageBySolId, setScanRejectMessageBySolId] = useState<
    Record<number, string>
  >({});
  const rememberWorkbenchScanReject = useCallback(
    (stockOutLineId: number, message: string | undefined | null) => {
      const id = Number(stockOutLineId);
      const m = String(message ?? "").trim();
      if (!id || !m) return;
      setScanRejectMessageBySolId((prev) => ({ ...prev, [id]: m }));
    },
    [],
  );
  const clearWorkbenchScanReject = useCallback((stockOutLineId: number) => {
    const id = Number(stockOutLineId);
    if (!id) return;
    setScanRejectMessageBySolId((prev) => {
      if (!(id in prev)) return prev;
      const next = { ...prev };
      delete next[id];
      return next;
    });
  }, []);

  const [paginationController, setPaginationController] = useState({
    pageNum: 0,
    pageSize: 10,
  });

  const [usernameList, setUsernameList] = useState<NameList[]>([]);

  const initializationRef = useRef(false);
  const scannerInitializedRef = useRef(false);
  const autoAssignRef = useRef(false);

  const formProps = useForm();
  const errors = formProps.formState.errors;
  const [isSubmittingAll, setIsSubmittingAll] = useState<boolean>(false);
  const [autoAssignStatus, setAutoAssignStatus] = useState<
    "idle" | "checking" | "assigned" | "no_orders"
  >("idle");
  const [completionStatus, setCompletionStatus] =
    useState<PickOrderCompletionResponse | null>(null);
  const [autoAssignMessage, setAutoAssignMessage] = useState<string>("");

  //  Add QR modal states
  const [qrModalOpen, setQrModalOpen] = useState(false);
  const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
  const [selectedFloor, setSelectedFloor] = useState<string | null>(null);
  //  Add GoodPickExecutionForm states
  const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
  const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] =
    useState<any | null>(null);
  const [fgPickOrders, setFgPickOrders] = useState<FGPickOrderResponse[]>([]);
  const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false);
  const [workbenchLotLabelModalOpen, setWorkbenchLotLabelModalOpen] =
    useState(false);
  const [workbenchLotLabelInitialPayload, setWorkbenchLotLabelInitialPayload] =
    useState<{ itemId: number; stockInLineId: number } | null>(null);
  const [workbenchLotLabelContextLot, setWorkbenchLotLabelContextLot] =
    useState<any | null>(null);
  const [workbenchLotLabelReminderText, setWorkbenchLotLabelReminderText] =
    useState<string | null>(null);

  useEffect(() => {
    if (!qrScanSuccess || !workbenchLotLabelModalOpen) return;
    setWorkbenchLotLabelModalOpen(false);
    setWorkbenchLotLabelInitialPayload(null);
    setWorkbenchLotLabelContextLot(null);
    setWorkbenchLotLabelReminderText(null);
  }, [qrScanSuccess, workbenchLotLabelModalOpen]);

  //  Add these missing state variables
  const [isManualScanning, setIsManualScanning] = useState<boolean>(false);
  const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(
    new Set(),
  );
  const [lastProcessedQr, setLastProcessedQr] = useState<string>("");
  const [isRefreshingData, setIsRefreshingData] = useState<boolean>(false);
  // Track processed QR codes by itemId+stockInLineId combination for better lot confirmation handling
  const [processedQrCombinations, setProcessedQrCombinations] = useState<
    Map<number, Set<number>>
  >(new Map());

  // Cache for fetchStockInLineInfo API calls to avoid redundant requests
  const stockInLineInfoCache = useRef<
    Map<number, { lotNo: string | null; timestamp: number }>
  >(new Map());
  const CACHE_TTL = 60000; // 60 seconds cache TTL
  const abortControllerRef = useRef<AbortController | null>(null);
  const qrProcessingTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  // Use refs for processed QR tracking to avoid useEffect dependency issues and delays
  const processedQrCodesRef = useRef<Set<string>>(new Set());
  const lastProcessedQrRef = useRef<string>("");

  // Store callbacks in refs to avoid useEffect dependency issues
  const processOutsideQrCodeRef = useRef<
    ((latestQr: string, qrScanCountAtInvoke?: number) => Promise<void>) | null
  >(null);
  const resetScanRef = useRef<(() => void) | null>(null);

  // Manual lot confirmation modal state (test shortcut {2fic})
  const [manualLotConfirmationOpen, setManualLotConfirmationOpen] =
    useState(false);
  const getAllLotsFromHierarchical = useCallback(
    (data: JobOrderLotsHierarchicalWorkbenchResponse | null): any[] => {
      if (!data || !data.pickOrder || !data.pickOrderLines) {
        return [];
      }

      const allLots: any[] = [];

      data.pickOrderLines.forEach((line) => {
        // 用来记录这一行已经通过 lots 出现过的 lotId（避免 stockouts 再渲染一次）
        const lotIdSet = new Set<number>();
        /** 已由有批次建議分配的量（加總後與 pick_order_line.requiredQty 的差額 = 無批次列應顯示的數），對齊 DO Workbench */
        let lotsAllocatedSumForLine = 0;

        // lots：按 lotId 去重并合并 requiredQty（对齐 GoodPickExecutiondetail）
        if (line.lots && line.lots.length > 0) {
          const lotMap = new Map<number, any>();

          line.lots.forEach((lot: any) => {
            const lotId = lot.lotId;
            if (lotId == null) return;
            if (lotMap.has(lotId)) {
              const existingLot = lotMap.get(lotId);
              existingLot.requiredQty =
                (existingLot.requiredQty || 0) + (lot.requiredQty || 0);
            } else {
              lotMap.set(lotId, { ...lot });
            }
          });

          lotMap.forEach((lot: any) => {
            lotsAllocatedSumForLine += Number(lot.requiredQty) || 0;
            if (lot.lotId != null) lotIdSet.add(lot.lotId);

            allLots.push({
              ...lot,
              pickOrderLineId: line.id,
              itemId: line.itemId,
              itemCode: line.itemCode,
              itemName: line.itemName,
              uomCode: line.uomCode,
              uomDesc: line.uomDesc,
              itemTotalAvailableQty: line.totalAvailableQty ?? null,
              pickOrderLineRequiredQty: line.requiredQty,
              pickOrderLineStatus: line.status,
              jobOrderId: data.pickOrder.jobOrder.id,
              jobOrderCode: data.pickOrder.jobOrder.code,
              pickOrderId: data.pickOrder.id,
              pickOrderCode: data.pickOrder.code,
              pickOrderConsoCode: data.pickOrder.consoCode,
              pickOrderTargetDate: data.pickOrder.targetDate,
              pickOrderType: data.pickOrder.type,
              pickOrderStatus: data.pickOrder.status,
              pickOrderAssignTo: data.pickOrder.assignTo,
              handler: line.handler,
              noLot: false,
            });
          });
        }

        /** 工單 API 常在有揀貨後仍回傳 lots: []，缺口只在 stockouts；此時用非 noLot 的已揀量扣 POL（對齊實際剩餘） */
        const stockoutsPickedSumNonNoLot = (line.stockouts ?? []).reduce(
          (acc: number, s: any) => {
            if (!s || s.noLot) return acc;
            return acc + (Number(s.qty) || 0);
          },
          0,
        );
        const noLotRemainingBasis =
          lotsAllocatedSumForLine > 0
            ? lotsAllocatedSumForLine
            : stockoutsPickedSumNonNoLot;

        // stockouts：用于“无 suggested lot / noLot”场景也显示并可 submit 0 闭环
        if (line.stockouts && line.stockouts.length > 0) {
          line.stockouts.forEach((stockout: any) => {
            const hasLot = stockout.lotId != null;
            const lotAlreadyInLots =
              hasLot && lotIdSet.has(stockout.lotId as number);

            // 有批次 & 已经通过 lots 渲染过 → 跳过，避免一条变两行
            if (!stockout.noLot && lotAlreadyInLots) {
              return;
            }

            allLots.push({
              pickOrderLineId: line.id,
              itemId: line.itemId,
              itemCode: line.itemCode,
              itemName: line.itemName,
              uomCode: line.uomCode,
              uomDesc: line.uomDesc,
              itemTotalAvailableQty: line.totalAvailableQty ?? null,
              pickOrderLineRequiredQty: line.requiredQty,
              pickOrderLineStatus: line.status,
              jobOrderId: data.pickOrder.jobOrder.id,
              jobOrderCode: data.pickOrder.jobOrder.code,
              pickOrderId: data.pickOrder.id,
              pickOrderCode: data.pickOrder.code,
              pickOrderConsoCode: data.pickOrder.consoCode,
              pickOrderTargetDate: data.pickOrder.targetDate,
              pickOrderType: data.pickOrder.type,
              pickOrderStatus: data.pickOrder.status,
              pickOrderAssignTo: data.pickOrder.assignTo,
              handler: line.handler,

              lotId: stockout.lotId || null,
              lotNo: stockout.lotNo || null,
              expiryDate: null,
              location: stockout.location || null,
              availableQty: stockout.availableQty ?? 0,
              // 無批次列：有 SPL 時扣 suggested 合計；僅有 stockouts（lots 空）時扣已揀量（對齊 DO + workbench 僅 SOL 情境）
              requiredQty: stockout.noLot
                ? Math.max(
                    0,
                    (Number(line.requiredQty) || 0) - noLotRemainingBasis,
                  )
                : Number(line.requiredQty) || 0,
              actualPickQty: stockout.qty ?? 0,
              processingStatus: stockout.status || "pending",
              lotAvailability: stockout.noLot
                ? "insufficient_stock"
                : "available",
              suggestedPickLotId: null,
              stockOutLineId: stockout.id || null,
              stockOutLineQty: stockout.qty ?? 0,
              stockOutLineStatus: stockout.status || null,
              stockInLineId: null,
              routerIndex: stockout.noLot ? 999999 : null,
              routerArea: null,
              routerRoute: null,
              noLot: !!stockout.noLot,
            });
          });
        }
      });

      return allLots;
    },
    [],
  );
  const extractFloor = (lot: any): string => {
    const raw = lot.routerRoute || lot.routerArea || lot.location || "";
    const match = raw.match(/^(\d+F?)/i) || raw.split("-")[0];
    return (match?.[1] || match || raw || "")
      .toUpperCase()
      .replace(/(\d)F?/i, "$1F");
  };

  // 楼层排序权重：4F > 3F > 2F（数字越大越靠前）
  const floorSortOrder = (floor: string): number => {
    const n = parseInt(floor.replace(/\D/g, ""), 10);
    return isNaN(n) ? 0 : n;
  };
  const combinedLotData = useMemo(() => {
    const lots = getAllLotsFromHierarchical(jobOrderData);
    // 前端覆盖：issue form/submit0 不会立刻改写后端 qty 时，用本地缓存让 UI 与 batch submit 计算一致
    return lots.map((lot: any) => {
      const solId = Number(lot.stockOutLineId) || 0;
      if (solId > 0) {
        const hasPickedOverride = Object.prototype.hasOwnProperty.call(
          issuePickedQtyBySolId,
          solId,
        );
        const picked = Number(
          issuePickedQtyBySolId[solId] ?? lot.actualPickQty ?? 0,
        );
        const statusRaw =
          localSolStatusById[solId] ?? lot.stockOutLineStatus ?? "";
        const status = String(statusRaw).toLowerCase();
        const isEnded = status === "completed" || status === "rejected";
        return {
          ...lot,
          actualPickQty: hasPickedOverride ? picked : lot.actualPickQty,
          stockOutLineQty: hasPickedOverride ? picked : lot.stockOutLineQty,
          stockOutLineStatus: isEnded ? statusRaw : statusRaw || "checked",
        };
      }
      return lot;
    });
  }, [
    jobOrderData,
    getAllLotsFromHierarchical,
    issuePickedQtyBySolId,
    localSolStatusById,
  ]);

  /** 單筆將提交數量：Issue 改數 → pickQtyData → 已完成行用實際量 → noLot/過期/unavailable → 0 → 否則 required（對齊 DO Workbench） */
  const resolveSingleSubmitQty = useCallback(
    (lot: any) => {
      const required = Number(
        lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
      );
      const solId = Number(lot.stockOutLineId) || 0;
      const lotId = lot.lotId;
      const lotKey =
        Number.isFinite(solId) && solId > 0
          ? `sol:${solId}`
          : `${lot.pickOrderLineId}-${lotId}`;

      const issuePicked = solId > 0 ? issuePickedQtyBySolId[solId] : undefined;
      if (issuePicked !== undefined && !Number.isNaN(Number(issuePicked))) {
        return Number(issuePicked);
      }

      const st = String(lot.stockOutLineStatus || "").toLowerCase();
      if (
        st === "completed" ||
        st === "partially_completed" ||
        st === "partially_complete"
      ) {
        return Number(lot.stockOutLineQty ?? lot.actualPickQty ?? 0);
      }

      if (Object.prototype.hasOwnProperty.call(pickQtyData, lotKey)) {
        const fromPick = pickQtyData[lotKey];
        if (
          fromPick !== undefined &&
          fromPick !== null &&
          !Number.isNaN(Number(fromPick))
        ) {
          return Number(fromPick);
        }
      }
      if (isInventoryLotLineUnavailable(lot)) {
        return 0;
      }
      if (lot.noLot === true || !lot.lotId) {
        return 0;
      }
      if (isLotAvailabilityExpired(lot)) {
        return 0;
      }
      return required;
    },
    [issuePickedQtyBySolId, pickQtyData],
  );

  const getWorkbenchQtyLotKey = useCallback((lot: any) => {
    const solId = Number(lot?.stockOutLineId) || 0;
    if (Number.isFinite(solId) && solId > 0) return `sol:${solId}`;
    return `${lot?.pickOrderLineId}-${lot?.lotId}`;
  }, []);

  const workbenchScanPickQtyFromLot = useCallback(
    (lot: any) => {
      const solId = Number(lot?.stockOutLineId);
      const sourceLot =
        Number.isFinite(solId) && solId > 0
          ? combinedLotData.find((r) => Number(r.stockOutLineId) === solId) ??
            lot
          : lot;
      const lotKey = getWorkbenchQtyLotKey(sourceLot);
      const hasExplicitPickOverride = Object.prototype.hasOwnProperty.call(
        pickQtyData,
        lotKey,
      );
      const n = Number(resolveSingleSubmitQty(sourceLot));
      if (hasExplicitPickOverride && Number.isFinite(n) && n === 0)
        return { qty: 0 } as const;
      if (!Number.isFinite(n) || n <= 0) return {};
      return { qty: n } as const;
    },
    [resolveSingleSubmitQty, combinedLotData, pickQtyData, getWorkbenchQtyLotKey],
  );

  const lotFloorPrefixFilter = useMemo(() => {
    const storeId = String(workbenchStoreId ?? "")
      .trim()
      .toUpperCase()
      .replace(/\s/g, "");
    const floorKey = storeId.replace(/\//g, "");
    return floorKey ? `${floorKey}-` : "";
  }, [workbenchStoreId]);

  const defaultLabelPrinterName = useMemo(() => {
    const storeId = String(workbenchStoreId ?? "")
      .trim()
      .toUpperCase()
      .replace(/\s/g, "");
    const floorKey = storeId.replace(/\//g, "");
    if (floorKey === "2F") return "Label機 2F A+B";
    if (floorKey === "4F") return "Label機 4F 乾貨 C, D";
    return undefined;
  }, [workbenchStoreId]);

  const openWorkbenchLotLabelModalForLot = useCallback((lot: any, reminderText?: string | null) => {
    const itemId = Number(lot?.itemId);
    const stockInLineId = Number(lot?.stockInLineId);
    const solId = Number(lot?.stockOutLineId);
    if (!Number.isFinite(itemId) || itemId <= 0 || !Number.isFinite(solId) || solId <= 0) {
      return;
    }
    setWorkbenchLotLabelContextLot(lot);
    if (Number.isFinite(stockInLineId) && stockInLineId > 0) {
      setWorkbenchLotLabelInitialPayload({ itemId, stockInLineId });
    } else {
      setWorkbenchLotLabelInitialPayload(null);
    }
    setWorkbenchLotLabelReminderText(reminderText ?? null);
    // Clear latched success so the lot-label modal effect cannot instantly re-close on open.
    setQrScanSuccess(false);
    setWorkbenchLotLabelModalOpen(true);
  }, []);

  const shouldOpenWorkbenchLotLabelModalForFailure = useCallback(
    (code?: string | null, msg?: string | null): boolean => {
      const normalizedCode = String(code || "").toUpperCase();
      if (
        normalizedCode.includes("UNAVAILABLE") ||
        normalizedCode.includes("EXPIRED")
      ) {
        return true;
      }
      const normalizedMsg = String(msg || "").toLowerCase();
      if (!normalizedMsg) return false;
      return (
        normalizedMsg.includes("unavailable") ||
        normalizedMsg.includes("not available") ||
        normalizedMsg.includes("expired") ||
        normalizedMsg.includes("不可用") ||
        normalizedMsg.includes("無法使用") ||
        normalizedMsg.includes("过期")
      );
    },
    [],
  );

  const handleWorkbenchLotLabelScanPick = useCallback(
    async ({
      inventoryLotLineId,
      lotNo,
      qty,
    }: {
      inventoryLotLineId: number;
      lotNo: string;
      qty?: number;
    }) => {
      const lot = workbenchLotLabelContextLot;
      if (!lot?.stockOutLineId) {
        throw new Error(t("Missing stock out line for this row."));
      }
      const qtyPayload = Number.isFinite(Number(qty))
        ? { qty: Number(qty) }
        : workbenchScanPickQtyFromLot(lot);
      const res = await workbenchScanPick({
        stockOutLineId: Number(lot.stockOutLineId),
        lotNo: String(lotNo || "").trim(),
        inventoryLotLineId,
        storeId: workbenchStoreId,
        userId: currentUserId ?? 1,
        ...qtyPayload,
      });
      if (res.code !== "SUCCESS") {
        const errMsg =
          (res as { message?: string })?.message ||
          t("Workbench scan-pick failed.");
        rememberWorkbenchScanReject(Number(lot.stockOutLineId), errMsg);
        throw new Error(errMsg);
      }
      clearWorkbenchScanReject(Number(lot.stockOutLineId));
      const pickOrderId = filterArgs?.pickOrderId
        ? Number(filterArgs.pickOrderId)
        : undefined;
      if (pickOrderId) {
        const latest =
          await fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench(pickOrderId);
        setJobOrderData(latest);
        setIssuePickedQtyBySolId(loadIssuePickedMapJo(pickOrderId));
        getAllLotsFromHierarchical(latest);
      }
      setWorkbenchLotLabelModalOpen(false);
      setWorkbenchLotLabelContextLot(null);
      setWorkbenchLotLabelInitialPayload(null);
    },
    [
      workbenchLotLabelContextLot,
      currentUserId,
      workbenchScanPickQtyFromLot,
      workbenchStoreId,
      rememberWorkbenchScanReject,
      clearWorkbenchScanReject,
      filterArgs?.pickOrderId,
      getAllLotsFromHierarchical,
      t,
    ],
  );

  const workbenchLotLabelSubmitQty = useMemo(() => {
    if (!workbenchLotLabelContextLot) return 0;
    return Number(resolveSingleSubmitQty(workbenchLotLabelContextLot)) || 0;
  }, [workbenchLotLabelContextLot, resolveSingleSubmitQty]);

  const handleWorkbenchLotLabelSubmitQtyChange = useCallback(
    (qty: number) => {
      if (!workbenchLotLabelContextLot) return;
      const lotKey = getWorkbenchQtyLotKey(workbenchLotLabelContextLot);
      setPickQtyData((prev) => ({ ...prev, [lotKey]: qty }));
    },
    [workbenchLotLabelContextLot, getWorkbenchQtyLotKey],
  );

  const workbenchLotLabelScanPickDisabled = useMemo(() => {
    if (!workbenchLotLabelModalOpen || !workbenchLotLabelContextLot) return true;
    const lot = workbenchLotLabelContextLot;
    const status = String(lot.stockOutLineStatus || "").toLowerCase();
    const isNoLot = !lot.lotNo || String(lot.lotNo).trim() === "";
    if (isNoLot) return false;
    if (status === "pending" || status === "rejected") return false;
    return true;
  }, [workbenchLotLabelModalOpen, workbenchLotLabelContextLot]);

  const originalCombinedData = useMemo(() => {
    return getAllLotsFromHierarchical(jobOrderData);
  }, [jobOrderData, getAllLotsFromHierarchical]);

  // Enhanced lotDataIndexes with cached active lots for better performance (align with GoodPickExecutiondetail)
  const lotDataIndexes = useMemo(() => {
    const byItemId = new Map<number, any[]>();
    const byItemCode = new Map<string, any[]>();
    const byLotId = new Map<number, any>();
    const byLotNo = new Map<string, any[]>();
    const byStockInLineId = new Map<number, any[]>();
    const activeLotsByItemId = new Map<number, any[]>();
    const rejectedStatuses = new Set(["rejected"]);

    for (let i = 0; i < combinedLotData.length; i++) {
      const lot = combinedLotData[i];
      const solStatus = String(lot.stockOutLineStatus || "").toLowerCase();
      const lotAvailability = String(lot.lotAvailability || "").toLowerCase();
      const processingStatus = String(lot.processingStatus || "").toLowerCase();
      const isUnavailable = isInventoryLotLineUnavailable(lot);
      const isExpired = isLotAvailabilityExpired(lot);
      const isRejected =
        rejectedStatuses.has(lotAvailability) ||
        rejectedStatuses.has(solStatus) ||
        rejectedStatuses.has(processingStatus);
      const isEnded = solStatus === "checked" || solStatus === "completed";
      const isPartially =
        solStatus === "partially_completed" ||
        solStatus === "partially_complete";
      const isPending = solStatus === "pending" || solStatus === "";
      const isActive =
        !isRejected &&
        !isUnavailable &&
        !isExpired &&
        !isEnded &&
        (isPending || isPartially);

      if (lot.itemId) {
        if (!byItemId.has(lot.itemId)) {
          byItemId.set(lot.itemId, []);
          activeLotsByItemId.set(lot.itemId, []);
        }
        byItemId.get(lot.itemId)!.push(lot);
        if (isActive) activeLotsByItemId.get(lot.itemId)!.push(lot);
      }
      if (lot.itemCode) {
        if (!byItemCode.has(lot.itemCode)) byItemCode.set(lot.itemCode, []);
        byItemCode.get(lot.itemCode)!.push(lot);
      }
      if (lot.lotId) byLotId.set(lot.lotId, lot);
      if (lot.lotNo) {
        if (!byLotNo.has(lot.lotNo)) byLotNo.set(lot.lotNo, []);
        byLotNo.get(lot.lotNo)!.push(lot);
      }
      if (lot.stockInLineId) {
        if (!byStockInLineId.has(lot.stockInLineId))
          byStockInLineId.set(lot.stockInLineId, []);
        byStockInLineId.get(lot.stockInLineId)!.push(lot);
      }
    }

    return {
      byItemId,
      byItemCode,
      byLotId,
      byLotNo,
      byStockInLineId,
      activeLotsByItemId,
    };
  }, [combinedLotData]);

  // Cached version of fetchStockInLineInfo to avoid redundant API calls
  const fetchStockInLineInfoCached = useCallback(
    async (stockInLineId: number): Promise<{ lotNo: string | null }> => {
      const now = Date.now();
      const cached = stockInLineInfoCache.current.get(stockInLineId);
      if (cached && now - cached.timestamp < CACHE_TTL) {
        return { lotNo: cached.lotNo };
      }

      if (abortControllerRef.current) abortControllerRef.current.abort();
      const abortController = new AbortController();
      abortControllerRef.current = abortController;

      const stockInLineInfo = await fetchStockInLineInfo(stockInLineId);
      stockInLineInfoCache.current.set(stockInLineId, {
        lotNo: stockInLineInfo.lotNo || null,
        timestamp: now,
      });
      if (stockInLineInfoCache.current.size > 100) {
        const firstKey = stockInLineInfoCache.current.keys().next().value;
        if (firstKey !== undefined)
          stockInLineInfoCache.current.delete(firstKey);
      }
      return { lotNo: stockInLineInfo.lotNo || null };
    },
    [],
  );
  //  修改：加载未分配的 Job Order 订单
  const loadUnassignedOrders = useCallback(async () => {
    setIsLoadingUnassigned(true);
    try {
      //const orders = await fetchUnassignedJobOrderPickOrders();
      //setUnassignedOrders(orders);
    } catch (error) {
      console.error("Error loading unassigned orders:", error);
    } finally {
      setIsLoadingUnassigned(false);
    }
  }, []);

  //  修改：分配订单给当前用户
  const handleAssignOrder = useCallback(
    async (pickOrderId: number) => {
      if (!currentUserId) {
        console.error("Missing user id in session");
        return;
      }

      try {
        const result = await assignJobOrderPickOrderForWorkbench(
          pickOrderId,
          currentUserId,
        );
        const msg = String(result?.message || "");
        const assignSkippedByStatus =
          msg.toLowerCase().startsWith("skipped assign:");
        if (msg === "Successfully assigned" || assignSkippedByStatus) {
          console.log(" Successfully assigned pick order");
          if (!assignSkippedByStatus) {
            try {
              /*
              const refreshed =
                await fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench(
                  pickOrderId,
                );
                
              const uniqueItemIds = Array.from(
                new Set(
                  (refreshed?.pickOrderLines ?? [])
                    .map((line) => line?.itemId)
                    .filter(
                      (itemId): itemId is number =>
                        typeof itemId === "number" &&
                        Number.isFinite(itemId) &&
                        itemId > 0,
                    ),
                ),
              );
              
              await Promise.all(
                uniqueItemIds.map((itemId) =>
                  updateJoPickOrderHandledBy({
                    pickOrderId,
                    itemId,
                    userId: currentUserId,
                  }),
                ),
              );
              */
            } catch (handledBySyncError) {
              console.warn(
                "⚠️ Assigned but failed to sync JoPickOrder.handledBy for some lines:",
                handledBySyncError,
              );
            }
          }
          // 刷新数据
          window.dispatchEvent(new CustomEvent("pickOrderAssigned"));
          // 重新加载未分配订单列表
          loadUnassignedOrders();
        } else {
          console.warn("⚠️ Assignment failed:", result.message);
          alert(`Assignment failed: ${result.message}`);
        }
      } catch (error) {
        console.error("❌ Error assigning order:", error);
        alert("Error occurred during assignment");
      }
    },
    [currentUserId, loadUnassignedOrders],
  );

  const fetchFgPickOrdersData = useCallback(async () => {
    if (!currentUserId) return;

    setFgPickOrdersLoading(true);
    try {
      const allFgPickOrders = await fetchFGPickOrdersByUserIdWorkbench(
        currentUserId,
      );
      setFgPickOrders(Array.isArray(allFgPickOrders) ? allFgPickOrders : []);
      console.log(" Fetched FG pick orders(workbench):", allFgPickOrders);
    } catch (error) {
      console.error("❌ Error fetching FG pick orders:", error);
      setFgPickOrders([]);
    } finally {
      setFgPickOrdersLoading(false);
    }
  }, [currentUserId]);

  useEffect(() => {
    if (combinedLotData.length > 0) {
      fetchFgPickOrdersData();
    }
  }, [combinedLotData, fetchFgPickOrdersData]);

  //  Handle QR code button click
  const handleQrCodeClick = (pickOrderId: number) => {
    console.log(`QR Code clicked for pick order ID: ${pickOrderId}`);
    // TODO: Implement QR code functionality
  };

  //  修改：使用 Job Order API 获取数据
  const fetchJobOrderData = useCallback(
    async (pickOrderId?: number) => {
      setCombinedDataLoading(true);
      try {
        if (!pickOrderId) {
          console.warn("⚠️ No pickOrderId provided, skipping API call");
          return;
        }

        // 直接使用类型化的响应
        const jobOrderData =
          await fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench(pickOrderId);
        console.log("✅ Job Order data (hierarchical):", jobOrderData);

        setJobOrderData(jobOrderData);
        setIssuePickedQtyBySolId(loadIssuePickedMapJo(pickOrderId));

        // 使用辅助函数获取所有 lots（不再扁平化）
        getAllLotsFromHierarchical(jobOrderData);
      } catch (error) {
        console.error("❌ Error fetching job order data:", error);
        setJobOrderData(null);
        setIssuePickedQtyBySolId({});
      } finally {
        setCombinedDataLoading(false);
      }
    },
    [getAllLotsFromHierarchical],
  );

  const applyLocalStockOutLineUpdate = useCallback((..._args: unknown[]) => {
    // JO Workbench：以伺服器刷新為主；保留呼叫點與 DO Workbench 一致（不更新本地 SOL 鏡像）。
  }, []);

  const refreshWorkbenchAfterScanPick = useCallback(async () => {
    const pickOrderId = filterArgs?.pickOrderId
      ? Number(filterArgs.pickOrderId)
      : undefined;
    await fetchJobOrderData(pickOrderId);
  }, [fetchJobOrderData, filterArgs?.pickOrderId]);

  const updateHandledBy = useCallback(
    async (pickOrderId: number, itemId: number) => {
      if (!currentUserId || !pickOrderId || !itemId) {
        return;
      }

      try {
        console.log(
          `Updating JoPickOrder.handledBy for pickOrderId: ${pickOrderId}, itemId: ${itemId}, userId: ${currentUserId}`,
        );
        await updateJoPickOrderHandledBy({
          pickOrderId: pickOrderId,
          itemId: itemId,
          userId: currentUserId,
        });
        console.log("✅ JoPickOrder.handledBy updated successfully");
      } catch (error) {
        console.error("❌ Error updating JoPickOrder.handledBy:", error);
        // Don't throw - this is not critical for the main flow
      }
    },
    [currentUserId],
  );
  //  修改：初始化 — Workbench 先 prime SPL/SOL（後端不寫 pick_order.assignTo），再載入階層資料
  useEffect(() => {
    if (session && currentUserId && !initializationRef.current) {
      console.log("✅ Session loaded, initializing job order...");
      initializationRef.current = true;

      const pickOrderId = filterArgs?.pickOrderId
        ? Number(filterArgs.pickOrderId)
        : undefined;
      if (pickOrderId) {
        void (async () => {
          try {
            await handleAssignOrder(pickOrderId);
          } finally {
            await fetchJobOrderData(pickOrderId);
          }
        })();
      }
      loadUnassignedOrders();
    }
  }, [
    session,
    currentUserId,
    fetchJobOrderData,
    loadUnassignedOrders,
    filterArgs?.pickOrderId,
    handleAssignOrder,
  ]);

  // 與 GoodPickExecutiondetail 一致：session 就緒後自動開啟背景掃碼（平板現場用）
  useEffect(() => {
    if (session && currentUserId && !scannerInitializedRef.current) {
      scannerInitializedRef.current = true;
      console.log("✅ [JO] Auto-starting QR scanner in background mode");
      setIsManualScanning(true);
      startScan();
    }
  }, [session, currentUserId, startScan]);

  // 僅在元件卸載時重置，讓 React Strict Mode 二次掛載仍能再走一次自動開掃（不因 startScan 引用變化而重複開掃）
  useEffect(() => {
    return () => {
      scannerInitializedRef.current = false;
    };
  }, []);

  //  Add event listener for manual assignment
  useEffect(() => {
    const handlePickOrderAssigned = () => {
      console.log("🔄 Pick order assigned event received, refreshing data...");
      const pickOrderId = filterArgs?.pickOrderId
        ? Number(filterArgs.pickOrderId)
        : undefined;
      if (pickOrderId) {
        fetchJobOrderData(pickOrderId);
      }
    };

    window.addEventListener("pickOrderAssigned", handlePickOrderAssigned);

    return () => {
      window.removeEventListener("pickOrderAssigned", handlePickOrderAssigned);
    };
  }, [fetchJobOrderData, filterArgs?.pickOrderId]);

  /** 純 lotNo 手動輸入：與 DO Workbench 相同，不寫 checked，改提示用 scan-pick / Just Completed */
  const handleQrCodeSubmit = useCallback(
    async (lotNo: string) => {
      console.log(` Processing QR Code for lot: ${lotNo}`);

      if (!lotNo || lotNo === "null" || lotNo.trim() === "") {
        console.error("  Invalid lotNo: null, undefined, or empty");
        return;
      }

      const currentLotData = combinedLotData;
      console.log(
        ` Available lots:`,
        currentLotData.map((lot) => lot.lotNo),
      );

      const lotNoLower = lotNo.toLowerCase();
      const matchingLots = currentLotData.filter((lot) => {
        if (!lot.lotNo) return false;
        return lot.lotNo === lotNo || lot.lotNo.toLowerCase() === lotNoLower;
      });

      if (matchingLots.length === 0) {
        console.error(`  Lot not found: ${lotNo}`);
        setQrScanError(true);
        setQrScanSuccess(false);
        const availableLotNos = currentLotData
          .map((lot) => lot.lotNo)
          .join(", ");
        console.log(
          `  QR Code "${lotNo}" does not match any expected lots. Available lots: ${availableLotNos}`,
        );
        return;
      }

      const hasExpiredLot = matchingLots.some(
        (lot: any) =>
          String(lot.lotAvailability || "").toLowerCase() === "expired",
      );
      if (hasExpiredLot) {
        console.warn(`⚠️ [QR PROCESS] Scanned lot ${lotNo} is expired`);
        setQrScanError(true);
        setQrScanSuccess(false);
        return;
      }

      setQrScanError(true);
      setQrScanSuccess(false);
      setQrScanErrorMsg(
        tPick(
          "Workbench uses scan-pick. Please use Just Completed / submit via scan-pick instead of checked status.",
        ),
      );
    },
    [combinedLotData, tPick],
  );

  const clearLotConfirmationState = useCallback(
    (clearProcessedRefs: boolean = false) => {
      setExpectedLotData(null);
      setScannedLotData(null);
      setSelectedLotForQr(null);

      if (clearProcessedRefs) {
        setTimeout(() => {
          lastProcessedQrRef.current = "";
          processedQrCodesRef.current.clear();
        }, 100);
      }
    },
    [],
  );

  //  Add handleLotConfirmation function
  const handleLotConfirmation = useCallback(
    async (overrideScannedLot?: any) => {
      const effectiveScannedLot = overrideScannedLot ?? scannedLotData;
      if (!expectedLotData || !effectiveScannedLot || !selectedLotForQr) {
        console.error(
          "❌ [LOT CONFIRM] Missing required data for lot confirmation",
        );
        return;
      }

      console.log(
        "✅ [LOT CONFIRM] User confirmed lot substitution - processing now",
      );
      console.log("✅ [LOT CONFIRM] Expected lot:", expectedLotData);
      console.log("✅ [LOT CONFIRM] Scanned lot:", scannedLotData);
      console.log("✅ [LOT CONFIRM] Selected lot for QR:", selectedLotForQr);

      setIsConfirmingLot(true);
      try {
        let newLotLineId = effectiveScannedLot?.inventoryLotLineId;
        if (!newLotLineId && effectiveScannedLot?.stockInLineId) {
          try {
            if (
              currentUserId &&
              selectedLotForQr.pickOrderId &&
              selectedLotForQr.itemId
            ) {
              try {
                await updateHandledBy(
                  selectedLotForQr.pickOrderId,
                  selectedLotForQr.itemId,
                );
                console.log(
                  `✅ [LOT CONFIRM] Handler updated for itemId ${selectedLotForQr.itemId}`,
                );
              } catch (error) {
                console.error(
                  `❌ [LOT CONFIRM] Error updating handler (non-critical):`,
                  error,
                );
              }
            }
            console.log(
              `🔍 [LOT CONFIRM] Fetching lot detail for stockInLineId: ${effectiveScannedLot.stockInLineId}`,
            );
            const ld = await fetchLotDetail(effectiveScannedLot.stockInLineId);
            newLotLineId = ld.inventoryLotLineId;
            console.log(
              `✅ [LOT CONFIRM] Fetched lot detail: inventoryLotLineId=${newLotLineId}`,
            );
          } catch (error) {
            console.error(
              "❌ [LOT CONFIRM] Error fetching lot detail (stockInLineId may not exist):",
              error,
            );
            // If stockInLineId doesn't exist, we can still proceed with lotNo substitution
            // The backend confirmLotSubstitution should handle this case
          }
        }
        if (!newLotLineId) {
          console.warn(
            "⚠️ [LOT CONFIRM] No inventory lot line id for scanned lot, proceeding with lotNo only",
          );
          // Continue anyway - backend may handle lotNo substitution without inventoryLotLineId
        }

        console.log("=== [LOT CONFIRM] Lot Confirmation Debug ===");
        console.log("Selected Lot:", selectedLotForQr);
        console.log("Pick Order Line ID:", selectedLotForQr.pickOrderLineId);
        console.log("Stock Out Line ID:", selectedLotForQr.stockOutLineId);
        console.log(
          "Suggested Pick Lot ID:",
          selectedLotForQr.suggestedPickLotId,
        );
        console.log("Lot ID (fallback):", selectedLotForQr.lotId);
        console.log("New Inventory Lot Line ID:", newLotLineId);
        console.log("Scanned Lot No:", effectiveScannedLot.lotNo);
        console.log(
          "Scanned StockInLineId:",
          effectiveScannedLot.stockInLineId,
        );

        const originalSuggestedPickLotId =
          selectedLotForQr.suggestedPickLotId || selectedLotForQr.lotId;

        let switchedToUnavailable = false;
        // noLot / missing suggestedPickLotId 场景：没有 originalSuggestedPickLotId，改用 updateStockOutLineStatusByQRCodeAndLotNo
        if (!originalSuggestedPickLotId) {
          if (!selectedLotForQr?.stockOutLineId) {
            throw new Error("Missing stockOutLineId for noLot line");
          }
          console.log(
            "🔄 [LOT CONFIRM] No originalSuggestedPickLotId, using updateStockOutLineStatusByQRCodeAndLotNo...",
          );
          const res = await updateStockOutLineStatusByQRCodeAndLotNo({
            pickOrderLineId: selectedLotForQr.pickOrderLineId,
            inventoryLotNo: effectiveScannedLot.lotNo || "",
            stockInLineId: effectiveScannedLot?.stockInLineId ?? null,
            stockOutLineId: selectedLotForQr.stockOutLineId,
            itemId: selectedLotForQr.itemId,
            status: "checked",
          });
          console.log(
            "✅ [LOT CONFIRM] updateStockOutLineStatusByQRCodeAndLotNo result:",
            res,
          );
          switchedToUnavailable = res?.code === "BOUND_UNAVAILABLE";
          const ok =
            res?.code === "checked" ||
            res?.code === "SUCCESS" ||
            switchedToUnavailable;
          if (!ok) {
            const errMsg =
              res?.code === "LOT_UNAVAILABLE"
                ? tPick(
                    "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.",
                  )
                : res?.message ||
                  tPick(
                    "Lot switch failed; pick line was not marked as checked.",
                  );
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(errMsg);
            return;
          }
        } else {
          //  Call confirmLotSubstitution to update the suggested lot
          console.log("🔄 [LOT CONFIRM] Calling confirmLotSubstitution...");
          const substitutionResult = await confirmLotSubstitution({
            pickOrderLineId: selectedLotForQr.pickOrderLineId,
            stockOutLineId: selectedLotForQr.stockOutLineId,
            originalSuggestedPickLotId,
            newInventoryLotNo: effectiveScannedLot.lotNo || "",
            // ✅ required by LotSubstitutionConfirmRequest
            newStockInLineId: effectiveScannedLot?.stockInLineId ?? null,
          });

          console.log(
            "✅ [LOT CONFIRM] Lot substitution result:",
            substitutionResult,
          );

          // ✅ CRITICAL: substitution failed => DO NOT mark original stockOutLine as checked.
          // Keep modal open so user can cancel/rescan.
          switchedToUnavailable =
            substitutionResult?.code === "SUCCESS_UNAVAILABLE" ||
            substitutionResult?.code === "BOUND_UNAVAILABLE";
          if (
            !substitutionResult ||
            (substitutionResult.code !== "SUCCESS" && !switchedToUnavailable)
          ) {
            console.error(
              "❌ [LOT CONFIRM] Lot substitution failed. Will NOT update stockOutLine status.",
            );
            const errMsg =
              substitutionResult?.code === "LOT_UNAVAILABLE"
                ? tPick(
                    "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.",
                  )
                : substitutionResult?.message ||
                  `换批失败：stockInLineId ${
                    effectiveScannedLot?.stockInLineId ?? ""
                  } 不存在或无法匹配`;
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(errMsg);
            return;
          }
        }

        // Workbench：以 scan-pick 過帳（與 DO Workbench 一致）；非 workbench 仍寫 checked
        if (
          workbenchMode &&
          selectedLotForQr?.stockOutLineId &&
          !switchedToUnavailable
        ) {
          const lotNoToScan = String(effectiveScannedLot.lotNo || "").trim();
          const silId = effectiveScannedLot?.stockInLineId;
          if (!lotNoToScan) {
            const errMsg = tPick("Cannot resolve lot number for confirmation.");
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(errMsg);
            return;
          }
          const res = await workbenchScanPick({
            stockOutLineId: selectedLotForQr.stockOutLineId,
            lotNo: lotNoToScan,
            ...(typeof silId === "number" && Number.isFinite(silId) && silId > 0
              ? { stockInLineId: silId }
              : {}),
            ...workbenchScanPickQtyFromLot(selectedLotForQr),
            storeId: workbenchStoreId,
            userId: currentUserId ?? 1,
          });
          if (res.code !== "SUCCESS") {
            const errMsg =
              (res as { message?: string })?.message ||
              tPick("Workbench scan-pick failed.");
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(errMsg);
            if (selectedLotForQr.stockOutLineId != null) {
              rememberWorkbenchScanReject(
                Number(selectedLotForQr.stockOutLineId),
                errMsg,
              );
            }
            return;
          }
          clearWorkbenchScanReject(Number(selectedLotForQr.stockOutLineId));
        } else if (selectedLotForQr?.stockOutLineId && !switchedToUnavailable) {
          console.log(
            `🔄 [LOT CONFIRM] Updating stockOutLine ${selectedLotForQr.stockOutLineId} to 'checked'`,
          );
          await updateStockOutLineStatus({
            id: selectedLotForQr.stockOutLineId,
            status: "checked",
            qty: 0,
          });
          console.log(
            `✅ [LOT CONFIRM] Stock out line ${selectedLotForQr.stockOutLineId} status updated to 'checked'`,
          );
        }

        //  Close modal and clean up state BEFORE refreshing
        clearLotConfirmationState(false);

        //  Clear QR processing state but DON'T clear processedQrCodes yet
        setQrScanError(false);
        setQrScanSuccess(true);
        setQrScanErrorMsg("");
        setQrScanInput("");

        //  Set refreshing flag to prevent QR processing during refresh
        setIsRefreshingData(true);

        //  Refresh data to show updated lot
        console.log("🔄 Refreshing job order data...");
        const pickOrderId = filterArgs?.pickOrderId
          ? Number(filterArgs.pickOrderId)
          : undefined;
        await fetchJobOrderData(pickOrderId);
        console.log(" Lot substitution confirmed and data refreshed");

        //  Clear processed QR codes and flags immediately after refresh
        // This allows new QR codes to be processed right away
        setTimeout(() => {
          console.log(" Clearing processed QR codes and resuming scan");
          setProcessedQrCodes(new Set());
          setLastProcessedQr("");
          setQrScanSuccess(false);
          setIsRefreshingData(false);
          // ✅ Clear processedQrCombinations to allow reprocessing the same QR if needed
          if (effectiveScannedLot?.stockInLineId && selectedLotForQr?.itemId) {
            setProcessedQrCombinations((prev) => {
              const newMap = new Map(prev);
              const itemId = selectedLotForQr.itemId;
              if (itemId && newMap.has(itemId)) {
                newMap.get(itemId)!.delete(effectiveScannedLot.stockInLineId);
                if (newMap.get(itemId)!.size === 0) {
                  newMap.delete(itemId);
                }
              }
              return newMap;
            });
          }
        }, 500); //  Reduced from 3000ms to 500ms - just enough for UI update
      } catch (error) {
        console.error("Error confirming lot substitution:", error);
        const errMsg = tPick("Lot confirmation failed. Please try again.");
        setQrScanError(true);
        setQrScanSuccess(false);
        setQrScanErrorMsg(errMsg);
        //  Clear refresh flag on error
        setIsRefreshingData(false);
      } finally {
        setIsConfirmingLot(false);
      }
    },
    [
      expectedLotData,
      scannedLotData,
      selectedLotForQr,
      fetchJobOrderData,
      currentUserId,
      updateHandledBy,
      tPick,
      clearLotConfirmationState,
      workbenchMode,
      workbenchScanPickQtyFromLot,
      workbenchStoreId,
      rememberWorkbenchScanReject,
      clearWorkbenchScanReject,
    ],
  );

  const processOutsideQrCode = useCallback(
    async (latestQr: string, _qrScanCountAtInvoke?: number) => {
      const totalStartTime = performance.now();
      console.log(
        `  [PROCESS OUTSIDE QR START] QR: ${latestQr.substring(0, 50)}...`,
      );
      console.log(`  Start time: ${new Date().toISOString()}`);

      // ✅ Measure index access time
      const indexAccessStart = performance.now();
      const indexes = lotDataIndexes; // Access the memoized indexes
      const indexAccessTime = performance.now() - indexAccessStart;
      console.log(
        `  [PERF] Index access time: ${indexAccessTime.toFixed(2)}ms`,
      );

      // 1) Parse JSON safely (parse once, reuse)
      const parseStartTime = performance.now();
      let qrData: any = null;
      let parseTime = 0;
      try {
        qrData = JSON.parse(latestQr);
        parseTime = performance.now() - parseStartTime;
        console.log(`  [PERF] JSON parse time: ${parseTime.toFixed(2)}ms`);
      } catch {
        console.log(
          "QR content is not JSON; skipping lotNo direct submit to avoid false matches.",
        );
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
        });
        return;
      }

      try {
        const validationStartTime = performance.now();
        if (!(qrData?.stockInLineId && qrData?.itemId)) {
          console.log(
            "QR JSON missing required fields (itemId, stockInLineId).",
          );
          startTransition(() => {
            setQrScanError(true);
            setQrScanSuccess(false);
          });
          return;
        }
        const validationTime = performance.now() - validationStartTime;
        console.log(`  [PERF] Validation time: ${validationTime.toFixed(2)}ms`);

        const scannedItemId = qrData.itemId;
        const scannedStockInLineId = qrData.stockInLineId;

        // ✅ Check if this combination was already processed
        const duplicateCheckStartTime = performance.now();
        const itemProcessedSet = processedQrCombinations.get(scannedItemId);
        if (itemProcessedSet?.has(scannedStockInLineId)) {
          const duplicateCheckTime =
            performance.now() - duplicateCheckStartTime;
          console.log(
            `  [SKIP] Already processed combination: itemId=${scannedItemId}, stockInLineId=${scannedStockInLineId} (check time: ${duplicateCheckTime.toFixed(
              2,
            )}ms)`,
          );
          return;
        }
        const duplicateCheckTime = performance.now() - duplicateCheckStartTime;
        console.log(
          `  [PERF] Duplicate check time: ${duplicateCheckTime.toFixed(2)}ms`,
        );

        // ✅ OPTIMIZATION: Use cached active lots directly (no filtering needed)
        const lookupStartTime = performance.now();
        const activeSuggestedLots =
          indexes.activeLotsByItemId.get(scannedItemId) || [];
        // ✅ Also get all lots for this item (not just active ones) to allow lot switching even when all lots are rejected
        const allLotsForItem = indexes.byItemId.get(scannedItemId) || [];
        const lookupTime = performance.now() - lookupStartTime;
        console.log(
          `  [PERF] Index lookup time: ${lookupTime.toFixed(2)}ms, found ${
            activeSuggestedLots.length
          } active lots, ${allLotsForItem.length} total lots`,
        );

        // ✅ Check if scanned lot is rejected BEFORE checking activeSuggestedLots
        // This allows users to scan other lots even when all suggested lots are rejected
        const scannedLot = allLotsForItem.find(
          (lot: any) => lot.stockInLineId === scannedStockInLineId,
        );

        if (scannedLot) {
          const isRejected =
            scannedLot.stockOutLineStatus?.toLowerCase() === "rejected" ||
            scannedLot.lotAvailability === "rejected" ||
            isInventoryLotLineUnavailable(scannedLot);

          if (isRejected) {
            console.warn(
              `⚠️ [QR PROCESS] Scanned lot (stockInLineId: ${scannedStockInLineId}, lotNo: ${scannedLot.lotNo}) is rejected or unavailable`,
            );
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(
                `此批次（${
                  scannedLot.lotNo || scannedStockInLineId
                }）已被拒绝，无法使用。请扫描其他批次。`,
              );
            });
            // Mark as processed to prevent re-processing
            setProcessedQrCombinations((prev) => {
              const newMap = new Map(prev);
              if (!newMap.has(scannedItemId))
                newMap.set(scannedItemId, new Set());
              newMap.get(scannedItemId)!.add(scannedStockInLineId);
              return newMap;
            });
            return;
          }

          const isExpired =
            String(scannedLot.lotAvailability || "").toLowerCase() ===
            "expired";
          if (isExpired) {
            console.warn(
              `⚠️ [QR PROCESS] Scanned lot (stockInLineId: ${scannedStockInLineId}, lotNo: ${scannedLot.lotNo}) is expired`,
            );
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(
                `此批次（${
                  scannedLot.lotNo || scannedStockInLineId
                }）已过期，无法使用。请扫描其他批次。`,
              );
            });
            // Mark as processed to prevent re-processing the same expired QR repeatedly
            setProcessedQrCombinations((prev) => {
              const newMap = new Map(prev);
              if (!newMap.has(scannedItemId))
                newMap.set(scannedItemId, new Set());
              newMap.get(scannedItemId)!.add(scannedStockInLineId);
              return newMap;
            });
            return;
          }
        }

        // ✅ If no active suggested lots, auto-switch to scanned lot (no modal)
        if (activeSuggestedLots.length === 0) {
          // Check if there are any lots for this item (even if all are rejected)
          if (allLotsForItem.length === 0) {
            console.error("No lots found for this item");
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg("当前订单中没有此物品的批次信息");
            });
            return;
          }

          console.log(
            `⚠️ [QR PROCESS] No active suggested lots, auto-switching to scanned lot.`,
          );

          // Find a rejected lot as expected lot (the one that was rejected)
          const rejectedLot = allLotsForItem.find(
            (lot: any) =>
              lot.stockOutLineStatus?.toLowerCase() === "rejected" ||
              lot.lotAvailability === "rejected" ||
              isInventoryLotLineUnavailable(lot),
          );
          const expectedLot =
            rejectedLot ||
            pickExpectedLotForSubstitution(
              allLotsForItem.filter(
                (l: any) => l.lotNo != null && String(l.lotNo).trim() !== "",
              ),
            ) ||
            allLotsForItem[0];

          let scannedLotNo: string | null = scannedLot?.lotNo || null;
          if (!scannedLotNo) {
            try {
              const info =
                await fetchStockInLineInfoCached(scannedStockInLineId);
              scannedLotNo = info?.lotNo || null;
            } catch (e) {
              console.warn(
                "Failed to fetch lotNo for stockInLineId:",
                scannedStockInLineId,
                e,
              );
            }
          }
          if (!scannedLotNo) {
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(
                tPick(
                  "Cannot resolve lot number from QR. Please rescan or use manual confirmation.",
                ),
              );
            });
            return;
          }

          if (!workbenchMode) {
            const substitutionResult = await confirmLotSubstitution({
              pickOrderLineId: expectedLot.pickOrderLineId,
              stockOutLineId: expectedLot.stockOutLineId,
              originalSuggestedPickLotId: expectedLot.suggestedPickLotId,
              newInventoryLotNo: "",
              newStockInLineId: scannedStockInLineId,
            });
            const substitutionCode = (substitutionResult as any)?.code;
            const switchedToUnavailable =
              substitutionCode === "SUCCESS_UNAVAILABLE" ||
              substitutionCode === "BOUND_UNAVAILABLE";
            if (
              !substitutionResult ||
              (substitutionCode !== "SUCCESS" && !switchedToUnavailable)
            ) {
              const errMsg =
                substitutionResult?.message ||
                tPick("Lot switch failed; pick line was not updated.");
              startTransition(() => {
                setQrScanError(true);
                setQrScanSuccess(false);
                setQrScanErrorMsg(errMsg);
              });
              if (expectedLot.stockOutLineId != null) {
                rememberWorkbenchScanReject(
                  Number(expectedLot.stockOutLineId),
                  errMsg,
                );
              }
              return;
            }
          }

          const res = await workbenchScanPick({
            stockOutLineId: expectedLot.stockOutLineId,
            lotNo: scannedLotNo,
            ...(typeof scannedStockInLineId === "number" &&
            Number.isFinite(scannedStockInLineId) &&
            scannedStockInLineId > 0
              ? { stockInLineId: scannedStockInLineId }
              : {}),
            ...workbenchScanPickQtyFromLot(expectedLot),
            storeId: workbenchStoreId,
            userId: currentUserId ?? 1,
          });
          const ok = res.code === "SUCCESS";
          if (!ok) {
            const failMsg =
              (res as { message?: string })?.message ||
              tPick("Workbench scan-pick failed.");
            if (
              shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) &&
              expectedLot
            ) {
              openWorkbenchLotLabelModalForLot(expectedLot, failMsg);
              return;
            }
            if (workbenchMode && expectedLot.stockOutLineId != null) {
              rememberWorkbenchScanReject(
                Number(expectedLot.stockOutLineId),
                failMsg,
              );
            }
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(failMsg);
            });
            return;
          }

          clearWorkbenchScanReject(Number(expectedLot.stockOutLineId));

          startTransition(() => {
            setQrScanError(false);
            setQrScanSuccess(true);
          });

          setProcessedQrCombinations((prev) => {
            const newMap = new Map(prev);
            if (!newMap.has(scannedItemId))
              newMap.set(scannedItemId, new Set());
            newMap.get(scannedItemId)!.add(scannedStockInLineId);
            return newMap;
          });

          if (workbenchMode) {
            await refreshWorkbenchAfterScanPick();
          }

          return;
        }

        // ✅ OPTIMIZATION: Direct Map lookup for stockInLineId match (O(1))
        const matchStartTime = performance.now();
        let exactMatch: any = null;
        const stockInLineLots =
          indexes.byStockInLineId.get(scannedStockInLineId) || [];
        // Find exact match from stockInLineId index, then verify it's in active lots
        for (let i = 0; i < stockInLineLots.length; i++) {
          const lot = stockInLineLots[i];
          if (
            lot.itemId === scannedItemId &&
            activeSuggestedLots.includes(lot)
          ) {
            exactMatch = lot;
            break;
          }
        }
        const matchTime = performance.now() - matchStartTime;
        console.log(
          `  [PERF] Find exact match time: ${matchTime.toFixed(2)}ms, found: ${
            exactMatch ? "yes" : "no"
          }`,
        );

        // ✅ Check if scanned lot exists in allLotsForItem but not in activeSuggestedLots
        // This handles the case where Lot A is rejected and user scans Lot B
        // Also handle case where scanned lot is not in allLotsForItem (scannedLot is undefined)
        if (!exactMatch) {
          const expectedLot =
            pickExpectedLotForSubstitution(activeSuggestedLots) ||
            allLotsForItem[0];
          if (expectedLot) {
            const shouldAutoSwitch =
              !scannedLot ||
              scannedLot.stockInLineId !== expectedLot.stockInLineId;
            if (shouldAutoSwitch) {
              console.log(
                `⚠️ [QR PROCESS] Auto-switching (scanned lot ${
                  scannedLot?.lotNo || "not in data"
                } is not in active suggested lots)`,
              );

              let scannedLotNo: string | null = scannedLot?.lotNo || null;
              if (!scannedLotNo) {
                try {
                  const info =
                    await fetchStockInLineInfoCached(scannedStockInLineId);
                  scannedLotNo = info?.lotNo || null;
                } catch (e) {
                  console.warn(
                    "Failed to fetch lotNo for stockInLineId:",
                    scannedStockInLineId,
                    e,
                  );
                }
              }
              if (!scannedLotNo) {
                startTransition(() => {
                  setQrScanError(true);
                  setQrScanSuccess(false);
                  setQrScanErrorMsg(
                    tPick(
                      "Cannot resolve lot number from QR. Please rescan or use manual confirmation.",
                    ),
                  );
                });
                return;
              }

              if (!workbenchMode) {
                const substitutionResult = await confirmLotSubstitution({
                  pickOrderLineId: expectedLot.pickOrderLineId,
                  stockOutLineId: expectedLot.stockOutLineId,
                  originalSuggestedPickLotId: expectedLot.suggestedPickLotId,
                  newInventoryLotNo: "",
                  newStockInLineId: scannedStockInLineId,
                });
                const substitutionCode = (substitutionResult as any)?.code;
                const switchedToUnavailable =
                  substitutionCode === "SUCCESS_UNAVAILABLE" ||
                  substitutionCode === "BOUND_UNAVAILABLE";
                if (
                  !substitutionResult ||
                  (substitutionCode !== "SUCCESS" && !switchedToUnavailable)
                ) {
                  const errMsg =
                    substitutionResult?.message ||
                    tPick("Lot switch failed; pick line was not updated.");
                  startTransition(() => {
                    setQrScanError(true);
                    setQrScanSuccess(false);
                    setQrScanErrorMsg(errMsg);
                  });
                  if (expectedLot.stockOutLineId != null) {
                    rememberWorkbenchScanReject(
                      Number(expectedLot.stockOutLineId),
                      errMsg,
                    );
                  }
                  return;
                }
              }

              const res = await workbenchScanPick({
                stockOutLineId: expectedLot.stockOutLineId,
                lotNo: scannedLotNo,
                ...(typeof scannedStockInLineId === "number" &&
                Number.isFinite(scannedStockInLineId) &&
                scannedStockInLineId > 0
                  ? { stockInLineId: scannedStockInLineId }
                  : {}),
                ...workbenchScanPickQtyFromLot(expectedLot),
                storeId: workbenchStoreId,
                userId: currentUserId ?? 1,
              });
              const ok = res.code === "SUCCESS";
              if (!ok) {
                const failMsg =
                  (res as { message?: string })?.message ||
                  tPick("Workbench scan-pick failed.");
                if (
                  shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) &&
                  expectedLot
                ) {
                  openWorkbenchLotLabelModalForLot(expectedLot, failMsg);
                  return;
                }
                if (workbenchMode && expectedLot.stockOutLineId != null) {
                  rememberWorkbenchScanReject(
                    Number(expectedLot.stockOutLineId),
                    failMsg,
                  );
                }
                startTransition(() => {
                  setQrScanError(true);
                  setQrScanSuccess(false);
                  setQrScanErrorMsg(failMsg);
                });
                return;
              }

              clearWorkbenchScanReject(Number(expectedLot.stockOutLineId));

              startTransition(() => {
                setQrScanError(false);
                setQrScanSuccess(true);
              });

              setProcessedQrCombinations((prev) => {
                const newMap = new Map(prev);
                if (!newMap.has(scannedItemId))
                  newMap.set(scannedItemId, new Set());
                newMap.get(scannedItemId)!.add(scannedStockInLineId);
                return newMap;
              });
              if (workbenchMode) {
                await refreshWorkbenchAfterScanPick();
              }
              return;
            }
          }
        }

        if (exactMatch) {
          // ✅ Case 1: stockInLineId 匹配 - 直接处理，不需要确认
          console.log(
            `✅ Exact stockInLineId match found for lot: ${exactMatch.lotNo}`,
          );

          if (!exactMatch.stockOutLineId) {
            console.warn(
              "No stockOutLineId on exactMatch, cannot update status by QR.",
            );
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
            });
            return;
          }

          try {
            const apiStartTime = performance.now();
            console.log(
              workbenchMode
                ? `  [API CALL START] workbenchScanPick`
                : `  [API CALL START] Calling updateStockOutLineStatusByQRCodeAndLotNo`,
            );
            console.log(
              `  [API CALL] API start time: ${new Date().toISOString()}`,
            );
            const res = await workbenchScanPick({
              stockOutLineId: exactMatch.stockOutLineId,
              lotNo: exactMatch.lotNo,
              ...(typeof scannedStockInLineId === "number" &&
              Number.isFinite(scannedStockInLineId) &&
              scannedStockInLineId > 0
                ? { stockInLineId: scannedStockInLineId }
                : typeof exactMatch.stockInLineId === "number" &&
                    Number.isFinite(exactMatch.stockInLineId) &&
                    exactMatch.stockInLineId > 0
                  ? { stockInLineId: exactMatch.stockInLineId }
                  : {}),
              ...workbenchScanPickQtyFromLot(exactMatch),
              storeId: workbenchStoreId,
              userId: currentUserId ?? 1,
            });
            const apiTime = performance.now() - apiStartTime;
            console.log(
              `  [API CALL END] Total API time: ${apiTime.toFixed(2)}ms (${(
                apiTime / 1000
              ).toFixed(3)}s)`,
            );
            console.log(
              `  [API CALL] API end time: ${new Date().toISOString()}`,
            );

            const ok = res.code === "SUCCESS";
            if (ok) {
              clearWorkbenchScanReject(Number(exactMatch.stockOutLineId));

              // ✅ Batch state updates using startTransition
              const stateUpdateStartTime = performance.now();
              startTransition(() => {
                setQrScanError(false);
                setQrScanSuccess(true);
              });
              const stateUpdateTime = performance.now() - stateUpdateStartTime;
              console.log(
                `  [PERF] State update time: ${stateUpdateTime.toFixed(2)}ms`,
              );

              // Mark this combination as processed
              const markProcessedStartTime = performance.now();
              setProcessedQrCombinations((prev) => {
                const newMap = new Map(prev);
                if (!newMap.has(scannedItemId)) {
                  newMap.set(scannedItemId, new Set());
                }
                newMap.get(scannedItemId)!.add(scannedStockInLineId);
                return newMap;
              });
              const markProcessedTime =
                performance.now() - markProcessedStartTime;
              console.log(
                `  [PERF] Mark processed time: ${markProcessedTime.toFixed(
                  2,
                )}ms`,
              );

              if (workbenchMode) {
                await refreshWorkbenchAfterScanPick();
              }

              const totalTime = performance.now() - totalStartTime;
              console.log(
                `✅ [PROCESS OUTSIDE QR END] Total time: ${totalTime.toFixed(
                  2,
                )}ms (${(totalTime / 1000).toFixed(3)}s)`,
              );
              console.log(`  End time: ${new Date().toISOString()}`);
              console.log(
                `📊 Breakdown: parse=${parseTime.toFixed(
                  2,
                )}ms, validation=${validationTime.toFixed(
                  2,
                )}ms, duplicateCheck=${duplicateCheckTime.toFixed(
                  2,
                )}ms, lookup=${lookupTime.toFixed(
                  2,
                )}ms, match=${matchTime.toFixed(2)}ms, api=${apiTime.toFixed(
                  2,
                )}ms, stateUpdate=${stateUpdateTime.toFixed(
                  2,
                )}ms, markProcessed=${markProcessedTime.toFixed(2)}ms`,
              );
              console.log(
                workbenchMode
                  ? "✅ Workbench scan-pick: list refreshed from server"
                  : "✅ Status updated locally, no full data refresh needed",
              );
            } else {
              console.warn("Unexpected response code from backend:", res.code);
              const failMsg =
                (res as { message?: string })?.message ||
                tPick("Workbench scan-pick failed.");
              if (
                shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) &&
                exactMatch
              ) {
                openWorkbenchLotLabelModalForLot(exactMatch, failMsg);
                return;
              }
              if (workbenchMode && exactMatch.stockOutLineId != null) {
                rememberWorkbenchScanReject(
                  Number(exactMatch.stockOutLineId),
                  failMsg,
                );
              }
              startTransition(() => {
                setQrScanError(true);
                setQrScanSuccess(false);
                setQrScanErrorMsg(failMsg);
              });
            }
          } catch (e) {
            const totalTime = performance.now() - totalStartTime;
            console.error(
              `❌ [PROCESS OUTSIDE QR ERROR] Total time: ${totalTime.toFixed(
                2,
              )}ms`,
            );
            console.error(
              "Error calling updateStockOutLineStatusByQRCodeAndLotNo:",
              e,
            );
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
            });
          }

          return; // ✅ 直接返回，不需要确认表单
        }

        // ✅ Case 2: itemId 匹配但 stockInLineId 不匹配
        // Workbench 策略：不彈窗，直接切換到掃到的批次並提交一次掃描
        const mismatchCheckStartTime = performance.now();
        const itemProcessedSet2 = processedQrCombinations.get(scannedItemId);
        if (itemProcessedSet2?.has(scannedStockInLineId)) {
          const mismatchCheckTime = performance.now() - mismatchCheckStartTime;
          console.log(
            `  [SKIP] Already processed this exact combination (check time: ${mismatchCheckTime.toFixed(
              2,
            )}ms)`,
          );
          return;
        }
        const mismatchCheckTime = performance.now() - mismatchCheckStartTime;
        console.log(
          `  [PERF] Mismatch check time: ${mismatchCheckTime.toFixed(2)}ms`,
        );

        const expectedLotStartTime = performance.now();
        const expectedLot = pickExpectedLotForSubstitution(activeSuggestedLots);
        if (!expectedLot) {
          console.error("Could not determine expected lot for auto-switch");
          startTransition(() => {
            setQrScanError(true);
            setQrScanSuccess(false);
          });
          return;
        }
        const expectedLotTime = performance.now() - expectedLotStartTime;
        console.log(
          `  [PERF] Get expected lot time: ${expectedLotTime.toFixed(2)}ms`,
        );

        console.log(
          `⚠️ Lot mismatch (auto): Expected stockInLineId=${expectedLot.stockInLineId}, Scanned stockInLineId=${scannedStockInLineId}`,
        );

        // 1) 先把掃到的 stockInLineId 轉成 lotNo（workbenchScanPick 需要 lotNo）
        let scannedLotNo: string | null = null;
        try {
          const info = await fetchStockInLineInfoCached(scannedStockInLineId);
          scannedLotNo = info?.lotNo || null;
        } catch (e) {
          console.warn(
            "Failed to fetch lotNo for stockInLineId:",
            scannedStockInLineId,
            e,
          );
        }
        if (!scannedLotNo) {
          const msg = tPick(
            "Cannot resolve lot number from QR. Please rescan or use manual confirmation.",
          );
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(msg);
          return;
        }

        // 2) 非 workbench：先 confirmLotSubstitution；workbench 僅依 scan-pick 規則與錯誤訊息
        if (!workbenchMode) {
          const substitutionResult = await confirmLotSubstitution({
            pickOrderLineId: expectedLot.pickOrderLineId,
            stockOutLineId: expectedLot.stockOutLineId,
            originalSuggestedPickLotId: expectedLot.suggestedPickLotId,
            newInventoryLotNo: "",
            newStockInLineId: scannedStockInLineId,
          });
          const substitutionCode = (substitutionResult as any)?.code;
          const switchedToUnavailable =
            substitutionCode === "SUCCESS_UNAVAILABLE" ||
            substitutionCode === "BOUND_UNAVAILABLE";
          if (
            !substitutionResult ||
            (substitutionCode !== "SUCCESS" && !switchedToUnavailable)
          ) {
            const errMsg =
              substitutionResult?.message ||
              tPick("Lot switch failed; pick line was not updated.");
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(errMsg);
            if (expectedLot.stockOutLineId != null) {
              rememberWorkbenchScanReject(
                Number(expectedLot.stockOutLineId),
                errMsg,
              );
            }
            return;
          }
        }

        // 3) 提交掃描（workbench：直接 workbenchScanPick）
        try {
          const res = await workbenchScanPick({
            stockOutLineId: expectedLot.stockOutLineId,
            lotNo: scannedLotNo,
            ...(typeof scannedStockInLineId === "number" &&
            Number.isFinite(scannedStockInLineId) &&
            scannedStockInLineId > 0
              ? { stockInLineId: scannedStockInLineId }
              : {}),
            ...workbenchScanPickQtyFromLot(expectedLot),
            storeId: workbenchStoreId,
            userId: currentUserId ?? 1,
          });

          const ok = res.code === "SUCCESS";

          if (!ok) {
            const failMsg =
              (res as { message?: string })?.message ||
              tPick("Workbench scan-pick failed.");
            if (
              shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) &&
              expectedLot
            ) {
              openWorkbenchLotLabelModalForLot(expectedLot, failMsg);
              return;
            }
            if (workbenchMode && expectedLot.stockOutLineId != null) {
              rememberWorkbenchScanReject(
                Number(expectedLot.stockOutLineId),
                failMsg,
              );
            }
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(failMsg);
            return;
          }

          clearWorkbenchScanReject(Number(expectedLot.stockOutLineId));

          startTransition(() => {
            setQrScanError(false);
            setQrScanSuccess(true);
          });

          setProcessedQrCombinations((prev) => {
            const newMap = new Map(prev);
            if (!newMap.has(scannedItemId))
              newMap.set(scannedItemId, new Set());
            newMap.get(scannedItemId)!.add(scannedStockInLineId);
            return newMap;
          });

          if (workbenchMode) {
            await refreshWorkbenchAfterScanPick();
          }
        } catch (e) {
          console.error("Auto-switch scanPick failed:", e);
          setQrScanError(true);
          setQrScanSuccess(false);
          return;
        }

        const totalTime = performance.now() - totalStartTime;
        console.log(
          `✅ [PROCESS OUTSIDE QR AUTO-SWITCH] Total time: ${totalTime.toFixed(
            2,
          )}ms (${(totalTime / 1000).toFixed(3)}s)`,
        );
        console.log(`  End time: ${new Date().toISOString()}`);
        console.log(
          `📊 Breakdown: parse=${parseTime.toFixed(
            2,
          )}ms, validation=${validationTime.toFixed(
            2,
          )}ms, duplicateCheck=${duplicateCheckTime.toFixed(
            2,
          )}ms, lookup=${lookupTime.toFixed(2)}ms, match=${matchTime.toFixed(
            2,
          )}ms, mismatchCheck=${mismatchCheckTime.toFixed(
            2,
          )}ms, expectedLot=${expectedLotTime.toFixed(2)}ms`,
        );
      } catch (error) {
        const totalTime = performance.now() - totalStartTime;
        console.error(
          `❌ [PROCESS OUTSIDE QR ERROR] Total time: ${totalTime.toFixed(2)}ms`,
        );
        console.error("Error during QR code processing:", error);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
        });
        return;
      }
    },
    [
      lotDataIndexes,
      processedQrCombinations,
      combinedLotData,
      fetchStockInLineInfoCached,
      workbenchMode,
      currentUserId,
      clearWorkbenchScanReject,
      rememberWorkbenchScanReject,
      refreshWorkbenchAfterScanPick,
      workbenchScanPickQtyFromLot,
      tPick,
      workbenchStoreId,
      confirmLotSubstitution,
      openWorkbenchLotLabelModalForLot,
      shouldOpenWorkbenchLotLabelModalForFailure,
    ],
  );

  // Store in refs for immediate access in qrValues effect
  processOutsideQrCodeRef.current = processOutsideQrCode;
  resetScanRef.current = resetScan;

  const handleManualInputSubmit = useCallback(() => {
    if (qrScanInput.trim() !== "") {
      handleQrCodeSubmit(qrScanInput.trim());
    }
  }, [qrScanInput, handleQrCodeSubmit]);

  //  Handle QR code submission from modal (internal scanning)
  const handleQrCodeSubmitFromModal = useCallback(
    async (lotNo: string) => {
      if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) {
        console.log(` QR Code verified for lot: ${lotNo}`);

        const requiredQty = selectedLotForQr.requiredQty;
        const lotId = selectedLotForQr.lotId;

        // Create stock out line
        const stockOutLineData: CreateStockOutLine = {
          consoCode: selectedLotForQr.pickOrderConsoCode,
          pickOrderLineId: selectedLotForQr.pickOrderLineId,
          inventoryLotLineId: selectedLotForQr.lotId,
          qty: 0.0,
        };

        try {
          await createStockOutLine(stockOutLineData);
          console.log("Stock out line created successfully!");

          // Close modal
          setQrModalOpen(false);
          setSelectedLotForQr(null);

          // Set pick quantity
          const lotKey = `${selectedLotForQr.pickOrderLineId}-${lotId}`;
          setTimeout(() => {
            setPickQtyData((prev) => ({
              ...prev,
              [lotKey]: requiredQty,
            }));
            console.log(
              ` Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`,
            );
          }, 500);

          // Refresh data
          const pickOrderId = filterArgs?.pickOrderId
            ? Number(filterArgs.pickOrderId)
            : undefined;
          await fetchJobOrderData(pickOrderId);
        } catch (error) {
          console.error("Error creating stock out line:", error);
        }
      }
    },
    [selectedLotForQr, fetchJobOrderData],
  );

  useEffect(() => {
    // Skip if scanner not active or no data or currently refreshing
    if (
      !isManualScanning ||
      qrValues.length === 0 ||
      combinedLotData.length === 0 ||
      isRefreshingData
    )
      return;

    const latestQr = qrValues[qrValues.length - 1];

    // ✅ Test shortcut: {2fitestx,y} or {2fittestx,y} where x=itemId, y=stockInLineId
    if (
      (latestQr.startsWith("{2fitest") || latestQr.startsWith("{2fittest")) &&
      latestQr.endsWith("}")
    ) {
      let content = "";
      if (latestQr.startsWith("{2fittest"))
        content = latestQr.substring(9, latestQr.length - 1);
      else content = latestQr.substring(8, latestQr.length - 1);

      const parts = content.split(",");
      if (parts.length === 2) {
        const itemId = parseInt(parts[0].trim(), 10);
        const stockInLineId = parseInt(parts[1].trim(), 10);
        if (!isNaN(itemId) && !isNaN(stockInLineId)) {
          const simulatedQr = JSON.stringify({ itemId, stockInLineId });

          lastProcessedQrRef.current = latestQr;
          processedQrCodesRef.current.add(latestQr);
          setLastProcessedQr(latestQr);
          setProcessedQrCodes(new Set(processedQrCodesRef.current));

          processOutsideQrCodeRef.current?.(simulatedQr);
          resetScanRef.current?.();
          return;
        }
      }
    }

    // ✅ Shortcut: {2fic} open manual lot confirmation modal
    if (latestQr === "{2fic}") {
      setManualLotConfirmationOpen(true);
      resetScanRef.current?.();
      lastProcessedQrRef.current = latestQr;
      processedQrCodesRef.current.add(latestQr);
      setLastProcessedQr(latestQr);
      setProcessedQrCodes(new Set(processedQrCodesRef.current));
      return;
    }

    // Skip processing if manual modal open for same QR
    if (manualLotConfirmationOpen) {
      if (latestQr === lastProcessedQrRef.current) return;
    }

    // Skip if already processed (refs)
    if (
      processedQrCodesRef.current.has(latestQr) ||
      lastProcessedQrRef.current === latestQr
    )
      return;

    // Mark processed immediately
    lastProcessedQrRef.current = latestQr;
    processedQrCodesRef.current.add(latestQr);
    if (processedQrCodesRef.current.size > 100) {
      const firstValue = processedQrCodesRef.current.values().next().value;
      if (firstValue !== undefined)
        processedQrCodesRef.current.delete(firstValue);
    }

    // Process immediately
    if (qrProcessingTimeoutRef.current) {
      clearTimeout(qrProcessingTimeoutRef.current);
      qrProcessingTimeoutRef.current = null;
    }

    processOutsideQrCodeRef.current?.(latestQr);

    // UI state updates (non-blocking)
    startTransition(() => {
      setLastProcessedQr(latestQr);
      setProcessedQrCodes(new Set(processedQrCodesRef.current));
    });

    return () => {
      if (qrProcessingTimeoutRef.current) {
        clearTimeout(qrProcessingTimeoutRef.current);
        qrProcessingTimeoutRef.current = null;
      }
    };
  }, [
    qrValues.length,
    isManualScanning,
    isRefreshingData,
    combinedLotData.length,
    manualLotConfirmationOpen,
    isConfirmingLot,
  ]);

  const handlePickQtyChange = useCallback(
    (lotKey: string, value: number | string) => {
      if (value === "" || value === null || value === undefined) {
        setPickQtyData((prev) => ({
          ...prev,
          [lotKey]: 0,
        }));
        return;
      }

      const numericValue =
        typeof value === "string" ? parseFloat(value) : value;

      if (isNaN(numericValue)) {
        setPickQtyData((prev) => ({
          ...prev,
          [lotKey]: 0,
        }));
        return;
      }

      setPickQtyData((prev) => ({
        ...prev,
        [lotKey]: numericValue,
      }));
    },
    [],
  );

  const checkAndAutoAssignNext = useCallback(async () => {
    if (!currentUserId) return;

    try {
      const completionResponse = await checkPickOrderCompletion(currentUserId);

      if (
        completionResponse.code === "SUCCESS" &&
        completionResponse.entity?.hasCompletedOrders
      ) {
        console.log("Found completed pick orders, auto-assigning next...");
        //  移除前端的自动分配逻辑，因为后端已经处理了
        // await handleAutoAssignAndRelease(); // 删除这个函数
      }
    } catch (error) {
      console.error("Error checking pick order completion:", error);
    }
  }, [currentUserId]);

  const handleSubmitPickQtyWithQty = useCallback(
    async (
      lot: any,
      submitQty: number,
      source: "justComplete" | "singleSubmit",
    ) => {
      if (!lot.stockOutLineId) {
        console.error("No stock out line found for this lot");
        return;
      }
      const solId = Number(lot.stockOutLineId);
      if (solId > 0 && actionBusyBySolId[solId]) {
        console.warn("Action already in progress for stockOutLineId:", solId);
        return;
      }

      const pickOrderIdForRefresh = filterArgs?.pickOrderId
        ? Number(filterArgs.pickOrderId)
        : undefined;

      try {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: true }));
        const targetUnavailable = isInventoryLotLineUnavailable(lot);
        const effectiveSubmitQty =
          targetUnavailable && submitQty > 0 ? 0 : submitQty;

        const canonicalLotForSol =
          solId > 0
            ? combinedLotData.find((r) => Number(r.stockOutLineId) === solId) ??
              lot
            : lot;

        if (workbenchMode && source === "justComplete") {
          const solIdForOverride =
            Number(canonicalLotForSol.stockOutLineId) || 0;
          const lotIdForOverride = canonicalLotForSol.lotId;
          const lotKeyForOverride =
            Number.isFinite(solIdForOverride) && solIdForOverride > 0
              ? `sol:${solIdForOverride}`
              : `${canonicalLotForSol.pickOrderLineId}-${lotIdForOverride}`;
          const hasExplicitSubmitOverride =
            Object.prototype.hasOwnProperty.call(
              pickQtyData,
              lotKeyForOverride,
            );
          const explicitSubmitOverride = hasExplicitSubmitOverride
            ? Number(pickQtyData[lotKeyForOverride])
            : NaN;

          const qtyPayload = workbenchScanPickQtyFromLot(canonicalLotForSol);
          const wbJustQty = qtyPayload.qty;
          const isUnavailableForJustComplete =
            isInventoryLotLineUnavailable(canonicalLotForSol);
          const isNoLotForJustComplete =
            canonicalLotForSol.noLot === true ||
            !String(canonicalLotForSol.lotNo ?? "").trim();
          const canPostScanPick =
            // unavailable lot: Just Completed must always submit qty=0, even without lotNo
            isUnavailableForJustComplete ||
            isLotAvailabilityExpired(canonicalLotForSol) ||
            // noLot row: Just Completed always submit qty=0
            isNoLotForJustComplete ||
            (canonicalLotForSol.lotNo &&
              String(canonicalLotForSol.lotNo).trim() !== "" &&
              ((hasExplicitSubmitOverride &&
                Number.isFinite(explicitSubmitOverride) &&
                explicitSubmitOverride === 0) ||
                (wbJustQty != null && wbJustQty > 0)));

          if (canPostScanPick) {
            const qtyToSend =
              isUnavailableForJustComplete
                ? 0
                : isLotAvailabilityExpired(canonicalLotForSol)
                ? 0
                : isNoLotForJustComplete
                ? 0
                : hasExplicitSubmitOverride && explicitSubmitOverride === 0
                ? 0
                : Number(wbJustQty);
            const res = await workbenchScanPick({
              stockOutLineId: Number(canonicalLotForSol.stockOutLineId),
              lotNo: String(canonicalLotForSol.lotNo).trim(),
              ...(typeof canonicalLotForSol.stockInLineId === "number" &&
              Number.isFinite(canonicalLotForSol.stockInLineId) &&
              canonicalLotForSol.stockInLineId > 0
                ? { stockInLineId: canonicalLotForSol.stockInLineId }
                : {}),
              qty: qtyToSend,
              storeId: workbenchStoreId,
              userId: currentUserId ?? 1,
            });
            const scanOk = res.code === "SUCCESS";
            if (!scanOk) {
              rememberWorkbenchScanReject(
                Number(canonicalLotForSol.stockOutLineId),
                (res as { message?: string })?.message,
              );
              throw new Error(
                (res as { message?: string })?.message ||
                  "Workbench scan-pick failed",
              );
            }
            clearWorkbenchScanReject(Number(canonicalLotForSol.stockOutLineId));
            setPickQtyData((prev) => {
              if (!Object.prototype.hasOwnProperty.call(prev, lotKeyForOverride))
                return prev;
              const next = { ...prev };
              delete next[lotKeyForOverride];
              return next;
            });
            await refreshWorkbenchAfterScanPick();
            setTimeout(() => {
              checkAndAutoAssignNext();
            }, 1000);
            console.log(
              "Just Completed (workbench): workbenchScanPick posted without QR.",
            );
            return;
          }
          const justCompleteErr = t(
            "Just Completed (workbench): requires valid quantity; expired rows must not use this button.",
          );
          if (solId > 0) {
            rememberWorkbenchScanReject(solId, justCompleteErr);
          }
          setQrScanErrorMsg(justCompleteErr);
          throw new Error(justCompleteErr);
        }

        if (effectiveSubmitQty === 0 && source === "singleSubmit") {
          console.log(`=== SUBMITTING ALL ZEROS CASE ===`);
          console.log(`Lot: ${lot.lotNo}`);
          console.log(`Stock Out Line ID: ${lot.stockOutLineId}`);

          if (workbenchMode) {
            const res = await workbenchScanPick({
              stockOutLineId: Number(lot.stockOutLineId),
              lotNo: String(lot.lotNo ?? "").trim(),
              ...(typeof lot.stockInLineId === "number" &&
              Number.isFinite(lot.stockInLineId) &&
              lot.stockInLineId > 0
                ? { stockInLineId: lot.stockInLineId }
                : {}),
              qty: 0,
              storeId: workbenchStoreId,
              userId: currentUserId ?? 1,
            });
            const scanOk = res.code === "SUCCESS";
            if (!scanOk) {
              rememberWorkbenchScanReject(
                Number(lot.stockOutLineId),
                (res as { message?: string })?.message,
              );
              throw new Error(
                (res as { message?: string })?.message ||
                  "Workbench scan-pick failed (qty=0)",
              );
            }
            clearWorkbenchScanReject(Number(lot.stockOutLineId));
            await refreshWorkbenchAfterScanPick();
            setTimeout(() => {
              checkAndAutoAssignNext();
            }, 1000);
            return;
          }

          throw new Error("Unsupported legacy checked flow on workbench page");
        }

        if (
          workbenchMode &&
          effectiveSubmitQty > 0 &&
          lot.lotNo &&
          String(lot.lotNo).trim() !== "" &&
          !isLotAvailabilityExpired(lot) &&
          !isInventoryLotLineUnavailable(lot)
        ) {
          const res = await workbenchScanPick({
            stockOutLineId: Number(lot.stockOutLineId),
            lotNo: String(lot.lotNo).trim(),
            ...(typeof lot.stockInLineId === "number" &&
            Number.isFinite(lot.stockInLineId) &&
            lot.stockInLineId > 0
              ? { stockInLineId: lot.stockInLineId }
              : {}),
            qty: Number(effectiveSubmitQty),
            storeId: workbenchStoreId,
            userId: currentUserId ?? 1,
          });
          const scanOk = res.code === "SUCCESS";
          if (!scanOk) {
            rememberWorkbenchScanReject(
              Number(lot.stockOutLineId),
              (res as { message?: string })?.message,
            );
            throw new Error(
              (res as { message?: string })?.message ||
                "Workbench scan-pick failed",
            );
          }
          clearWorkbenchScanReject(Number(lot.stockOutLineId));
          const successLotKey = getWorkbenchQtyLotKey(lot);
          setPickQtyData((prev) => {
            if (!Object.prototype.hasOwnProperty.call(prev, successLotKey))
              return prev;
            const next = { ...prev };
            delete next[successLotKey];
            return next;
          });
          await refreshWorkbenchAfterScanPick();
          setTimeout(() => {
            checkAndAutoAssignNext();
          }, 1000);
          return;
        }

        const currentActualPickQty = lot.actualPickQty || 0;
        const cumulativeQty = currentActualPickQty + effectiveSubmitQty;

        let newStatus = "partially_completed";

        if (cumulativeQty >= lot.requiredQty) {
          newStatus = "completed";
        } else if (cumulativeQty > 0) {
          newStatus = "partially_completed";
        } else {
          newStatus = "pending";
        }

        console.log(`=== PICK QUANTITY SUBMISSION DEBUG ===`);
        console.log(`Lot: ${lot.lotNo}`);
        console.log(`Required Qty: ${lot.requiredQty}`);
        console.log(`Current Actual Pick Qty: ${currentActualPickQty}`);
        console.log(`New Submitted Qty: ${effectiveSubmitQty}`);
        console.log(`Cumulative Qty: ${cumulativeQty}`);
        console.log(`New Status: ${newStatus}`);
        console.log(`=====================================`);

        if (!workbenchMode) {
          await updateStockOutLineStatus({
            id: lot.stockOutLineId,
            status: newStatus,
            qty: effectiveSubmitQty,
          });
          applyLocalStockOutLineUpdate(
            Number(lot.stockOutLineId),
            newStatus,
            cumulativeQty,
          );
        }

        // Workbench completion is handled in backend scan-pick flow.

        void fetchJobOrderData(pickOrderIdForRefresh);
        console.log("Pick quantity submitted successfully!");

        setTimeout(() => {
          checkAndAutoAssignNext();
        }, 1000);
      } catch (error) {
        console.error("Error submitting pick quantity:", error);
        setQrScanError(true);
        setQrScanSuccess(false);
      } finally {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: false }));
      }
    },
    [
      fetchJobOrderData,
      checkAndAutoAssignNext,
      actionBusyBySolId,
      applyLocalStockOutLineUpdate,
      workbenchMode,
      currentUserId,
      rememberWorkbenchScanReject,
      clearWorkbenchScanReject,
      refreshWorkbenchAfterScanPick,
      combinedLotData,
      workbenchScanPickQtyFromLot,
      pickQtyData,
      tPick,
      workbenchStoreId,
      filterArgs?.pickOrderId,
    ],
  );

  const handleSkip = useCallback(
    async (lot: any) => {
      try {
        console.log(
          "Just Complete clicked (workbench: scan-pick without QR when possible):",
          lot.lotNo,
        );
        await handleSubmitPickQtyWithQty(lot, 0, "justComplete");
      } catch (err) {
        console.error("Error in Skip:", err);
      }
    },
    [handleSubmitPickQtyWithQty],
  );

  const hasPendingBatchSubmit = useMemo(() => {
    return combinedLotData.some((lot) => {
      const status = String(lot.stockOutLineStatus || "").toLowerCase();
      return (
        status === "pending" ||
        status === "partially_completed" ||
        status === "partially_complete"
      );
    });
  }, [combinedLotData]);

  useEffect(() => {
    if (!hasPendingBatchSubmit) return;
    const handler = (event: BeforeUnloadEvent) => {
      event.preventDefault();
      event.returnValue = "";
    };
    window.addEventListener("beforeunload", handler);
    return () => window.removeEventListener("beforeunload", handler);
  }, [hasPendingBatchSubmit]);

  const handleSubmitAllScanned = useCallback(async () => {
    const startTime = performance.now();
    console.log(`  [BATCH SUBMIT START]`);
    console.log(`  Start time: ${new Date().toISOString()}`);

    const scannedLots = combinedLotData.filter((lot) => {
      const status = lot.stockOutLineStatus;
      const statusLower = String(status || "").toLowerCase();
      if (statusLower === "completed" || statusLower === "complete") {
        return false;
      }
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      ) {
        return true;
      }
      return false;
    });

    if (scannedLots.length === 0) {
      console.log("No scanned items to submit");
      return;
    }

    setIsSubmittingAll(true);
    console.log(
      `📦 Submitting ${scannedLots.length} items using workbench batch scan-pick (qty=0)...`,
    );

    try {
      const submitStartTime = performance.now();

      const result = await workbenchBatchScanPick({
        lines: scannedLots.map((lot) => ({
          stockOutLineId: Number(lot.stockOutLineId) || 0,
          lotNo: "",
          qty: 0,
          storeId: workbenchStoreId,
          userId: currentUserId ?? 1,
        })),
      });

      const submitTime = performance.now() - submitStartTime;
      console.log(
        `  Batch submit API call completed in ${submitTime.toFixed(2)}ms (${(
          submitTime / 1000
        ).toFixed(3)}s)`,
      );
      console.log(`📥 Batch submit result:`, result);

      const refreshStartTime = performance.now();
      const pickOrderId = filterArgs?.pickOrderId
        ? Number(filterArgs.pickOrderId)
        : undefined;
      await fetchJobOrderData(pickOrderId);
      const refreshTime = performance.now() - refreshStartTime;
      console.log(
        `  Data refresh time: ${refreshTime.toFixed(2)}ms (${(
          refreshTime / 1000
        ).toFixed(3)}s)`,
      );

      const totalTime = performance.now() - startTime;
      console.log(`  [BATCH SUBMIT END]`);
      console.log(
        `  Total time: ${totalTime.toFixed(2)}ms (${(totalTime / 1000).toFixed(
          3,
        )}s)`,
      );
      console.log(`  End time: ${new Date().toISOString()}`);

      if (result && result.code === "SUCCESS") {
        setQrScanSuccess(true);
        setTimeout(() => {
          setQrScanSuccess(false);
          checkAndAutoAssignNext();
          if (onBackToList) {
            onBackToList();
          }
        }, 2000);
      } else {
        console.error("Batch submit failed:", result);
        setQrScanError(true);
      }
    } catch (error) {
      console.error("Error submitting all scanned items:", error);
      setQrScanError(true);
    } finally {
      setIsSubmittingAll(false);
    }
  }, [
    combinedLotData,
    fetchJobOrderData,
    checkAndAutoAssignNext,
    currentUserId,
    onBackToList,
    workbenchStoreId,
    filterArgs?.pickOrderId,
  ]);

  const scannedItemsCount = useMemo(() => {
    const filtered = combinedLotData.filter((lot) => {
      const status = lot.stockOutLineStatus;
      const statusLower = String(status || "").toLowerCase();
      if (statusLower === "completed" || statusLower === "complete") {
        return false;
      }
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      ) {
        return true;
      }
      return false;
    });

    const noLotCount = filtered.filter((l) => l.noLot === true).length;
    const normalCount = filtered.filter((l) => l.noLot !== true).length;
    console.log(
      `📊 scannedItemsCount calculation: total=${filtered.length}, noLot=${noLotCount}, normal=${normalCount}`,
    );
    console.log(`📊 All items breakdown:`, {
      total: combinedLotData.length,
      noLot: combinedLotData.filter((l) => l.noLot === true).length,
      normal: combinedLotData.filter((l) => l.noLot !== true).length,
    });

    return filtered.length;
  }, [combinedLotData]);

  // 先定义 filteredByFloor 和 availableFloors
  const availableFloors = useMemo(() => {
    const floors = new Set<string>();
    combinedLotData.forEach((lot) => {
      const f = extractFloor(lot);
      if (f) floors.add(f);
    });
    return Array.from(floors).sort(
      (a, b) => floorSortOrder(b) - floorSortOrder(a),
    );
  }, [combinedLotData]);

  const filteredByFloor = useMemo(() => {
    if (!selectedFloor) return combinedLotData;
    return combinedLotData.filter((lot) => extractFloor(lot) === selectedFloor);
  }, [combinedLotData, selectedFloor]);

  // 與批量篩選一致：noLot / 過期 的 pending 也算已處理（對齊 GoodPickExecutiondetail）
  const progress = useMemo(() => {
    const data = selectedFloor ? filteredByFloor : combinedLotData;
    if (data.length === 0) return { completed: 0, total: 0 };
    const nonPendingCount = data.filter((lot) => {
      const status = lot.stockOutLineStatus?.toLowerCase();
      if (status !== "pending") return true;
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      )
        return true;
      return false;
    }).length;
    return { completed: nonPendingCount, total: data.length };
  }, [selectedFloor, filteredByFloor, combinedLotData]);
  //  Handle reject lot
  const handleRejectLot = useCallback(
    async (lot: any) => {
      if (!lot.stockOutLineId) {
        console.error("No stock out line found for this lot");
        return;
      }

      try {
        await updateStockOutLineStatus({
          id: lot.stockOutLineId,
          status: "rejected",
          qty: 0,
        });

        const pickOrderId = filterArgs?.pickOrderId
          ? Number(filterArgs.pickOrderId)
          : undefined;
        await fetchJobOrderData(pickOrderId);
        console.log("Lot rejected successfully!");

        setTimeout(() => {
          checkAndAutoAssignNext();
        }, 1000);
      } catch (error) {
        console.error("Error rejecting lot:", error);
      }
    },
    [fetchJobOrderData, checkAndAutoAssignNext],
  );

  //  Handle pick execution form
  const handlePickExecutionForm = useCallback((lot: any) => {
    console.log("=== Pick Execution Form ===");
    console.log("Lot data:", lot);

    if (!lot) {
      console.warn("No lot data provided for pick execution form");
      return;
    }

    console.log("Opening pick execution form for lot:", lot.lotNo);

    setSelectedLotForExecutionForm(lot);
    setPickExecutionFormOpen(true);

    console.log("Pick execution form opened for lot ID:", lot.lotId);
  }, []);


  //  Calculate remaining required quantity
  const calculateRemainingRequiredQty = useCallback((lot: any) => {
    const requiredQty = lot.requiredQty || 0;
    const stockOutLineQty = lot.stockOutLineQty || 0;
    return Math.max(0, requiredQty - stockOutLineQty);
  }, []);



  const handlePageChange = useCallback((event: unknown, newPage: number) => {
    setPaginationController((prev) => ({
      ...prev,
      pageNum: newPage,
    }));
  }, []);

  const handlePageSizeChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      const newPageSize = parseInt(event.target.value, 10);
      setPaginationController({
        pageNum: 0,
        pageSize: newPageSize,
      });
    },
    [],
  );

  // Pagination data: align DO workbench grouping display
  const paginatedData = useMemo(() => {
    const sourceData = selectedFloor ? filteredByFloor : combinedLotData;

    /** 同 pick_order_line 內「無庫位 / no-lot」列 extractFloor 為空，若仍用 0 會被排到全表最後，編號欄也變成新群組。繼承該行最大樓層權重並以 polId 相鄰排序。 */
    const lineMaxFloorOrder = new Map<number, number>();
    for (const lot of sourceData) {
      const polId = Number(lot.pickOrderLineId);
      if (!Number.isFinite(polId) || polId <= 0) continue;
      const o = floorSortOrder(extractFloor(lot));
      const prev = lineMaxFloorOrder.get(polId) ?? 0;
      if (o > prev) lineMaxFloorOrder.set(polId, o);
    }
    const effectiveFloorOrder = (lot: any): number => {
      const own = floorSortOrder(extractFloor(lot));
      if (own > 0) return own;
      const polId = Number(lot.pickOrderLineId);
      if (Number.isFinite(polId) && polId > 0) {
        const inherited = lineMaxFloorOrder.get(polId);
        if (inherited != null && inherited > 0) return inherited;
      }
      return 0;
    };
    const isNoLotTailRow = (lot: any) =>
      lot.noLot === true || lot.lotId == null || lot.lotId === undefined;

    const sortedData = [...sourceData].sort((a, b) => {
      const efA = effectiveFloorOrder(a);
      const efB = effectiveFloorOrder(b);
      if (efA !== efB) return efB - efA; // 4F, 3F, 2F（含繼承樓層的缺口列）

      const polA = Number(a.pickOrderLineId);
      const polB = Number(b.pickOrderLineId);
      const hasPolA = Number.isFinite(polA) && polA > 0;
      const hasPolB = Number.isFinite(polB) && polB > 0;
      if (hasPolA && hasPolB && polA !== polB) return polA - polB;

      const aItem = String(a.itemCode || "");
      const bItem = String(b.itemCode || "");
      if (aItem !== bItem) return aItem.localeCompare(bItem);

      const aName = String(a.itemName || "");
      const bName = String(b.itemName || "");
      if (aName !== bName) return aName.localeCompare(bName);

      const tailA = isNoLotTailRow(a) ? 1 : 0;
      const tailB = isNoLotTailRow(b) ? 1 : 0;
      if (tailA !== tailB) return tailA - tailB;

      const aIndex = Number(a.routerIndex ?? 0);
      const bIndex = Number(b.routerIndex ?? 0);
      if (aIndex !== bIndex) return aIndex - bIndex;

      return (a.lotNo || "").localeCompare(b.lotNo || "");
    });

    const flattened = sortedData.map((lot, idx, arr) => {
      const key = `${lot.itemId ?? "null"}-${lot.itemCode ?? ""}`;
      const prev = idx > 0 ? arr[idx - 1] : undefined;
      const prevKey = prev
        ? `${prev.itemId ?? "null"}-${prev.itemCode ?? ""}`
        : null;
      const isGroupFirst = key !== prevKey;
      const groupDisplayIndex =
        arr
          .slice(0, idx + 1)
          .filter((row, rowIdx, all) => {
            const rowKey = `${row.itemId ?? "null"}-${row.itemCode ?? ""}`;
            const before = rowIdx > 0 ? all[rowIdx - 1] : undefined;
            const beforeKey = before
              ? `${before.itemId ?? "null"}-${before.itemCode ?? ""}`
              : null;
            return rowKey !== beforeKey;
          }).length;
      return { lot, isGroupFirst, groupDisplayIndex };
    });

    const startIndex =
      paginationController.pageNum * paginationController.pageSize;
    const endIndex = startIndex + paginationController.pageSize;
    return flattened.slice(startIndex, endIndex);
  }, [selectedFloor, filteredByFloor, combinedLotData, paginationController]);

  //  Add these functions for manual scanning
  const handleStartScan = useCallback(() => {
    console.log(" Starting manual QR scan...");
    setIsManualScanning(true);
    setProcessedQrCodes(new Set());
    setLastProcessedQr("");
    setQrScanError(false);
    setQrScanSuccess(false);
    startScan();
  }, [startScan]);

  const handleStopScan = useCallback(() => {
    console.log(" Stopping manual QR scan...");
    setIsManualScanning(false);
    setQrScanError(false);
    setQrScanSuccess(false);
    stopScan();
    resetScan();
  }, [stopScan, resetScan]);
  useEffect(() => {
    return () => {
      // Cleanup when component unmounts (e.g., when switching tabs)
      if (isManualScanning) {
        console.log("🧹 Component unmounting, stopping QR scanner...");
        stopScan();
        resetScan();
      }
    };
  }, [isManualScanning, stopScan, resetScan]);
  // 勿在 combinedLotData 仍為空時自動停掃：API 未回傳前會誤觸，與 GoodPickExecutiondetail（已註解掉同段）一致。
  // 無資料時 qrValues effect 本來就不會處理掃碼；真正無單據可再手動按停止。

  const getStatusMessage = useCallback(
    (lot: any) => {
      if (
        lot?.noLot === true ||
        lot?.lotAvailability === "insufficient_stock"
      ) {
        return t("This order is insufficient, please pick another lot.");
      }
      switch (lot.stockOutLineStatus?.toLowerCase()) {
        case "pending":
          return t("Please finish QR code scan and pick order.");
        case "checked":
          return t("Please submit the pick order.");
        case "partially_completed":
          return t(
            "Partial quantity submitted. Please submit more or complete the order.",
          );
        case "completed":
          return t("Pick order completed successfully!");
        case "rejected":
          return t("Lot has been rejected and marked as unavailable.");
        case "unavailable":
          return t("This order is insufficient, please pick another lot.");
        default:
          return t("Please finish QR code scan and pick order.");
      }
    },
    [t],
  );

  return (
    <TestQrCodeProvider
      lotData={combinedLotData}
      onScanLot={handleQrCodeSubmit}
      filterActive={(lot) =>
        lot.lotAvailability !== "rejected" &&
        lot.stockOutLineStatus !== "rejected" &&
        lot.stockOutLineStatus !== "completed"
      }
    >
      <FormProvider {...formProps}>
        <Stack spacing={2}>
          {/* Progress bar + scan status fixed at top */}
          <Box
            sx={{
              position: "fixed",
              top: 0,
              left: 0,
              right: 0,
              zIndex: 1100,
              backgroundColor: "background.paper",
              pt: 2,
              pb: 1,
              px: 2,
              borderBottom: "1px solid",
              borderColor: "divider",
              boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
            }}
          >
            <LinearProgressWithLabel
              completed={progress.completed}
              total={progress.total}
              label={t("Progress")}
            />
            <ScanStatusAlert
              error={qrScanError}
              success={qrScanSuccess}
              errorMessage={
                qrScanErrorMsg ||
                t("QR code does not match any item in current orders.")
              }
              successMessage={t("QR code verified.")}
            />
          </Box>
          <Box
            sx={{
              display: "flex",
              gap: 1,
              alignItems: "center",
              flexWrap: "wrap",
            }}
          >
            <Button
              variant={selectedFloor === null ? "contained" : "outlined"}
              size="small"
              onClick={() => setSelectedFloor(null)}
            >
              {t("All")}
            </Button>
            {availableFloors.map((floor) => (
              <Button
                key={floor}
                variant={selectedFloor === floor ? "contained" : "outlined"}
                size="small"
                onClick={() => setSelectedFloor(floor)}
              >
                {floor}
              </Button>
            ))}
          </Box>
          <Box sx={{ display: "flex", gap: 1, alignItems: "center", flexWrap: "wrap" }}>
          
            
            <Typography variant="body2" sx={{ minWidth: "fit-content", mr: 1 }}>
              {t("Select Printer")}:
            </Typography>
            <Autocomplete
              options={printerOptions}
              getOptionLabel={(option) =>
                option.name || option.label || option.code || `Printer ${option.id}`
              }
              value={selectedPrinter}
              onChange={(_, newValue) => setSelectedPrinter(newValue)}
              sx={{ minWidth: 220 }}
              size="small"
              renderInput={(params) => (
                <TextField
                  {...params}
                  placeholder={t("Printer")}
                  inputProps={{ ...params.inputProps, readOnly: true }}
                />
              )}
            />
            <Typography variant="body2" sx={{ minWidth: "fit-content", ml: 1 }}>
              {t("Print Quantity")}:
            </Typography>
            <TextField
              type="number"
              label={t("Print Quantity")}
              value={printQty}
              onChange={(e) => {
                const value = parseInt(e.target.value) || 1;
                setPrintQty(Math.max(1, value));
              }}
              inputProps={{ min: 1, step: 1 }}
              sx={{ width: 120 }}
              size="small"
            />

            <Button
              variant="contained"
              color="primary"
              size="small"
              onClick={() => handlePickRecord("ALL")}
            >
              {t("Print Pick Record")} ALL
            </Button>
            <Button
              variant="contained"
              color="primary"
              size="small"
              onClick={() => handlePickRecord("2F")}
            >
              {t("Print Pick Record")} 2F
            </Button>
            <Button
              variant="contained"
              color="primary"
              size="small"
              onClick={() => handlePickRecord("3F")}
            >
              {t("Print Pick Record")} 3F
            </Button>
            <Button
              variant="contained"
              color="primary"
              size="small"
              onClick={() => handlePickRecord("4F")}
            >
              {t("Print Pick Record")} 4F
            </Button>
            {isPrinterComboMissing && (
              <Alert severity="warning" sx={{ py: 0, px: 1 }}>
                {t("Printer list is empty")}
              </Alert>
            )}
          </Box>
          {/* Job Order Header */}
          {jobOrderData && (
            <Paper sx={{ p: 2 }}>
              <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap">
              <Typography variant="subtitle1">
                  <strong>{t("Item Name")}:</strong>{" "}
                  {jobOrderData.pickOrder.jobOrder.itemCode || "-"}{" "}{jobOrderData.pickOrder.jobOrder.itemName || "-"}
                </Typography>
                <Typography variant="subtitle1">
                  <strong>{t("Job Order")}:</strong>{" "}
                  {jobOrderData.pickOrder?.jobOrder?.code || "-"}
                </Typography>
                <Typography variant="subtitle1">
                  <strong>{t("Pick Order Code")}:</strong>{" "}
                  {jobOrderData.pickOrder?.code || "-"}
                </Typography>
                <Typography variant="subtitle1">
                  <strong>{t("Target Date")}:</strong>{" "}
                  {jobOrderData.pickOrder?.targetDate || "-"}
                </Typography>
              </Stack>
            </Paper>
          )}

          {/* Combined Lot Table */}
          <Box sx={{ mt: 10 }}>
            <Box
              sx={{
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                mb: 2,
              }}
            >
              <Box sx={{ display: "flex", gap: 2, alignItems: "center" }}>
                {!isManualScanning ? (
                  <Button
                    variant="contained"
                    startIcon={<QrCodeIcon />}
                    onClick={handleStartScan}
                    color="primary"
                    sx={{ minWidth: "120px" }}
                  >
                    {t("Start QR Scan")}
                  </Button>
                ) : (
                  <Button
                    variant="outlined"
                    startIcon={<QrCodeIcon />}
                    onClick={handleStopScan}
                    color="secondary"
                    sx={{ minWidth: "120px" }}
                  >
                    {t("Stop QR Scan")}
                  </Button>
                )}
                {/*  ADD THIS: Submit All Scanned Button */}
                <Button
                  variant="contained"
                  color="success"
                  onClick={handleSubmitAllScanned}
                  disabled={scannedItemsCount === 0 || isSubmittingAll}
                  sx={{ minWidth: "160px" }}
                >
                  {isSubmittingAll ? (
                    <>
                      <CircularProgress size={16} sx={{ mr: 1 }} />
                      {t("Submitting...")}
                    </>
                  ) : (
                    `${t("Submit All Scanned")} (${scannedItemsCount})`
                  )}
                </Button>
              </Box>
            </Box>

            <TableContainer component={Paper}>
              <Table>
                <TableHead>
                  <TableRow>
                    <TableCell>{t("Index")}</TableCell>
                    <TableCell>{t("Item Code")}</TableCell>
                    <TableCell>{t("Route")}</TableCell>
                    <TableCell>{t("Handler")}</TableCell>

                    <TableCell>{t("Lot No")}</TableCell>
                    <TableCell align="right">
                      {t("Lot Required Pick Qty")}
                    </TableCell>
                    <TableCell align="right">{t("Available Qty")}</TableCell>
                    <TableCell align="center">{t("Scan Result")}</TableCell>
                    <TableCell align="center">{t("Qty will submit")}</TableCell>
                    <TableCell align="center">
                      {t("Submit Required Pick Qty")}
                    </TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {paginatedData.length === 0 ? (
                    <TableRow>
                      <TableCell colSpan={11} align="center">
                        <Typography variant="body2" color="text.secondary">
                          {t("No data available")}
                        </Typography>
                      </TableCell>
                    </TableRow>
                  ) : (
                    paginatedData.map((row) => {
                      const lot = row.lot;
                      const solIdForKey = Number(lot.stockOutLineId) || 0;
                      const lotKeyForSubmitQty =
                        Number.isFinite(solIdForKey) && solIdForKey > 0
                          ? `sol:${solIdForKey}`
                          : `${lot.pickOrderLineId}-${lot.lotId}`;
                      const submitQtyStatus = String(
                        lot.stockOutLineStatus || "",
                      ).toLowerCase();
                      const isSubmitQtyCompleted =
                        submitQtyStatus === "completed" ||
                        submitQtyStatus === "partially_completed" ||
                        submitQtyStatus === "partially_complete";
                      const lockedSubmitQtyDisplay =
                        isInventoryLotLineUnavailable(lot) &&
                        !isSubmitQtyCompleted
                          ? 0
                          : resolveSingleSubmitQty(lot);
                      const hasPickOverride =
                        Object.prototype.hasOwnProperty.call(
                          pickQtyData,
                          lotKeyForSubmitQty,
                        );
                      const fromPickRow = hasPickOverride
                        ? pickQtyData[lotKeyForSubmitQty]
                        : undefined;
                      const workbenchSubmitQtyDisplay =
                        hasPickOverride &&
                        fromPickRow !== undefined &&
                        fromPickRow !== null &&
                        !Number.isNaN(Number(fromPickRow))
                          ? Number(fromPickRow)
                          : lockedSubmitQtyDisplay;
                          const totalAvail = Number(lot.itemTotalAvailableQty ?? 0);
                          const isLastLotUnavailable = Number.isFinite(totalAvail) && totalAvail === 0;
                      return (
                        <TableRow
                          key={`${lot.pickOrderLineId}-${lot.lotId}`}
                          sx={{
                            // backgroundColor: lot.lotAvailability === 'rejected' ? 'grey.100' : 'inherit',
                            //opacity: lot.lotAvailability === 'rejected' ? 0.6 : 1,
                            "& .MuiTableCell-root": {
                              // color: lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit'
                            },
                          }}
                        >
                          <TableCell>
                            <Typography variant="body2" fontWeight="bold">
                              {row.isGroupFirst ? row.groupDisplayIndex : ""}
                            </Typography>
                          </TableCell>
                          <TableCell>
                          {row.isGroupFirst ? (
                            <>
                              {lot.itemCode} <br />
                              {lot.itemName} <br />
                              {lot.uomDesc}
                            </>
                          ) : ""}
                        </TableCell>

                          <TableCell>
                            <Typography variant="body2">
                              {lot.routerRoute || "-"}
                            </Typography>
                          </TableCell>
                          <TableCell>{lot.handler || "-"}</TableCell>

                          <TableCell>
                            <Stack
                              direction="row"
                              spacing={1}
                              alignItems="center"
                              justifyContent="space-between"
                            >
                            <Box>
                              <Typography
                                sx={{
                                  color:
                                    isInventoryLotLineUnavailable(lot) &&
                                    !(
                                      String(
                                        lot.stockOutLineStatus || "",
                                      ).toLowerCase() === "completed" ||
                                      String(
                                        lot.stockOutLineStatus || "",
                                      ).toLowerCase() ===
                                        "partially_completed" ||
                                      String(
                                        lot.stockOutLineStatus || "",
                                      ).toLowerCase() ===
                                        "partially_complete"
                                    )
                                    ? "error.main"
                                    : lot.lotAvailability === "expired"
                                      ? "warning.main"
                                      : "inherit",
                                }}
                              >
                                {lot.lotNo ? (
                                  /*
                                 
                                                   */
                                    lot.lotAvailability === "expired" ? (
                                      <>
                                        {lot.lotNo}{" "}
                                        <Box
                                          component="span"
                                          sx={{ fontSize: "0.85rem", lineHeight: 1.4 }}
                                        >
                                          {t(
                                            "is expired. Please check around have available QR code or not.",
                                          )}
                                        
                                        </Box>
                                        {isLastLotUnavailable && (
                                            <Box
                                              component="span"
                                              sx={{ fontSize: "0.85rem", lineHeight: 1.4 }}
                                            >
                                              {t("This is last lot, so no available lot.")}
                                            </Box>
                                          )}
                                      </>
                                  ) :  isInventoryLotLineUnavailable(lot) &&
                                  !(
                                    String(
                                      lot.stockOutLineStatus || "",
                                    ).toLowerCase() === "completed" ||
                                    String(
                                      lot.stockOutLineStatus || "",
                                    ).toLowerCase() === "partially_completed" ||
                                    String(
                                      lot.stockOutLineStatus || "",
                                    ).toLowerCase() === "partially_complete"
                                  ) ? (
                                    <>
                                      {lot.lotNo}{" "}
                                      <Box
                                        component="span"
                                        sx={{ fontSize: "0.85rem", lineHeight: 1.4 }}
                                      >
                                        {t(
                                          "is unavable. Please check around have available QR code or not.",
                                        )}
                                      </Box>
                                    </>
                     
                                  ) : (
                                    lot.lotNo
                                  )
                                ) : (
                                  <Box
                                    component="span"
                                    sx={{ fontSize: "0.85rem", lineHeight: 1.4 }}
                                  >
                                    {t(
                                      "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.",
                                    )}
                                  </Box>
                                )}
                              </Typography>
                            </Box>
                            {Number(lot.stockOutLineId) > 0 &&
                            Number(lot.itemId) > 0 ? (
                              <Button
                                variant="outlined"
                                size="small"
                                onClick={() =>
                                  openWorkbenchLotLabelModalForLot(lot)
                                }
                                disabled={
                                 
                                  (Number(lot.stockOutLineId) > 0 &&
                                    actionBusyBySolId[
                                      Number(lot.stockOutLineId)
                                    ] === true)
                                }
                                sx={{
                                  flexShrink: 0,
                                  fontSize: "0.7rem",
                                  py: 0.25,
                                  minWidth: "auto",
                                  px: 1,
                                  whiteSpace: "nowrap",
                                }}
                              >
                                {tPick("挑號 QR 碼")}
                              </Button>
                            ) : null}
                            </Stack>
                          </TableCell>
                          <TableCell align="right">
                            {(() => {
                              const requiredQty = lot.requiredQty || 0;
                              const unit =
                                lot.noLot === true || !lot.lotId
                                  ? lot.uomDesc || ""
                                  : lot.uomDesc || "";
                              return `${requiredQty.toLocaleString()}(${unit})`;
                            })()}
                          </TableCell>
                          <TableCell align="right">
                            {(() => {
                              const avail = lot.itemTotalAvailableQty;
                              if (avail == null) return "-";
                              const unit = lot.uomDesc || "";
                              return `${Number(
                                avail,
                              ).toLocaleString()}(${unit})`;
                            })()}
                          </TableCell>

                          <TableCell align="center">
                            {(() => {
                              const status =
                                lot.stockOutLineStatus?.toLowerCase();
                              const isRejected =
                                status === "rejected" ||
                                lot.lotAvailability === "rejected";
                              const isNoLot = !lot.lotNo;

                              if (isRejected && !isNoLot) {
                                return (
                                  <Box
                                    sx={{
                                      display: "flex",
                                      justifyContent: "center",
                                      alignItems: "center",
                                    }}
                                  >
                                    <Checkbox
                                      checked={true}
                                      disabled={true}
                                      readOnly={true}
                                      size="large"
                                      sx={{
                                        color: "error.main",
                                        "&.Mui-checked": {
                                          color: "error.main",
                                        },
                                        transform: "scale(1.3)",
                                      }}
                                    />
                                  </Box>
                                );
                              }

                              if (
                                isLotAvailabilityExpired(lot) &&
                                status !== "rejected"
                              ) {
                                return (
                                  <Box
                                    sx={{
                                      display: "flex",
                                      justifyContent: "center",
                                      alignItems: "center",
                                    }}
                                  >
                                    <Checkbox
                                      checked={true}
                                      disabled={true}
                                      readOnly={true}
                                      size="large"
                                      sx={{
                                        color: "warning.main",
                                        "&.Mui-checked": {
                                          color: "warning.main",
                                        },
                                        transform: "scale(1.3)",
                                      }}
                                    />
                                  </Box>
                                );
                              }

                              if (
                                !isNoLot &&
                                status !== "pending" &&
                                status !== "rejected"
                              ) {
                                return (
                                  <Box
                                    sx={{
                                      display: "flex",
                                      justifyContent: "center",
                                      alignItems: "center",
                                    }}
                                  >
                                    <Checkbox
                                      checked={true}
                                      disabled={true}
                                      readOnly={true}
                                      size="large"
                                      sx={{
                                        color: "success.main",
                                        "&.Mui-checked": {
                                          color: "success.main",
                                        },
                                        transform: "scale(1.3)",
                                      }}
                                    />
                                  </Box>
                                );
                              }

                              if (
                                isNoLot &&
                                (status === "partially_completed" ||
                                  status === "completed")
                              ) {
                                return (
                                  <Box
                                    sx={{
                                      display: "flex",
                                      justifyContent: "center",
                                      alignItems: "center",
                                    }}
                                  >
                                    <Checkbox
                                      checked={true}
                                      disabled={true}
                                      readOnly={true}
                                      size="large"
                                      sx={{
                                        color: "error.main",
                                        "&.Mui-checked": {
                                          color: "error.main",
                                        },
                                        transform: "scale(1.3)",
                                      }}
                                    />
                                  </Box>
                                );
                              }

                              return null;
                            })()}
                          </TableCell>

                          <TableCell align="center">
                            {workbenchSubmitQtyDisplay}
                          </TableCell>

                          <TableCell align="center">
                            <Box
                              sx={{ display: "flex", justifyContent: "center" }}
                            >
                              {(() => {
                                const status =
                                  lot.stockOutLineStatus?.toLowerCase();
                                const isRejected =
                                  status === "rejected" ||
                                  lot.lotAvailability === "rejected";
                                const isNoLot = !lot.lotNo;
                                const isUnavailableLot =
                                  isInventoryLotLineUnavailable(lot);

                                if (isRejected && !isNoLot) {
                                  const rejectDisplay = buildLotRejectDisplayMessage(
                                    lot,
                                    scanRejectMessageBySolId,
                                    tPick,
                                  );
                                  return (
                                    <Typography
                                      variant="body2"
                                      color="error.main"
                                      sx={{
                                        textAlign: "center",
                                        whiteSpace: "normal",
                                        wordBreak: "break-word",
                                        maxWidth: "200px",
                                        lineHeight: 1.5,
                                      }}
                                    >
                                      {rejectDisplay ??
                                        t(
                                          "This lot is rejected, please scan another lot.",
                                        )}
                                    </Typography>
                                  );
                                }

                                const lotKey = lotKeyForSubmitQty;
                                const qtyFieldEnabled =
                                  workbenchSubmitQtyFieldEnabledByLotKey[
                                    lotKey
                                  ] === true;
                                const displayedSubmitQty =
                                  workbenchSubmitQtyDisplay;
                                const hasPickOverrideRow =
                                  Object.prototype.hasOwnProperty.call(
                                    pickQtyData,
                                    lotKey,
                                  );
                                const textFieldValue = qtyFieldEnabled
                                  ? hasPickOverrideRow
                                    ? String(pickQtyData[lotKey])
                                    : String(displayedSubmitQty)
                                  : String(displayedSubmitQty);

                                return (
                                  <Stack
                                    direction="row"
                                    spacing={1}
                                    alignItems="center"
                                  >
                                    <TextField
                                      type="number"
                                      size="small"
                                      disabled={!qtyFieldEnabled}
                                      value={textFieldValue}
                                      onKeyDown={(e) => {
                                        if (!qtyFieldEnabled) return;
                                        if (e.key !== "{") return;
                                        e.preventDefault();
                                        setWorkbenchSubmitQtyFieldEnabledByLotKey(
                                          (prev) => ({
                                            ...prev,
                                            [lotKey]: false,
                                          }),
                                        );
                                        (
                                          e.currentTarget as HTMLInputElement
                                        ).blur();
                                      }}
                                      onChange={(e) => {
                                        if (!qtyFieldEnabled) return;
                                        handlePickQtyChange(
                                          lotKey,
                                          e.target.value,
                                        );
                                      }}
                                      inputProps={{ min: 0, step: 1 }}
                                      sx={{
                                        width: 96,
                                        "& .MuiInputBase-input": {
                                          fontSize: "0.75rem",
                                          py: 0.5,
                                          textAlign: "center",
                                        },
                                      }}
                                    />
                                    <Button
                                      variant="outlined"
                                      size="small"
                                      onClick={() => {
                                        setWorkbenchSubmitQtyFieldEnabledByLotKey(
                                          (prev) => ({
                                            ...prev,
                                            [lotKey]: !(prev[lotKey] === true),
                                          }),
                                        );
                                      }}
                                      disabled={
                                        lot.stockOutLineStatus ===
                                          "completed" ||
                                        (Number(lot.stockOutLineId) > 0 &&
                                          actionBusyBySolId[
                                            Number(lot.stockOutLineId)
                                          ] === true)
                                      }
                                      sx={{
                                        fontSize: "0.7rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "60px",
                                        borderColor: "warning.main",
                                        color: "warning.main",
                                      }}
                                      title={
                                        qtyFieldEnabled
                                          ? tPick("Lock quantity")
                                          : tPick("Edit quantity")
                                      }
                                    >
                                      {tPick("Edit")}
                                    </Button>
                                    <Button
                                      variant="outlined"
                                      size="small"
                                      onClick={async () => {
                                        const solId =
                                          Number(lot.stockOutLineId) || 0;
                                        if (solId > 0) {
                                          setActionBusyBySolId((prev) => ({
                                            ...prev,
                                            [solId]: true,
                                          }));
                                        }
                                        try {
                                          if (
                                            currentUserId &&
                                            lot.pickOrderId &&
                                            lot.itemId
                                          ) {
                                            try {
                                              await updateHandledBy(
                                                lot.pickOrderId,
                                                lot.itemId,
                                              );
                                            } catch (error) {
                                              console.error(
                                                "❌ Error updating handler (non-critical):",
                                                error,
                                              );
                                            }
                                          }
                                          await handleSubmitPickQtyWithQty(
                                            lot,
                                            0,
                                            "justComplete",
                                          );
                                        } finally {
                                          if (solId > 0) {
                                            setActionBusyBySolId((prev) => ({
                                              ...prev,
                                              [solId]: false,
                                            }));
                                          }
                                        }
                                      }}
                                      disabled={
                                        (Number(lot.stockOutLineId) > 0 &&
                                          actionBusyBySolId[
                                            Number(lot.stockOutLineId)
                                          ] === true) ||
                                        lot.stockOutLineStatus ===
                                          "completed" ||
                                        lot.stockOutLineStatus === "checked" ||
                                        lot.stockOutLineStatus ===
                                          "partially_completed" ||
                                        lot.stockOutLineStatus ===
                                          "partially_complete" ||
                                       // isUnavailableLot ||
                                        (Number(lot.stockOutLineId) > 0 &&
                                          issuePickedQtyBySolId[
                                            Number(lot.stockOutLineId)
                                          ] !== undefined)
                                      }
                                      title={
                                        isUnavailableLot
                                          ? t(
                                              "is unavable. Please check around have available QR code or not.",
                                            )
                                          : undefined
                                      }
                                      sx={{
                                        fontSize: "0.7rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "90px",
                                      }}
                                    >
                                      {tPick("Just Completed")}
                                    </Button>
                                  </Stack>
                                );
                              })()}
                            </Box>
                          </TableCell>
                        </TableRow>
                      );
                    })
                  )}
                </TableBody>
              </Table>
            </TableContainer>

            <TablePagination
              component="div"
              count={
                selectedFloor ? filteredByFloor.length : combinedLotData.length
              }
              page={paginationController.pageNum}
              rowsPerPage={paginationController.pageSize}
              onPageChange={handlePageChange}
              onRowsPerPageChange={handlePageSizeChange}
              rowsPerPageOptions={[10, 25, 50]}
              labelRowsPerPage={t("Rows per page")}
              labelDisplayedRows={({ from, to, count }) =>
                `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
              }
            />
          </Box>
        </Stack>

        {/*  QR Code Modal */}
        <QrCodeModal
          open={qrModalOpen}
          onClose={() => {
            setQrModalOpen(false);
            setSelectedLotForQr(null);
            // Keep scanner active like GoodPickExecutiondetail.
            resetScan();
          }}
          lot={selectedLotForQr}
          combinedLotData={combinedLotData}
          onQrCodeSubmit={handleQrCodeSubmitFromModal}
        />
        <WorkbenchLotLabelPrintModal
          open={workbenchLotLabelModalOpen}
          onClose={() => {
            setWorkbenchLotLabelModalOpen(false);
            setWorkbenchLotLabelContextLot(null);
            setWorkbenchLotLabelInitialPayload(null);
            setWorkbenchLotLabelReminderText(null);
          }}
          initialPayload={workbenchLotLabelInitialPayload}
          initialItemId={
            workbenchLotLabelContextLot != null
              ? Number(workbenchLotLabelContextLot.itemId)
              : null
          }
          defaultPrinterName={defaultLabelPrinterName}
          hideScanSection={
            workbenchLotLabelInitialPayload != null ||
            workbenchLotLabelContextLot != null
          }
          warehouseCodePrefixFilter={lotFloorPrefixFilter}
          triggerLotAvailableQty={
            workbenchLotLabelContextLot != null
              ? Number(workbenchLotLabelContextLot.availableQty)
              : null
          }
          triggerLotUom={
            workbenchLotLabelContextLot != null
              ? String(workbenchLotLabelContextLot.uomDesc ?? "").trim() || null
              : null
          }
          disableScanPick={workbenchLotLabelScanPickDisabled}
          onWorkbenchScanPick={handleWorkbenchLotLabelScanPick}
          submitQty={workbenchLotLabelSubmitQty}
          onSubmitQtyChange={handleWorkbenchLotLabelSubmitQtyChange}
          reminderText={workbenchLotLabelReminderText ?? undefined}
        />
        {/* Manual Lot Confirmation Modal (test shortcut {2fic}); JO Workbench 不使用 LotConfirmationModal，直接走 handleLotConfirmation */}
        <ManualLotConfirmationModal
          open={manualLotConfirmationOpen}
          onClose={() => setManualLotConfirmationOpen(false)}
          onConfirm={(currentLotNo, newLotNo) => {
            setManualLotConfirmationOpen(false);
            const row = selectedLotForQr;
            if (
              !row?.stockOutLineId ||
              !row?.pickOrderLineId ||
              row.itemId == null
            ) {
              alert(
                t(
                  "Open 挑號 QR 碼 on a pick line first, then scan {2fic} to use manual lot substitution.",
                ),
              );
              return;
            }
            setExpectedLotData({
              ...row,
              lotNo: currentLotNo,
            });
            setScannedLotData({
              lotNo: newLotNo,
              itemCode: String(row.itemCode ?? ""),
              itemName: String(row.itemName ?? ""),
              inventoryLotLineId: null,
              stockInLineId: null,
            });
            window.setTimeout(() => {
              void handleLotConfirmation();
            }, 0);
          }}
          expectedLot={expectedLotData}
          scannedLot={scannedLotData}
          isLoading={isConfirmingLot}
        />
      
      </FormProvider>
    </TestQrCodeProvider>
  );
};

export default JobPickExecution;
