/*
"use client";

import {
  Box,
  Button,
  Stack,
  TextField,
  Typography,
  Alert,
  CircularProgress,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  Checkbox,
  TablePagination,
  Modal,
  Chip,
} from "@mui/material";
import dayjs from "dayjs";
import TestQrCodeProvider from "../QrCodeScannerProvider/TestQrCodeProvider";
import { fetchLotDetail } from "@/app/api/inventory/actions";
import { formatDepartureTime } from "@/app/utils/formatUtil";
import React, {
  useCallback,
  useEffect,
  useState,
  useRef,
  useMemo,
  startTransition,
} from "react";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
import {
  updateStockOutLineStatus,
  createStockOutLine,
  updateStockOutLine,
  recordPickExecutionIssue,
  fetchFGPickOrders, //  Add this import
  FGPickOrderResponse,
  stockReponse,
  PickExecutionIssueData,
  checkPickOrderCompletion,
  fetchAllPickOrderLotsHierarchical,
  PickOrderCompletionResponse,
  checkAndCompletePickOrderByConsoCode,
  updateSuggestedLotLineId,
  updateStockOutLineStatusByQRCodeAndLotNo,
  confirmLotSubstitution,
  fetchDoPickOrderDetail, //  必须添加
  DoPickOrderDetail, //  必须添加
  fetchFGPickOrdersByUserId,
  batchSubmitList, // 添加：导入 batchSubmitList
  batchSubmitListRequest, // 添加：导入类型
  batchSubmitListLineRequest,
  batchScan,
  BatchScanRequest,
  BatchScanLineRequest,
} from "@/app/api/pickOrder/actions";

import FGPickOrderInfoCard from "./FGPickOrderInfoCard";
import LotConfirmationModal from "./LotConfirmationModal";
//import { fetchItem } from "@/app/api/settings/item";
import {
  updateInventoryLotLineStatus,
  analyzeQrCode,
} from "@/app/api/inventory/actions";
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 } 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 "./GoodPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard";
import LinearProgressWithLabel from "../common/LinearProgressWithLabel";
import ScanStatusAlert from "../common/ScanStatusAlert";
import { translateLotSubstitutionFailure } from "./lotSubstitutionMessage";
import LotLabelPrintModal from "@/components/InventorySearch/LotLabelPrintModal";
interface Props {
  filterArgs: Record<string, any>;
  onSwitchToRecordTab?: () => void;
  onRefreshReleasedOrderCount?: () => void;
}
type LotConfirmRunContext = {
  expectedLotData: {
    lotNo: string | null;
    itemCode?: string;
    itemName?: string;
  };
  scannedLotData: {
    lotNo: string | null;
    itemCode?: string;
    itemName?: string;
    stockInLineId: number; // 必須有，API 用
    inventoryLotLineId?: number | null;
  };
  selectedLotForQr: any; // 與現在一樣：含 pickOrderLineId, stockOutLineId, suggestedPickLotId, itemId…
};

/** 同物料多行时，优先对「有建议批次号」的行做替换，避免误选「无批次/不足」行 
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) => (l.stockOutLineStatus || "").toLowerCase() === "pending",
    );
    return pending || withLotNo[0];
  }
  return activeSuggestedLots[0];
}

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

  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>("");
  const [fgPickOrder, setFgPickOrder] = useState<FGPickOrderResponse | null>(
    null,
  );
  const fetchingRef = useRef<Set<number>>(new Set());

  useEffect(() => {
    // ✅ Don't process if modal is not open
    if (!open) {
      return;
    }

    // ✅ Don't process if lot confirmation modal is open
    if (lotConfirmationOpen) {
      console.log(
        "Lot confirmation modal is open, skipping QrCodeModal processing...",
      );
      return;
    }

    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;
      }

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

        if (qrData.stockInLineId && qrData.itemId) {
          // ✅ Check if we're already fetching this stockInLineId
          if (fetchingRef.current.has(qrData.stockInLineId)) {
            console.log(
              `  [QR MODAL] Already fetching stockInLineId: ${qrData.stockInLineId}, skipping duplicate call`,
            );
            return;
          }

          setProcessedQrCodes((prev) => new Set(prev).add(latestQr));
          setIsProcessingQr(true);
          setQrScanFailed(false);

          // ✅ Mark as fetching
          fetchingRef.current.add(qrData.stockInLineId);

          const fetchStartTime = performance.now();
          console.log(
            `  [QR MODAL] Starting fetchStockInLineInfo for stockInLineId: ${qrData.stockInLineId}`,
          );

          fetchStockInLineInfo(qrData.stockInLineId)
            .then((stockInLineInfo) => {
              // ✅ Remove from fetching set
              fetchingRef.current.delete(qrData.stockInLineId);

              // ✅ Check again if modal is still open and lot confirmation is not open
              if (!open || lotConfirmationOpen) {
                console.log("Modal state changed, skipping result processing");
                return;
              }

              const fetchTime = performance.now() - fetchStartTime;
              console.log(
                `  [QR MODAL] fetchStockInLineInfo time: ${fetchTime.toFixed(
                  2,
                )}ms (${(fetchTime / 1000).toFixed(3)}s)`,
              );
              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) => {
              // ✅ Remove from fetching set
              fetchingRef.current.delete(qrData.stockInLineId);

              // ✅ Check again if modal is still open
              if (!open || lotConfirmationOpen) {
                console.log("Modal state changed, skipping error handling");
                return;
              }

              const fetchTime = performance.now() - fetchStartTime;
              console.error(
                `❌ [QR MODAL] fetchStockInLineInfo failed after ${fetchTime.toFixed(
                  2,
                )}ms:`,
                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,
    lotConfirmationOpen,
    open,
  ]);

  // 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()}
            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 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("pickOrder");
  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("");
            }}
            placeholder={expectedLot?.lotNo || t("Enter expected lot number")}
            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("");
            }}
            placeholder={scannedLot?.lotNo || t("Enter scanned lot number")}
            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>
  );
};

/** 過期批號（未換有效批前）：與 noLot 類似——單筆/批量預設提交量為 0，除非 Issue 改數  
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";
}

/** Issue「改數」未寫入 SOL，刷新/換頁後需靠 session 還原，否則 Qty will submit 會回到 req  
const FG_ISSUE_PICKED_KEY = (doPickOrderId: number) =>
  `fpsms-fg-issuePickedQty:${doPickOrderId}`;

function loadIssuePickedMap(doPickOrderId: number): Record<number, number> {
  if (typeof window === "undefined" || !doPickOrderId) return {};
  try {
    const raw = sessionStorage.getItem(FG_ISSUE_PICKED_KEY(doPickOrderId));
    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 saveIssuePickedMap(
  doPickOrderId: number,
  map: Record<number, number>,
) {
  if (typeof window === "undefined" || !doPickOrderId) return;
  try {
    sessionStorage.setItem(
      FG_ISSUE_PICKED_KEY(doPickOrderId),
      JSON.stringify(map),
    );
  } catch {
    // quota / private mode
  }
}

const PickExecution: React.FC<Props> = ({
  filterArgs,
  onSwitchToRecordTab,
  onRefreshReleasedOrderCount,
}) => {
  const { t } = useTranslation("pickOrder");
  const router = useRouter();
  const { data: session } = useSession() as { data: SessionWithTokens | null };
  const [doPickOrderDetail, setDoPickOrderDetail] =
    useState<DoPickOrderDetail | null>(null);
  const [selectedPickOrderId, setSelectedPickOrderId] = useState<number | null>(
    null,
  );
  const [pickOrderSwitching, setPickOrderSwitching] = useState(false);
  const currentUserId = session?.id ? parseInt(session.id) : undefined;
  const [allLotsCompleted, setAllLotsCompleted] = useState(false);
  const [combinedLotData, setCombinedLotData] = useState<any[]>([]);
  const [combinedDataLoading, setCombinedDataLoading] = useState(false);
  const [originalCombinedData, setOriginalCombinedData] = useState<any[]>([]);
  // issue form 里填的 actualPickQty（用于 batch submit 只提交实际拣到数量，而不是补拣到 required）
  const [issuePickedQtyBySolId, setIssuePickedQtyBySolId] = useState<
    Record<number, number>
  >({});
  const applyLocalStockOutLineUpdate = useCallback(
    (stockOutLineId: number, status: string, actualPickQty?: number) => {
      setCombinedLotData((prev) =>
        prev.map((lot) => {
          if (Number(lot.stockOutLineId) !== Number(stockOutLineId)) return lot;
          return {
            ...lot,
            stockOutLineStatus: status,
            ...(typeof actualPickQty === "number"
              ? { actualPickQty, stockOutLineQty: actualPickQty }
              : {}),
          };
        }),
      );
    },
    [],
  );
  // 防止重复点击（Submit / Just Completed / Issue）
  const [actionBusyBySolId, setActionBusyBySolId] = useState<
    Record<number, boolean>
  >({});

  const {
    values: qrValues,
    isScanning,
    startScan,
    stopScan,
    resetScan,
  } = useQrCodeScannerContext();

  const [qrScanInput, setQrScanInput] = useState<string>("");
  const [qrScanError, setQrScanError] = useState<boolean>(false);
  const [qrScanErrorMsg, setQrScanErrorMsg] = useState<string>("");
  const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
  const [manualLotConfirmationOpen, setManualLotConfirmationOpen] =
    useState(false);
  const [lotLabelPrintModalOpen, setLotLabelPrintModalOpen] = useState(false);
  const [lotLabelPrintInitialPayload, setLotLabelPrintInitialPayload] =
    useState<{
      itemId: number;
      stockInLineId: number;
    } | null>(null);
  const [lotLabelPrintReminderText, setLotLabelPrintReminderText] = useState<
    string | null
  >(null);
  const [pickQtyData, setPickQtyData] = useState<Record<string, number>>({});
  const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});

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

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

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

  const formProps = useForm();
  const errors = formProps.formState.errors;

  //  QR scanner states (always-on, no modal)
  const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
  const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
  const [lotConfirmationError, setLotConfirmationError] = useState<
    string | null
  >(null);
  /** QR 静默换批失败时显示在对应行的 Lot# 列，key = stockOutLineId  
  const [lotSwitchFailByStockOutLineId, setLotSwitchFailByStockOutLineId] =
    useState<Record<number, string>>({});
  const [expectedLotData, setExpectedLotData] = useState<any>(null);
  const [scannedLotData, setScannedLotData] = useState<any>(null);
  const [isConfirmingLot, setIsConfirmingLot] = useState(false);
  //  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 lotFloorPrefixFilter = useMemo(() => {
    const storeId = String(fgPickOrders?.[0]?.storeId ?? "")
      .trim()
      .toUpperCase()
      .replace(/\s/g, "");
    // e.g. "2/F" -> "2F-", "4/F" -> "4F-"
    const floorKey = storeId.replace(/\//g, "");
    return floorKey ? `${floorKey}-` : "";
  }, [fgPickOrders]);
  const defaultLabelPrinterName = useMemo(() => {
    const storeId = String(fgPickOrders?.[0]?.storeId ?? "")
      .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;
  }, [fgPickOrders]);
  //  Add these missing state variables after line 352
  const [isManualScanning, setIsManualScanning] = 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());
  const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(
    new Set(),
  );
  const [lastProcessedQr, setLastProcessedQr] = useState<string>("");
  const [isRefreshingData, setIsRefreshingData] = useState<boolean>(false);
  const [isSubmittingAll, setIsSubmittingAll] = useState<boolean>(false);

  // 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);
  const lotConfirmOpenedQrCountRef = useRef<number>(0);
  const lotConfirmLastQrRef = useRef<string>("");
  const lotConfirmSkipNextScanRef = useRef<boolean>(false);
  const lotConfirmOpenedAtRef = useRef<number>(0);
  const handleLotConfirmationRef = useRef<
    | ((
        overrideScannedLot?: any,
        runContext?: LotConfirmRunContext,
      ) => Promise<void>)
    | null
  >(null);

  //  Handle QR code button click
  const handleQrCodeClick = (pickOrderId: number) => {
    console.log(`QR Code clicked for pick order ID: ${pickOrderId}`);
    // TODO: Implement QR code functionality
  };
  const progress = useMemo(() => {
    if (combinedLotData.length === 0) {
      return { completed: 0, total: 0 };
    }

    // 與 allItemsReady 一致：noLot / 過期 / unavailable 的 pending 也算「已面對該行」可收尾
    const nonPendingCount = combinedLotData.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: combinedLotData.length,
    };
  }, [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);

      // Return cached value if still valid
      if (cached && now - cached.timestamp < CACHE_TTL) {
        console.log(
          `✅ [CACHE HIT] Using cached stockInLineInfo for ${stockInLineId}`,
        );
        return { lotNo: cached.lotNo };
      }

      // Cancel previous request if exists
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }

      // Create new abort controller for this request
      const abortController = new AbortController();
      abortControllerRef.current = abortController;

      try {
        console.log(
          `  [CACHE MISS] Fetching stockInLineInfo for ${stockInLineId}`,
        );
        const stockInLineInfo = await fetchStockInLineInfo(stockInLineId);

        // Store in cache
        stockInLineInfoCache.current.set(stockInLineId, {
          lotNo: stockInLineInfo.lotNo || null,
          timestamp: now,
        });

        // Limit cache size to prevent memory leaks
        if (stockInLineInfoCache.current.size > 100) {
          const firstKey = stockInLineInfoCache.current.keys().next().value;
          if (firstKey !== undefined) {
            stockInLineInfoCache.current.delete(firstKey);
          }
        }

        return { lotNo: stockInLineInfo.lotNo || null };
      } catch (error: any) {
        if (error.name === "AbortError") {
          console.log(`  [CACHE] Request aborted for ${stockInLineId}`);
          throw error;
        }
        console.error(
          `❌ [CACHE] Error fetching stockInLineInfo for ${stockInLineId}:`,
          error,
        );
        throw error;
      }
    },
    [],
  );

  const handleLotMismatch = useCallback(
    (fullExpectedLotRow: any, scannedLot: any, qrScanCountAtOpen?: number) => {
      const mismatchStartTime = performance.now();
      console.log(`  [HANDLE LOT MISMATCH START]`);
      console.log(`  Start time: ${new Date().toISOString()}`);
      console.log("Lot mismatch detected:", { fullExpectedLotRow, scannedLot });

      lotConfirmOpenedQrCountRef.current =
        typeof qrScanCountAtOpen === "number" ? qrScanCountAtOpen : 1;

      // ✅ Use setTimeout to avoid flushSync warning - schedule state + silent substitution in next tick
      const setTimeoutStartTime = performance.now();
      console.time("setLotMismatchStateAndSubstitute");
      setTimeout(() => {
        const setStateStartTime = performance.now();
        const expectedForDisplay = {
          lotNo: fullExpectedLotRow.lotNo,
          itemCode: fullExpectedLotRow.itemCode,
          itemName: fullExpectedLotRow.itemName,
        };
        const scannedMerged = {
          ...scannedLot,
          lotNo: scannedLot.lotNo || null,
        };
        setExpectedLotData(expectedForDisplay);
        setScannedLotData(scannedMerged);
        setSelectedLotForQr(fullExpectedLotRow);
        // The QR that triggered mismatch must NOT be treated as confirmation rescan.
        lotConfirmSkipNextScanRef.current = true;
        lotConfirmOpenedAtRef.current = Date.now();

        const sid = Number(scannedLot.stockInLineId);
        if (!Number.isFinite(sid)) {
          console.error(
            `  [HANDLE LOT MISMATCH] Invalid stockInLineId for substitution: ${scannedLot.stockInLineId}`,
          );
          const errMsg = t(
            "Lot switch failed; pick line was not marked as checked.",
          );
          const rowSol = Number(fullExpectedLotRow.stockOutLineId);
          if (Number.isFinite(rowSol)) {
            setLotSwitchFailByStockOutLineId((prev) => ({
              ...prev,
              [rowSol]: errMsg,
            }));
          }
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(errMsg);
          const setStateTime = performance.now() - setStateStartTime;
          console.timeEnd("setLotMismatchStateAndSubstitute");
          console.log(
            `  [HANDLE LOT MISMATCH] Lot switch failed (invalid stockInLineId), setState time: ${setStateTime.toFixed(
              2,
            )}ms`,
          );
          return;
        }

        const runContext: LotConfirmRunContext = {
          expectedLotData: expectedForDisplay,
          scannedLotData: {
            ...scannedMerged,
            stockInLineId: sid,
            itemCode: scannedMerged.itemCode ?? fullExpectedLotRow.itemCode,
            itemName: scannedMerged.itemName ?? fullExpectedLotRow.itemName,
            inventoryLotLineId:
              scannedLot.inventoryLotLineId ?? scannedLot.lotId ?? null,
          },
          selectedLotForQr: fullExpectedLotRow,
        };
        void handleLotConfirmationRef.current?.(undefined, runContext);

        const setStateTime = performance.now() - setStateStartTime;
        console.timeEnd("setLotMismatchStateAndSubstitute");
        console.log(
          `  [HANDLE LOT MISMATCH] Silent lot substitution scheduled (setState time: ${setStateTime.toFixed(
            2,
          )}ms)`,
        );
      }, 0);
      const setTimeoutTime = performance.now() - setTimeoutStartTime;
      console.log(
        `  [PERF] setTimeout scheduling time: ${setTimeoutTime.toFixed(2)}ms`,
      );

      // ✅ Fetch lotNo in background ONLY for display purposes (using cached version)
      if (!scannedLot.lotNo && scannedLot.stockInLineId) {
        const stockInLineId = scannedLot.stockInLineId;
        if (typeof stockInLineId !== "number") {
          console.warn(
            `  [HANDLE LOT MISMATCH] Invalid stockInLineId: ${stockInLineId}`,
          );
          return;
        }
        console.log(
          `  [HANDLE LOT MISMATCH] Fetching lotNo in background (stockInLineId: ${stockInLineId})`,
        );
        const fetchStartTime = performance.now();

        fetchStockInLineInfoCached(stockInLineId)
          .then((stockInLineInfo) => {
            const fetchTime = performance.now() - fetchStartTime;
            console.log(
              `  [HANDLE LOT MISMATCH] fetchStockInLineInfoCached time: ${fetchTime.toFixed(
                2,
              )}ms (${(fetchTime / 1000).toFixed(3)}s)`,
            );

            const updateStateStartTime = performance.now();
            startTransition(() => {
              setScannedLotData((prev: any) => ({
                ...prev,
                lotNo: stockInLineInfo.lotNo || null,
              }));
            });
            const updateStateTime = performance.now() - updateStateStartTime;
            console.log(
              `  [PERF] Update scanned lot data time: ${updateStateTime.toFixed(
                2,
              )}ms`,
            );

            const totalTime = performance.now() - mismatchStartTime;
            console.log(
              `  [HANDLE LOT MISMATCH] Background fetch completed: ${totalTime.toFixed(
                2,
              )}ms (${(totalTime / 1000).toFixed(3)}s)`,
            );
          })
          .catch((error) => {
            if (error.name !== "AbortError") {
              const fetchTime = performance.now() - fetchStartTime;
              console.error(
                `❌ [HANDLE LOT MISMATCH] fetchStockInLineInfoCached failed after ${fetchTime.toFixed(
                  2,
                )}ms:`,
                error,
              );
            }
          });
      } else {
        const totalTime = performance.now() - mismatchStartTime;
        console.log(
          `  [HANDLE LOT MISMATCH END] Total time: ${totalTime.toFixed(
            2,
          )}ms (${(totalTime / 1000).toFixed(3)}s)`,
        );
      }
    },
    [fetchStockInLineInfoCached, t],
  );
  const checkAllLotsCompleted = useCallback((lotData: any[]) => {
    if (lotData.length === 0) {
      setAllLotsCompleted(false);
      return false;
    }

    // Filter out rejected lots
    const nonRejectedLots = lotData.filter(
      (lot) =>
        lot.lotAvailability !== "rejected" &&
        lot.stockOutLineStatus !== "rejected",
    );

    if (nonRejectedLots.length === 0) {
      setAllLotsCompleted(false);
      return false;
    }

    // Check if all non-rejected lots are completed
    const allCompleted = nonRejectedLots.every(
      (lot) => lot.stockOutLineStatus === "completed",
    );

    setAllLotsCompleted(allCompleted);
    return allCompleted;
  }, []);
  // 在 fetchAllCombinedLotData 函数中（约 446-684 行）

  const fetchAllCombinedLotData = useCallback(
    async (userId?: number, pickOrderIdOverride?: number) => {
      setCombinedDataLoading(true);
      try {
        const userIdToUse = userId || currentUserId;

        console.log(
          "  fetchAllCombinedLotData called with userId:",
          userIdToUse,
        );

        if (!userIdToUse) {
          console.warn("⚠️ No userId available, skipping API call");
          setCombinedLotData([]);
          setOriginalCombinedData([]);
          setAllLotsCompleted(false);
          setIssuePickedQtyBySolId({});
          return;
        }

        //  获取新结构的层级数据
        const hierarchicalData =
          await fetchAllPickOrderLotsHierarchical(userIdToUse);
        console.log(" Hierarchical data (new structure):", hierarchicalData);

        //  检查数据结构
        if (
          !hierarchicalData.fgInfo ||
          !hierarchicalData.pickOrders ||
          hierarchicalData.pickOrders.length === 0
        ) {
          console.warn("⚠️ No FG info or pick orders found");
          setCombinedLotData([]);
          setOriginalCombinedData([]);
          setAllLotsCompleted(false);
          setIssuePickedQtyBySolId({});
          return;
        }

        //  使用合并后的 pick order 对象（现在只有一个对象，包含所有数据）
        const mergedPickOrder = hierarchicalData.pickOrders[0];

        //  设置 FG info 到 fgPickOrders（用于显示 FG 信息卡片）
        // 修改第 478-509 行的 fgOrder 构建逻辑：

        const fgOrder: FGPickOrderResponse = {
          doPickOrderId: hierarchicalData.fgInfo.doPickOrderId,
          ticketNo: hierarchicalData.fgInfo.ticketNo,
          storeId: hierarchicalData.fgInfo.storeId,
          shopCode: hierarchicalData.fgInfo.shopCode,
          shopName: hierarchicalData.fgInfo.shopName,
          truckLanceCode: hierarchicalData.fgInfo.truckLanceCode,
          DepartureTime: hierarchicalData.fgInfo.departureTime,
          shopAddress: "",
          pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",

          // 兼容字段（注意 consoCodes 是数组）
          pickOrderId: mergedPickOrder.pickOrderIds?.[0] || 0,
          pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes)
            ? mergedPickOrder.consoCodes[0] || ""
            : "",
          pickOrderTargetDate: mergedPickOrder.targetDate || "",
          pickOrderStatus: mergedPickOrder.status || "",
          deliveryOrderId: mergedPickOrder.doOrderIds?.[0] || 0,
          deliveryNo: mergedPickOrder.deliveryOrderCodes?.[0] || "",
          deliveryDate: "",
          shopId: 0,
          shopPoNo: "",
          numberOfCartons: mergedPickOrder.pickOrderLines?.length || 0,
          qrCodeData: hierarchicalData.fgInfo.doPickOrderId,

          // 多个 pick orders 信息：全部保留为数组
          numberOfPickOrders: mergedPickOrder.pickOrderIds?.length || 0,
          pickOrderIds: mergedPickOrder.pickOrderIds || [],
          pickOrderCodes: Array.isArray(mergedPickOrder.pickOrderCodes)
            ? mergedPickOrder.pickOrderCodes
            : [],
          deliveryOrderIds: mergedPickOrder.doOrderIds || [],
          deliveryNos: Array.isArray(mergedPickOrder.deliveryOrderCodes)
            ? mergedPickOrder.deliveryOrderCodes
            : [],
          lineCountsPerPickOrder: Array.isArray(
            mergedPickOrder.lineCountsPerPickOrder,
          )
            ? mergedPickOrder.lineCountsPerPickOrder
            : [],
        };

        setFgPickOrders([fgOrder]);
        console.log(
          "  DEBUG fgOrder.lineCountsPerPickOrder:",
          fgOrder.lineCountsPerPickOrder,
        );
        console.log("  DEBUG fgOrder.pickOrderCodes:", fgOrder.pickOrderCodes);
        console.log("  DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos);

        // 直接使用合并后的 pickOrderLines
        console.log("🎯 Displaying merged pick order lines");

        // 将层级数据转换为平铺格式（用于表格显示）
        const flatLotData: any[] = [];

        // 2/F 與後端 store_id 一致時需按 itemOrder；避免 API 未走 2F 分支時畫面仍亂序
        const doFloorKey = String(hierarchicalData.fgInfo.storeId ?? "")
          .trim()
          .toUpperCase()
          .replace(/\//g, "")
          .replace(/\s/g, "");
        const pickOrderLinesForDisplay =
          doFloorKey === "2F"
            ? [...(mergedPickOrder.pickOrderLines || [])].sort(
                (a: any, b: any) => {
                  const ao = a.itemOrder != null ? Number(a.itemOrder) : 999999;
                  const bo = b.itemOrder != null ? Number(b.itemOrder) : 999999;
                  if (ao !== bo) return ao - bo;
                  return (Number(a.id) || 0) - (Number(b.id) || 0);
                },
              )
            : mergedPickOrder.pickOrderLines || [];

        pickOrderLinesForDisplay.forEach((line: any) => {
          // 用来记录这一行已经通过 lots 出现过的 lotId
          const lotIdSet = new Set<number>();
          /** 已由有批次建議分配的量（加總後與 pick_order_line.requiredQty 的差額 = 無批次列應顯示的數）  
          let lotsAllocatedSumForLine = 0;

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

            line.lots.forEach((lot: any) => {
              const lotId = lot.id;
              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.id != null) {
                lotIdSet.add(lot.id);
              }

              flatLotData.push({
                pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes)
                  ? mergedPickOrder.consoCodes[0] || ""
                  : "",
                pickOrderTargetDate: mergedPickOrder.targetDate,
                pickOrderStatus: mergedPickOrder.status,
                pickOrderId:
                  line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0,
                pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",
                pickOrderLineId: line.id,
                pickOrderLineRequiredQty: line.requiredQty,
                pickOrderLineStatus: line.status,

                itemId: line.item.id,
                itemCode: line.item.code,
                itemName: line.item.name,
                uomDesc: line.item.uomDesc,
                uomShortDesc: line.item.uomShortDesc,

                lotId: lot.id,
                lotNo: lot.lotNo,
                expiryDate: lot.expiryDate,
                location: lot.location,
                stockUnit: lot.stockUnit,
                availableQty: lot.availableQty,
                requiredQty: lot.requiredQty,
                actualPickQty: lot.actualPickQty,
                inQty: lot.inQty,
                outQty: lot.outQty,
                holdQty: lot.holdQty,
                lotStatus: lot.lotStatus,
                lotAvailability: lot.lotAvailability,
                processingStatus: lot.processingStatus,
                suggestedPickLotId: lot.suggestedPickLotId,
                stockOutLineId: lot.stockOutLineId,
                stockOutLineStatus: lot.stockOutLineStatus,
                stockOutLineQty: lot.stockOutLineQty,
                stockInLineId: lot.stockInLineId,
                routerId: lot.router?.id,
                routerIndex: lot.router?.index,
                routerRoute: lot.router?.route,
                routerArea: lot.router?.area,
                noLot: false,
              });
            });
          }

          // ✅ stockouts：只保留“真正无批次 / 未在 lots 出现过”的行
          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;
              }

              // 只渲染：
              // - noLot === true 的 Null stock 行
              // - 或者 lotId 在 lots 中不存在的特殊情况
              flatLotData.push({
                pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes)
                  ? mergedPickOrder.consoCodes[0] || ""
                  : "",
                pickOrderTargetDate: mergedPickOrder.targetDate,
                pickOrderStatus: mergedPickOrder.status,
                pickOrderId:
                  line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0,
                pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",
                pickOrderLineId: line.id,
                pickOrderLineRequiredQty: line.requiredQty,
                pickOrderLineStatus: line.status,

                itemId: line.item.id,
                itemCode: line.item.code,
                itemName: line.item.name,
                uomDesc: line.item.uomDesc,
                uomShortDesc: line.item.uomShortDesc,

                lotId: stockout.lotId || null,
                lotNo: stockout.lotNo || null,
                expiryDate: null,
                location: stockout.location || null,
                stockUnit: line.item.uomDesc,
                availableQty: stockout.availableQty || 0,
                // 無批次列對應 suggested_pick_lot 的缺口量（如 11），勿用整行 POL 需求（100）以免顯示成 89 / 100
                requiredQty: stockout.noLot
                  ? Math.max(
                      0,
                      (Number(line.requiredQty) || 0) - lotsAllocatedSumForLine,
                    )
                  : Number(line.requiredQty) || 0,
                actualPickQty: stockout.qty || 0,
                inQty: 0,
                outQty: 0,
                holdQty: 0,
                lotStatus: stockout.noLot ? "unavailable" : "available",
                lotAvailability: stockout.noLot
                  ? "insufficient_stock"
                  : "available",
                processingStatus: stockout.status || "pending",
                suggestedPickLotId: null,
                stockOutLineId: stockout.id || null,
                stockOutLineStatus: stockout.status || null,
                stockOutLineQty: stockout.qty || 0,

                routerId: null,
                routerIndex: stockout.noLot ? 999999 : null,
                routerRoute: null,
                routerArea: null,
                noLot: !!stockout.noLot,
              });
            });
          }
        });

        console.log(" Transformed flat lot data:", flatLotData);
        console.log(
          "  Total items (including null stock):",
          flatLotData.length,
        );

        setCombinedLotData(flatLotData);
        setOriginalCombinedData(flatLotData);
        const doPid = hierarchicalData.fgInfo?.doPickOrderId;
        if (doPid) {
          setIssuePickedQtyBySolId(loadIssuePickedMap(doPid));
        }
        checkAllLotsCompleted(flatLotData);
      } catch (error) {
        console.error("  Error fetching combined lot data:", error);
        setCombinedLotData([]);
        setOriginalCombinedData([]);
        setAllLotsCompleted(false);
        setIssuePickedQtyBySolId({});
      } finally {
        setCombinedDataLoading(false);
      }
    },
    [currentUserId, checkAllLotsCompleted],
  ); //   移除 selectedPickOrderId 依赖
  //  Add effect to check completion when lot data changes
  const handleManualLotConfirmation = useCallback(
    async (currentLotNo: string, newLotNo: string) => {
      console.log(
        `  Manual lot confirmation: Current=${currentLotNo}, New=${newLotNo}`,
      );

      // 使用第一个输入框的 lot number 查找当前数据
      const currentLot = combinedLotData.find(
        (lot) => lot.lotNo && lot.lotNo === currentLotNo,
      );

      if (!currentLot) {
        console.error(`❌ Current lot not found: ${currentLotNo}`);
        alert(t("Current lot number not found. Please verify and try again."));
        return;
      }

      if (!currentLot.stockOutLineId) {
        console.error("❌ No stockOutLineId found for current lot");
        alert(
          t(
            "No stock out line found for current lot. Please contact administrator.",
          ),
        );
        return;
      }

      setIsConfirmingLot(true);

      try {
        // 调用 updateStockOutLineStatusByQRCodeAndLotNo API
        // 第一个 lot 用于获取 pickOrderLineId, stockOutLineId, itemId
        // 第二个 lot 作为 inventoryLotNo
        const res = await updateStockOutLineStatusByQRCodeAndLotNo({
          pickOrderLineId: currentLot.pickOrderLineId,
          inventoryLotNo: newLotNo, // 第二个输入框的值
          stockOutLineId: currentLot.stockOutLineId,
          itemId: currentLot.itemId,
          status: "checked",
        });

        console.log("📥 updateStockOutLineStatusByQRCodeAndLotNo result:", res);

        if (res.code === "checked" || res.code === "SUCCESS") {
          // ✅ 更新本地状态
          const entity = res.entity as any;

          setCombinedLotData((prev) =>
            prev.map((lot) => {
              if (
                lot.stockOutLineId === currentLot.stockOutLineId &&
                lot.pickOrderLineId === currentLot.pickOrderLineId
              ) {
                return {
                  ...lot,
                  stockOutLineStatus: "checked",
                  stockOutLineQty: entity?.qty
                    ? Number(entity.qty)
                    : lot.stockOutLineQty,
                };
              }
              return lot;
            }),
          );

          setOriginalCombinedData((prev) =>
            prev.map((lot) => {
              if (
                lot.stockOutLineId === currentLot.stockOutLineId &&
                lot.pickOrderLineId === currentLot.pickOrderLineId
              ) {
                return {
                  ...lot,
                  stockOutLineStatus: "checked",
                  stockOutLineQty: entity?.qty
                    ? Number(entity.qty)
                    : lot.stockOutLineQty,
                };
              }
              return lot;
            }),
          );

          console.log("✅ Lot substitution completed successfully");
          setQrScanSuccess(true);
          setQrScanError(false);

          // 关闭手动输入模态框
          setManualLotConfirmationOpen(false);

          // 刷新数据
          await fetchAllCombinedLotData();
        } else if (res.code === "LOT_NUMBER_MISMATCH") {
          console.warn("⚠️ Backend reported LOT_NUMBER_MISMATCH:", res.message);

          // ✅ 打开 lot confirmation modal 而不是显示 alert
          // 从响应消息中提取 expected lot number（如果可能）
          // 或者使用 currentLotNo 作为 expected lot
          const expectedLotNo = currentLotNo; // 当前 lot 是期望的

          // 查找新 lot 的信息（如果存在于 combinedLotData 中）
          const newLot = combinedLotData.find(
            (lot) => lot.lotNo && lot.lotNo === newLotNo,
          );

          // 设置 expected lot data
          setExpectedLotData({
            lotNo: expectedLotNo,
            itemCode: currentLot.itemCode || "",
            itemName: currentLot.itemName || "",
          });

          // 设置 scanned lot data
          setScannedLotData({
            lotNo: newLotNo,
            itemCode: newLot?.itemCode || currentLot.itemCode || "",
            itemName: newLot?.itemName || currentLot.itemName || "",
            inventoryLotLineId: newLot?.lotId || null,
            stockInLineId: null, // 手动输入时可能没有 stockInLineId
          });

          // 设置 selectedLotForQr 为当前 lot
          setSelectedLotForQr(currentLot);

          // 关闭手动输入模态框
          setManualLotConfirmationOpen(false);

          // 打开 lot confirmation modal
          setLotConfirmationOpen(true);

          setQrScanError(false); // 不显示错误，因为会打开确认模态框
          setQrScanSuccess(false);
        } else if (res.code === "ITEM_MISMATCH") {
          console.warn("⚠️ Backend reported ITEM_MISMATCH:", res.message);
          alert(t("Item mismatch: {message}", { message: res.message || "" }));
          setQrScanError(true);
          setQrScanSuccess(false);

          // 关闭手动输入模态框
          setManualLotConfirmationOpen(false);
        } else {
          console.warn("⚠️ Unexpected response code:", res.code);
          alert(
            t("Failed to update lot status. Response: {code}", {
              code: res.code,
            }),
          );
          setQrScanError(true);
          setQrScanSuccess(false);

          // 关闭手动输入模态框
          setManualLotConfirmationOpen(false);
        }
      } catch (error) {
        console.error("❌ Error in manual lot confirmation:", error);
        alert(t("Failed to confirm lot substitution. Please try again."));
        setQrScanError(true);
        setQrScanSuccess(false);

        // 关闭手动输入模态框
        setManualLotConfirmationOpen(false);
      } finally {
        setIsConfirmingLot(false);
      }
    },
    [combinedLotData, fetchAllCombinedLotData, t],
  );
  useEffect(() => {
    if (combinedLotData.length > 0) {
      checkAllLotsCompleted(combinedLotData);
    }
  }, [combinedLotData, checkAllLotsCompleted]);

  //  Add function to expose completion status to parent
  const getCompletionStatus = useCallback(() => {
    return allLotsCompleted;
  }, [allLotsCompleted]);

  //  Expose completion status to parent component
  useEffect(() => {
    // Dispatch custom event with completion status
    const event = new CustomEvent("pickOrderCompletionStatus", {
      detail: {
        allLotsCompleted,
        tabIndex: 1, //  明确指定这是来自标签页 1 的事件
      },
    });
    window.dispatchEvent(event);
  }, [allLotsCompleted]);

  const clearLotConfirmationState = useCallback(
    (clearProcessedRefs: boolean = false) => {
      setLotConfirmationOpen(false);
      setLotConfirmationError(null);
      setExpectedLotData(null);
      setScannedLotData(null);
      setSelectedLotForQr(null);
      lotConfirmLastQrRef.current = "";
      lotConfirmSkipNextScanRef.current = false;
      lotConfirmOpenedAtRef.current = 0;

      if (clearProcessedRefs) {
        setTimeout(() => {
          lastProcessedQrRef.current = "";
          processedQrCodesRef.current.clear();
          console.log(
            `  [LOT CONFIRM MODAL] Cleared refs to allow reprocessing`,
          );
        }, 100);
      }
    },
    [],
  );

  const parseQrPayload = useCallback(
    (rawQr: string): { itemId: number; stockInLineId: number } | null => {
      if (!rawQr) return null;

      if (
        (rawQr.startsWith("{2fitest") || rawQr.startsWith("{2fittest")) &&
        rawQr.endsWith("}")
      ) {
        let content = "";
        if (rawQr.startsWith("{2fittest")) {
          content = rawQr.substring(9, rawQr.length - 1);
        } else {
          content = rawQr.substring(8, rawQr.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)) {
            return { itemId, stockInLineId };
          }
        }
        return null;
      }

      try {
        const parsed = JSON.parse(rawQr);
        if (parsed?.itemId && parsed?.stockInLineId) {
          return { itemId: parsed.itemId, stockInLineId: parsed.stockInLineId };
        }
        return null;
      } catch {
        return null;
      }
    },
    [],
  );

  const handleLotConfirmation = useCallback(
    async (overrideScannedLot?: any, runContext?: LotConfirmRunContext) => {
      const exp = runContext?.expectedLotData ?? expectedLotData;
      const scan =
        overrideScannedLot ?? runContext?.scannedLotData ?? scannedLotData;
      const sel = runContext?.selectedLotForQr ?? selectedLotForQr;
      if (!exp || !scan || !sel) return;

      const newStockInLineId = scan?.stockInLineId;
      if (newStockInLineId == null || Number.isNaN(Number(newStockInLineId)))
        return;

      const rowSolKey = Number(sel.stockOutLineId);
      if (Number.isFinite(rowSolKey)) {
        setLotSwitchFailByStockOutLineId((prev) => {
          const next = { ...prev };
          delete next[rowSolKey];
          return next;
        });
      }

      setIsConfirmingLot(true);
      setLotConfirmationError(null);
      try {
        const substitutionResult = await confirmLotSubstitution({
          pickOrderLineId: sel.pickOrderLineId,
          stockOutLineId: sel.stockOutLineId,
          originalSuggestedPickLotId: sel.suggestedPickLotId,
          newInventoryLotNo: "",
          newStockInLineId: newStockInLineId,
        });

        const substitutionCode = substitutionResult?.code;
        const switchedToUnavailable =
          substitutionCode === "SUCCESS_UNAVAILABLE" ||
          substitutionCode === "BOUND_UNAVAILABLE";
        if (
          !substitutionResult ||
          (substitutionCode !== "SUCCESS" && !switchedToUnavailable)
        ) {
          const errMsg = translateLotSubstitutionFailure(t, substitutionResult);
          if (Number.isFinite(rowSolKey)) {
            setLotSwitchFailByStockOutLineId((prev) => ({
              ...prev,
              [rowSolKey]: errMsg,
            }));
          }
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(errMsg);
          return;
        }

        if (switchedToUnavailable) {
          const itemId = Number(sel?.itemId ?? exp?.itemId);
          const stockInLineId = Number(newStockInLineId);
          if (Number.isFinite(itemId) && Number.isFinite(stockInLineId)) {
            setLotLabelPrintInitialPayload({ itemId, stockInLineId });
            setLotLabelPrintReminderText(
              "該批次不可用，請移除該Label並列印新Label。",
            );
            setLotLabelPrintModalOpen(true);
          }
        }

        setQrScanError(false);
        setQrScanSuccess(false);
        setQrScanInput("");

        resetScan();

        setPickExecutionFormOpen(false);
        if (sel?.stockOutLineId && !switchedToUnavailable) {
          await updateStockOutLineStatus({
            id: sel.stockOutLineId,
            status: "checked",
            qty: 0,
          });
        }

        clearLotConfirmationState(false);

        setIsRefreshingData(true);
        await fetchAllCombinedLotData();
        setIsRefreshingData(false);
      } catch (error) {
        console.error("Error confirming lot substitution:", error);
        const errMsg = t("Lot confirmation failed. Please try again.");
        if (Number.isFinite(rowSolKey)) {
          setLotSwitchFailByStockOutLineId((prev) => ({
            ...prev,
            [rowSolKey]: errMsg,
          }));
        }
        setQrScanError(true);
        setQrScanErrorMsg(errMsg);
      } finally {
        setIsConfirmingLot(false);
      }
    },
    [
      expectedLotData,
      scannedLotData,
      selectedLotForQr,
      fetchAllCombinedLotData,
      resetScan,
      clearLotConfirmationState,
      t,
    ],
  );

  useEffect(() => {
    handleLotConfirmationRef.current = handleLotConfirmation;
  }, [handleLotConfirmation]);

  const handleLotConfirmationByRescan = useCallback(
    async (rawQr: string): Promise<boolean> => {
      if (
        !lotConfirmationOpen ||
        !selectedLotForQr ||
        !expectedLotData ||
        !scannedLotData
      ) {
        return false;
      }

      const payload = parseQrPayload(rawQr);
      const expectedStockInLineId = Number(selectedLotForQr.stockInLineId);
      const mismatchedStockInLineId = Number(scannedLotData?.stockInLineId);

      if (payload) {
        const rescannedStockInLineId = Number(payload.stockInLineId);

        // 再扫“差异 lot” => 直接执行切换
        if (
          Number.isFinite(mismatchedStockInLineId) &&
          rescannedStockInLineId === mismatchedStockInLineId
        ) {
          await handleLotConfirmation();
          return true;
        }

        // 再扫“原建议 lot” => 关闭弹窗并按原 lot 正常记一次扫描
        if (
          Number.isFinite(expectedStockInLineId) &&
          rescannedStockInLineId === expectedStockInLineId
        ) {
          clearLotConfirmationState(false);
          if (processOutsideQrCodeRef.current) {
            await processOutsideQrCodeRef.current(JSON.stringify(payload));
          }
          return true;
        }

        // 扫到第三个 lot（既不是当前差异 lot，也不是原建议 lot）：
        // 直接按“扫描到的这一批”执行切换。
        await handleLotConfirmation({
          lotNo: null,
          itemCode: expectedLotData?.itemCode,
          itemName: expectedLotData?.itemName,
          inventoryLotLineId: null,
          stockInLineId: rescannedStockInLineId,
        });
        return true;
      } else {
        // 兼容纯 lotNo 文本扫码
        const scannedText = rawQr?.trim();
        const expectedLotNo = expectedLotData?.lotNo?.trim();
        const mismatchedLotNo = scannedLotData?.lotNo?.trim();

        if (mismatchedLotNo && scannedText === mismatchedLotNo) {
          await handleLotConfirmation();
          return true;
        }

        if (expectedLotNo && scannedText === expectedLotNo) {
          clearLotConfirmationState(false);
          if (processOutsideQrCodeRef.current) {
            await processOutsideQrCodeRef.current(
              JSON.stringify({
                itemId: selectedLotForQr.itemId,
                stockInLineId: selectedLotForQr.stockInLineId,
              }),
            );
          }
          return true;
        }
      }

      return false;
    },
    [
      lotConfirmationOpen,
      selectedLotForQr,
      expectedLotData,
      scannedLotData,
      parseQrPayload,
      handleLotConfirmation,
      clearLotConfirmationState,
      handleLotMismatch,
    ],
  );

  const handleQrCodeSubmit = useCallback(
    async (lotNo: string) => {
      console.log(` Processing QR Code for lot: ${lotNo}`);

      //  检查 lotNo 是否为 null 或 undefined（包括字符串 "null"）
      if (!lotNo || lotNo === "null" || lotNo.trim() === "") {
        console.error("  Invalid lotNo: null, undefined, or empty");
        return;
      }

      //  Use current data without refreshing to avoid infinite loop
      const currentLotData = combinedLotData;
      console.log(
        ` Available lots:`,
        currentLotData.map((lot) => lot.lotNo),
      );

      //  修复：在比较前确保 lotNo 不为 null
      const lotNoLower = lotNo.toLowerCase();
      const matchingLots = currentLotData.filter((lot) => {
        if (!lot.lotNo) return false; //  跳过 null lotNo
        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;
      }

      console.log(` Found ${matchingLots.length} matching lots:`, matchingLots);
      setQrScanError(false);

      try {
        let successCount = 0;
        let errorCount = 0;

        for (const matchingLot of matchingLots) {
          console.log(
            `🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`,
          );

          if (matchingLot.stockOutLineId) {
            const stockOutLineUpdate = await updateStockOutLineStatus({
              id: matchingLot.stockOutLineId,
              status: "checked",
              qty: 0,
            });
            console.log(
              `Update stock out line result for line ${matchingLot.pickOrderLineId}:`,
              stockOutLineUpdate,
            );

            // Treat multiple backend shapes as success (type-safe via any)
            const r: any = stockOutLineUpdate as any;
            const updateOk =
              r?.code === "SUCCESS" ||
              typeof r?.id === "number" ||
              r?.type === "checked" ||
              r?.status === "checked" ||
              typeof r?.entity?.id === "number" ||
              r?.entity?.status === "checked";

            if (updateOk) {
              successCount++;
            } else {
              errorCount++;
            }
          } else {
            const createStockOutLineData = {
              consoCode: matchingLot.pickOrderConsoCode,
              pickOrderLineId: matchingLot.pickOrderLineId,
              inventoryLotLineId: matchingLot.lotId,
              qty: 0,
            };

            const createResult = await createStockOutLine(
              createStockOutLineData,
            );
            console.log(
              `Create stock out line result for line ${matchingLot.pickOrderLineId}:`,
              createResult,
            );

            if (createResult && createResult.code === "SUCCESS") {
              // Immediately set status to checked for new line
              let newSolId: number | undefined;
              const anyRes: any = createResult as any;
              if (typeof anyRes?.id === "number") {
                newSolId = anyRes.id;
              } else if (anyRes?.entity) {
                newSolId = Array.isArray(anyRes.entity)
                  ? anyRes.entity[0]?.id
                  : anyRes.entity?.id;
              }

              if (newSolId) {
                const setChecked = await updateStockOutLineStatus({
                  id: newSolId,
                  status: "checked",
                  qty: 0,
                });
                if (setChecked && setChecked.code === "SUCCESS") {
                  successCount++;
                } else {
                  errorCount++;
                }
              } else {
                console.warn(
                  "Created stock out line but no ID returned; cannot set to checked",
                );
                errorCount++;
              }
            } else {
              errorCount++;
            }
          }
        }

        //  FIXED: Set refresh flag before refreshing data
        setIsRefreshingData(true);
        console.log("🔄 Refreshing data after QR code processing...");
        await fetchAllCombinedLotData();

        if (successCount > 0) {
          console.log(
            ` QR Code processing completed: ${successCount} updated/created`,
          );
          setQrScanSuccess(true);
          setQrScanError(false);
          setQrScanInput(""); // Clear input after successful processing
          //setIsManualScanning(false);
          // stopScan();
          // resetScan();
          //  Clear success state after a delay

          //setTimeout(() => {
          //setQrScanSuccess(false);
          //}, 2000);
        } else {
          console.error(`  QR Code processing failed: ${errorCount} errors`);
          setQrScanError(true);
          setQrScanSuccess(false);

          //  Clear error state after a delay
          // setTimeout(() => {
          // setQrScanError(false);
          //}, 3000);
        }
      } catch (error) {
        console.error("  Error processing QR code:", error);
        setQrScanError(true);
        setQrScanSuccess(false);

        //  Clear error state after a delay
        setTimeout(() => {
          setQrScanError(false);
        }, 3000);
      } finally {
        //  Clear refresh flag after a short delay
        setTimeout(() => {
          setIsRefreshingData(false);
        }, 1000);
      }
    },
    [combinedLotData],
  );
  const handleFastQrScan = useCallback(
    async (lotNo: string) => {
      const startTime = performance.now();
      console.log(`  [FAST SCAN START] Lot: ${lotNo}`);
      console.log(`  Start time: ${new Date().toISOString()}`);

      // 从 combinedLotData 中找到对应的 lot
      const findStartTime = performance.now();
      const matchingLot = combinedLotData.find(
        (lot) => lot.lotNo && lot.lotNo === lotNo,
      );
      const findTime = performance.now() - findStartTime;
      console.log(`  Find lot time: ${findTime.toFixed(2)}ms`);

      if (!matchingLot || !matchingLot.stockOutLineId) {
        const totalTime = performance.now() - startTime;
        console.warn(
          `⚠️ Fast scan: Lot ${lotNo} not found or no stockOutLineId`,
        );
        console.log(`  Total time: ${totalTime.toFixed(2)}ms`);
        return;
      }

      try {
        // ✅ 使用快速 API
        const apiStartTime = performance.now();
        const res = await updateStockOutLineStatusByQRCodeAndLotNo({
          pickOrderLineId: matchingLot.pickOrderLineId,
          inventoryLotNo: lotNo,
          stockOutLineId: matchingLot.stockOutLineId,
          itemId: matchingLot.itemId,
          status: "checked",
        });
        const apiTime = performance.now() - apiStartTime;
        console.log(`  API call time: ${apiTime.toFixed(2)}ms`);

        if (res.code === "checked" || res.code === "SUCCESS") {
          // ✅ 只更新本地状态，不调用 fetchAllCombinedLotData
          const updateStartTime = performance.now();
          const entity = res.entity as any;

          setCombinedLotData((prev) =>
            prev.map((lot) => {
              if (
                lot.stockOutLineId === matchingLot.stockOutLineId &&
                lot.pickOrderLineId === matchingLot.pickOrderLineId
              ) {
                return {
                  ...lot,
                  stockOutLineStatus: "checked",
                  stockOutLineQty: entity?.qty
                    ? Number(entity.qty)
                    : lot.stockOutLineQty,
                };
              }
              return lot;
            }),
          );

          setOriginalCombinedData((prev) =>
            prev.map((lot) => {
              if (
                lot.stockOutLineId === matchingLot.stockOutLineId &&
                lot.pickOrderLineId === matchingLot.pickOrderLineId
              ) {
                return {
                  ...lot,
                  stockOutLineStatus: "checked",
                  stockOutLineQty: entity?.qty
                    ? Number(entity.qty)
                    : lot.stockOutLineQty,
                };
              }
              return lot;
            }),
          );
          const updateTime = performance.now() - updateStartTime;
          console.log(`  State update time: ${updateTime.toFixed(2)}ms`);

          const totalTime = performance.now() - startTime;
          console.log(`✅ [FAST SCAN END] Lot: ${lotNo}`);
          console.log(
            `  Total time: ${totalTime.toFixed(2)}ms (${(
              totalTime / 1000
            ).toFixed(3)}s)`,
          );
          console.log(`  End time: ${new Date().toISOString()}`);
        } else {
          const totalTime = performance.now() - startTime;
          console.warn(`⚠️ Fast scan failed for ${lotNo}:`, res.code);
          console.log(`  Total time: ${totalTime.toFixed(2)}ms`);
        }
      } catch (error) {
        const totalTime = performance.now() - startTime;
        console.error(`  Fast scan error for ${lotNo}:`, error);
        console.log(`  Total time: ${totalTime.toFixed(2)}ms`);
      }
    },
    [combinedLotData, updateStockOutLineStatusByQRCodeAndLotNo],
  );
  // Enhanced lotDataIndexes with cached active lots for better performance
  const lotDataIndexes = useMemo(() => {
    const indexStartTime = performance.now();
    console.log(
      `  [PERF] lotDataIndexes calculation START, data length: ${combinedLotData.length}`,
    );

    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[]>();

    // Cache active lots separately to avoid filtering on every scan
    const activeLotsByItemId = new Map<number, any[]>();
    const rejectedStatuses = new Set(["rejected"]);

    // ✅ Use for loop instead of forEach for better performance on tablets
    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);
      }
    }

    const indexTime = performance.now() - indexStartTime;
    if (indexTime > 10) {
      console.log(
        `  [PERF] lotDataIndexes calculation END: ${indexTime.toFixed(2)}ms (${(
          indexTime / 1000
        ).toFixed(3)}s)`,
      );
    }

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

  // Store resetScan in ref for immediate access (update on every render)
  resetScanRef.current = resetScan;

  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, but scanned lot is not rejected, allow lot switching
        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;
          }

          // ✅ Allow lot switching: find a rejected lot as expected lot, or use first lot
          // This allows users to switch to a new lot even when all suggested lots are rejected
          console.log(
            `⚠️ [QR PROCESS] No active suggested lots, but allowing lot switching.`,
          );

          // 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];

          // Silent lot substitution; modal only if switch fails
          console.log(
            `⚠️ [QR PROCESS] Lot switch (no active lots), attempting substitution`,
          );
          setSelectedLotForQr(expectedLot);
          handleLotMismatch(
            expectedLot,
            {
              lotNo: scannedLot?.lotNo || null,
              itemCode: expectedLot.itemCode,
              itemName: expectedLot.itemName,
              inventoryLotLineId: scannedLot?.lotId || null,
              stockInLineId: scannedStockInLineId,
            },
            qrScanCountAtInvoke,
          );
          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) {
          // Scanned lot is not in active suggested lots, open confirmation modal
          const expectedLot =
            pickExpectedLotForSubstitution(activeSuggestedLots) ||
            allLotsForItem[0];
          if (expectedLot) {
            // Check if scanned lot is different from expected, or if scannedLot is undefined (not in allLotsForItem)
            const shouldOpenModal =
              !scannedLot ||
              scannedLot.stockInLineId !== expectedLot.stockInLineId;
            if (shouldOpenModal) {
              console.log(
                `⚠️ [QR PROCESS] Lot switch (scanned lot ${
                  scannedLot?.lotNo || "not in data"
                } not in active suggested lots)`,
              );
              setSelectedLotForQr(expectedLot);
              handleLotMismatch(
                expectedLot,
                {
                  lotNo: scannedLot?.lotNo || null,
                  itemCode: expectedLot.itemCode,
                  itemName: expectedLot.itemName,
                  inventoryLotLineId: scannedLot?.lotId || null,
                  stockInLineId: scannedStockInLineId,
                },
                qrScanCountAtInvoke,
              );
              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(
              `  [API CALL START] Calling updateStockOutLineStatusByQRCodeAndLotNo`,
            );
            console.log(
              `  [API CALL] API start time: ${new Date().toISOString()}`,
            );
            const res = await updateStockOutLineStatusByQRCodeAndLotNo({
              pickOrderLineId: exactMatch.pickOrderLineId,
              inventoryLotNo: exactMatch.lotNo,
              stockOutLineId: exactMatch.stockOutLineId,
              itemId: exactMatch.itemId,
              status: "checked",
            });
            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()}`,
            );

            if (res.code === "checked" || res.code === "SUCCESS") {
              const entity = res.entity as any;

              // ✅ Batch state updates using startTransition
              const stateUpdateStartTime = performance.now();
              startTransition(() => {
                setQrScanError(false);
                setQrScanSuccess(true);

                setCombinedLotData((prev) =>
                  prev.map((lot) => {
                    if (
                      lot.stockOutLineId === exactMatch.stockOutLineId &&
                      lot.pickOrderLineId === exactMatch.pickOrderLineId
                    ) {
                      return {
                        ...lot,
                        stockOutLineStatus: "checked",
                        stockOutLineQty: entity?.qty ?? lot.stockOutLineQty,
                      };
                    }
                    return lot;
                  }),
                );

                setOriginalCombinedData((prev) =>
                  prev.map((lot) => {
                    if (
                      lot.stockOutLineId === exactMatch.stockOutLineId &&
                      lot.pickOrderLineId === exactMatch.pickOrderLineId
                    ) {
                      return {
                        ...lot,
                        stockOutLineStatus: "checked",
                        stockOutLineQty: entity?.qty ?? lot.stockOutLineQty,
                      };
                    }
                    return lot;
                  }),
                );
              });
              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`,
              );

              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(
                "✅ Status updated locally, no full data refresh needed",
              );
            } else {
              console.warn("Unexpected response code from backend:", res.code);
              startTransition(() => {
                setQrScanError(true);
                setQrScanSuccess(false);
              });
            }
          } 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 不匹配 - 显示确认表单
        // Check if we should allow reopening (different stockInLineId)
        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 confirmation");
          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: Expected stockInLineId=${expectedLot.stockInLineId}, Scanned stockInLineId=${scannedStockInLineId}`,
        );

        // Set selected lot immediately (no transition delay)
        const setSelectedLotStartTime = performance.now();
        setSelectedLotForQr(expectedLot);
        const setSelectedLotTime = performance.now() - setSelectedLotStartTime;
        console.log(
          `  [PERF] Set selected lot time: ${setSelectedLotTime.toFixed(2)}ms`,
        );

        const handleMismatchStartTime = performance.now();
        handleLotMismatch(
          expectedLot,
          {
            lotNo: null,
            itemCode: expectedLot.itemCode,
            itemName: expectedLot.itemName,
            inventoryLotLineId: null,
            stockInLineId: scannedStockInLineId,
          },
          qrScanCountAtInvoke,
        );
        const handleMismatchTime = performance.now() - handleMismatchStartTime;
        console.log(
          `  [PERF] Handle mismatch call time: ${handleMismatchTime.toFixed(
            2,
          )}ms`,
        );

        const totalTime = performance.now() - totalStartTime;
        console.log(
          `⚠️ [PROCESS OUTSIDE QR MISMATCH] Total time before modal: ${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, setSelectedLot=${setSelectedLotTime.toFixed(
            2,
          )}ms, handleMismatch=${handleMismatchTime.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,
      handleLotMismatch,
      processedQrCombinations,
      combinedLotData,
      fetchStockInLineInfoCached,
    ],
  );
  // Store processOutsideQrCode in ref for immediate access (update on every render)
  processOutsideQrCodeRef.current = processOutsideQrCode;

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

    const qrValuesChangeStartTime = performance.now();
    console.log(
      `  [QR VALUES EFFECT] Triggered at: ${new Date().toISOString()}`,
    );
    console.log(`  [QR VALUES EFFECT] qrValues.length: ${qrValues.length}`);
    console.log(`  [QR VALUES EFFECT] qrValues:`, qrValues);

    const latestQr = qrValues[qrValues.length - 1];
    console.log(`  [QR VALUES EFFECT] Latest QR: ${latestQr}`);
    console.log(
      `  [QR VALUES EFFECT] Latest QR detected at: ${new Date().toISOString()}`,
    );

    // ✅ FIXED: Handle test shortcut {2fitestx,y} or {2fittestx,y} where x=itemId, y=stockInLineId
    // Support both formats: {2fitest (2 t's) and {2fittest (3 t's)
    if (
      (latestQr.startsWith("{2fitest") || latestQr.startsWith("{2fittest")) &&
      latestQr.endsWith("}")
    ) {
      // Extract content: remove "{2fitest" or "{2fittest" and "}"
      let content = "";
      if (latestQr.startsWith("{2fittest")) {
        content = latestQr.substring(9, latestQr.length - 1); // Remove "{2fittest" and "}"
      } else if (latestQr.startsWith("{2fitest")) {
        content = latestQr.substring(8, latestQr.length - 1); // Remove "{2fitest" and "}"
      }

      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)) {
          console.log(
            `%c TEST QR: Detected ${latestQr.substring(
              0,
              9,
            )}... - Simulating QR input (itemId=${itemId}, stockInLineId=${stockInLineId})`,
            "color: purple; font-weight: bold",
          );

          // ✅ Simulate QR code JSON format
          const simulatedQr = JSON.stringify({
            itemId: itemId,
            stockInLineId: stockInLineId,
          });

          console.log(`  [TEST QR] Simulated QR content: ${simulatedQr}`);
          console.log(`  [TEST QR] Start time: ${new Date().toISOString()}`);
          const testStartTime = performance.now();

          // ✅ Mark as processed FIRST to avoid duplicate processing
          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);
            }
          }
          setLastProcessedQr(latestQr);
          setProcessedQrCodes(new Set(processedQrCodesRef.current));

          // ✅ Process immediately (bypass QR scanner delay)
          if (processOutsideQrCodeRef.current) {
            processOutsideQrCodeRef
              .current(simulatedQr, qrValues.length)
              .then(() => {
                const testTime = performance.now() - testStartTime;
                console.log(
                  `  [TEST QR] Total processing time: ${testTime.toFixed(
                    2,
                  )}ms (${(testTime / 1000).toFixed(3)}s)`,
                );
                console.log(
                  `  [TEST QR] End time: ${new Date().toISOString()}`,
                );
              })
              .catch((error) => {
                const testTime = performance.now() - testStartTime;
                console.error(
                  `❌ [TEST QR] Error after ${testTime.toFixed(2)}ms:`,
                  error,
                );
              });
          }

          // Reset scan
          if (resetScanRef.current) {
            resetScanRef.current();
          }

          const qrValuesChangeTime =
            performance.now() - qrValuesChangeStartTime;
          console.log(
            `  [QR VALUES EFFECT] Test QR handling time: ${qrValuesChangeTime.toFixed(
              2,
            )}ms`,
          );
          return; // ✅ IMPORTANT: Return early to prevent normal processing
        } else {
          console.warn(
            `  [TEST QR] Invalid itemId or stockInLineId: itemId=${parts[0]}, stockInLineId=${parts[1]}`,
          );
        }
      } else {
        console.warn(
          `  [TEST QR] Invalid format. Expected {2fitestx,y} or {2fittestx,y}, got: ${latestQr}`,
        );
      }
    }

    // 批次确认弹窗：须第二次扫码选择沿用建议批次或切换（不再自动确认）
    if (lotConfirmationOpen) {
      if (isConfirmingLot) {
        return;
      }
      if (lotConfirmSkipNextScanRef.current) {
        lotConfirmSkipNextScanRef.current = false;
        lotConfirmLastQrRef.current = latestQr || "";
        return;
      }
      if (!latestQr) {
        return;
      }
      // Prevent auto-accept from buffered duplicate right after modal opens,
      // but allow intentional second scan of the same QR after debounce window.
      const sameQr = latestQr === lotConfirmLastQrRef.current;
      const justOpened =
        lotConfirmOpenedAtRef.current > 0 &&
        Date.now() - lotConfirmOpenedAtRef.current < 800;
      if (sameQr && justOpened) {
        return;
      }
      lotConfirmLastQrRef.current = latestQr;
      void (async () => {
        try {
          const handled = await handleLotConfirmationByRescan(latestQr);
          if (handled && resetScanRef.current) {
            resetScanRef.current();
          }
        } catch (e) {
          console.error("Lot confirmation rescan failed:", e);
        }
      })();
      return;
    }

    // Skip processing if manual confirmation modal is open
    if (manualLotConfirmationOpen) {
      // Check if this is a different QR code than what triggered the modal
      const modalTriggerQr = lastProcessedQrRef.current;
      if (latestQr === modalTriggerQr) {
        console.log(`  [QR PROCESS] Skipping - manual modal open for same QR`);
        return;
      }
      // If it's a different QR, allow processing
      console.log(
        `  [QR PROCESS] Different QR detected while manual modal open, allowing processing`,
      );
    }

    const qrDetectionStartTime = performance.now();
    console.log(
      `  [QR DETECTION] Latest QR detected: ${latestQr?.substring(0, 50)}...`,
    );
    console.log(`  [QR DETECTION] Detection time: ${new Date().toISOString()}`);
    console.log(
      `  [QR DETECTION] Time since QR scanner set value: ${(
        qrDetectionStartTime - qrValuesChangeStartTime
      ).toFixed(2)}ms`,
    );

    // Skip if already processed (use refs to avoid dependency issues and delays)
    const checkProcessedStartTime = performance.now();
    if (
      processedQrCodesRef.current.has(latestQr) ||
      lastProcessedQrRef.current === latestQr
    ) {
      const checkTime = performance.now() - checkProcessedStartTime;
      console.log(
        `  [QR PROCESS] Already processed check time: ${checkTime.toFixed(
          2,
        )}ms`,
      );
      return;
    }
    const checkTime = performance.now() - checkProcessedStartTime;
    console.log(
      ` [QR PROCESS] Not processed check time: ${checkTime.toFixed(2)}ms`,
    );

    // Handle special shortcut
    if (latestQr === "{2fic}") {
      console.log(
        "  Detected {2fic} shortcut - opening manual lot confirmation form",
      );
      setManualLotConfirmationOpen(true);
      if (resetScanRef.current) {
        resetScanRef.current();
      }
      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);
        }
      }
      setLastProcessedQr(latestQr);
      setProcessedQrCodes((prev) => {
        const newSet = new Set(prev);
        newSet.add(latestQr);
        if (newSet.size > 100) {
          const firstValue = newSet.values().next().value;
          if (firstValue !== undefined) {
            newSet.delete(firstValue);
          }
        }
        return newSet;
      });
      return;
    }

    // Process new QR code immediately (background mode - no modal)
    // Check against refs to avoid state update delays
    if (latestQr && latestQr !== lastProcessedQrRef.current) {
      const processingStartTime = performance.now();
      console.log(
        `  [QR PROCESS] Starting processing at: ${new Date().toISOString()}`,
      );
      console.log(
        `  [QR PROCESS] Time since detection: ${(
          processingStartTime - qrDetectionStartTime
        ).toFixed(2)}ms`,
      );

      // ✅ Process immediately for better responsiveness
      // Clear any pending debounced processing
      if (qrProcessingTimeoutRef.current) {
        clearTimeout(qrProcessingTimeoutRef.current);
        qrProcessingTimeoutRef.current = null;
      }

      // Log immediately (console.log is synchronous)
      console.log(
        `  [QR PROCESS] Processing new QR code with enhanced validation: ${latestQr}`,
      );

      // Update refs immediately (no state update delay) - do this FIRST
      const refUpdateStartTime = performance.now();
      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);
        }
      }
      const refUpdateTime = performance.now() - refUpdateStartTime;
      console.log(
        `  [QR PROCESS] Ref update time: ${refUpdateTime.toFixed(2)}ms`,
      );

      // Process immediately in background - no modal/form needed, no delays
      // Use ref to avoid dependency issues
      const processCallStartTime = performance.now();
      if (processOutsideQrCodeRef.current) {
        processOutsideQrCodeRef
          .current(latestQr, qrValues.length)
          .then(() => {
            const processCallTime = performance.now() - processCallStartTime;
            const totalProcessingTime = performance.now() - processingStartTime;
            console.log(
              `  [QR PROCESS] processOutsideQrCode call time: ${processCallTime.toFixed(
                2,
              )}ms`,
            );
            console.log(
              `  [QR PROCESS] Total processing time: ${totalProcessingTime.toFixed(
                2,
              )}ms (${(totalProcessingTime / 1000).toFixed(3)}s)`,
            );
          })
          .catch((error) => {
            const processCallTime = performance.now() - processCallStartTime;
            const totalProcessingTime = performance.now() - processingStartTime;
            console.error(
              `❌ [QR PROCESS] processOutsideQrCode error after ${processCallTime.toFixed(
                2,
              )}ms:`,
              error,
            );
            console.error(
              `❌ [QR PROCESS] Total processing time before error: ${totalProcessingTime.toFixed(
                2,
              )}ms`,
            );
          });
      }

      // Update state for UI (but don't block on it)
      const stateUpdateStartTime = performance.now();
      setLastProcessedQr(latestQr);
      setProcessedQrCodes(new Set(processedQrCodesRef.current));
      const stateUpdateTime = performance.now() - stateUpdateStartTime;
      console.log(
        `  [QR PROCESS] State update time: ${stateUpdateTime.toFixed(2)}ms`,
      );

      const detectionTime = performance.now() - qrDetectionStartTime;
      const totalEffectTime = performance.now() - qrValuesChangeStartTime;
      console.log(
        `  [QR DETECTION] Total detection time: ${detectionTime.toFixed(2)}ms`,
      );
      console.log(
        `  [QR VALUES EFFECT] Total effect time: ${totalEffectTime.toFixed(
          2,
        )}ms`,
      );
    }

    return () => {
      if (qrProcessingTimeoutRef.current) {
        clearTimeout(qrProcessingTimeoutRef.current);
        qrProcessingTimeoutRef.current = null;
      }
    };
  }, [
    qrValues,
    isManualScanning,
    isRefreshingData,
    combinedLotData.length,
    lotConfirmationOpen,
    manualLotConfirmationOpen,
    handleLotConfirmationByRescan,
    isConfirmingLot,
  ]);
  const renderCountRef = useRef(0);
  const renderStartTimeRef = useRef<number | null>(null);

  // Track render performance
  useEffect(() => {
    renderCountRef.current++;
    const now = performance.now();

    if (renderStartTimeRef.current !== null) {
      const renderTime = now - renderStartTimeRef.current;
      if (renderTime > 100) {
        // Only log slow renders (>100ms)
        console.log(
          `  [PERF] Render #${renderCountRef.current} took ${renderTime.toFixed(
            2,
          )}ms, combinedLotData length: ${combinedLotData.length}`,
        );
      }
      renderStartTimeRef.current = null;
    }

    // Track when lotConfirmationOpen changes
    if (lotConfirmationOpen) {
      renderStartTimeRef.current = performance.now();
      console.log(`  [PERF] Render triggered by lotConfirmationOpen=true`);
    }
  }, [combinedLotData.length, lotConfirmationOpen]);
  // Auto-start scanner only once on mount
  const scannerInitializedRef = useRef(false);

  useEffect(() => {
    if (session && currentUserId && !initializationRef.current) {
      console.log(" Session loaded, initializing pick order...");
      initializationRef.current = true;

      //  Only fetch existing data, no auto-assignment
      fetchAllCombinedLotData();
    }
  }, [session, currentUserId, fetchAllCombinedLotData]);

  // Separate effect for auto-starting scanner (only once, prevents multiple resets)
  useEffect(() => {
    if (session && currentUserId && !scannerInitializedRef.current) {
      scannerInitializedRef.current = true;
      // ✅ Auto-start scanner on mount for tablet use (background mode - no modal)
      console.log("✅ Auto-starting QR scanner in background mode");
      setIsManualScanning(true);
      startScan();
    }
  }, [session, currentUserId, startScan]);

  //  Add event listener for manual assignment
  useEffect(() => {
    const handlePickOrderAssigned = () => {
      console.log("🔄 Pick order assigned event received, refreshing data...");
      fetchAllCombinedLotData();
    };

    window.addEventListener("pickOrderAssigned", handlePickOrderAssigned);

    return () => {
      window.removeEventListener("pickOrderAssigned", handlePickOrderAssigned);
    };
  }, [fetchAllCombinedLotData]);

  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

        try {
          const stockOutLineUpdate = await updateStockOutLineStatus({
            id: selectedLotForQr.stockOutLineId,
            status: "checked",
            qty: selectedLotForQr.stockOutLineQty || 0,
          });
          console.log("Stock out line updated successfully!");
          setQrScanSuccess(true);
          setQrScanError(false);

          // Clear selected lot (scanner stays active)
          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);
        } catch (error) {
          console.error("Error creating stock out line:", error);
        }
      }
    },
    [selectedLotForQr],
  );

  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 [autoAssignStatus, setAutoAssignStatus] = useState<
    "idle" | "checking" | "assigned" | "no_orders"
  >("idle");
  const [autoAssignMessage, setAutoAssignMessage] = useState<string>("");
  const [completionStatus, setCompletionStatus] =
    useState<PickOrderCompletionResponse | null>(null);

  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 resolveSingleSubmitQty = useCallback(
    (lot: any) => {
      const required = Number(
        lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
      );
      const solId = Number(lot.stockOutLineId) || 0;
      const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
      const issuePicked = solId > 0 ? issuePickedQtyBySolId[solId] : undefined;
      if (issuePicked !== undefined && !Number.isNaN(Number(issuePicked))) {
        return Number(issuePicked);
      }
      const fromPick = pickQtyData[lotKey];
      if (
        fromPick !== undefined &&
        fromPick !== null &&
        !Number.isNaN(Number(fromPick))
      ) {
        return Number(fromPick);
      }
      if (lot.noLot === true) {
        return 0;
      }
      if (isInventoryLotLineUnavailable(lot)) {
        return 0;
      }
      if (isLotAvailabilityExpired(lot)) {
        return 0;
      }
      return required;
    },
    [issuePickedQtyBySolId, pickQtyData],
  );
  //  Handle reject lot
  //  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);
  }, []);

  const handlePickExecutionFormSubmit = useCallback(
    async (data: any) => {
      try {
        console.log("Pick execution form submitted:", data);
        const issueData = {
          ...data,
          type: "Do", // Delivery Order Record 类型
          pickerName: session?.user?.name || "",
        };

        const result = await recordPickExecutionIssue(issueData);
        console.log("Pick execution issue recorded:", result);

        if (result && result.code === "SUCCESS") {
          console.log(" Pick execution issue recorded successfully");
          // 关键：issue form 只记录问题，不会更新 SOL.qty
          // 但 batch submit 需要知道“实际拣到多少”，否则会按 requiredQty 补拣到满
          const solId = Number(
            issueData.stockOutLineId || issueData.stockOutLineId === 0
              ? issueData.stockOutLineId
              : data?.stockOutLineId,
          );
          if (solId > 0) {
            const picked = Number(issueData.actualPickQty || 0);
            setIssuePickedQtyBySolId((prev) => {
              const next = { ...prev, [solId]: picked };
              const doId = fgPickOrders[0]?.doPickOrderId;
              if (doId) saveIssuePickedMap(doId, next);
              return next;
            });
            setCombinedLotData((prev) =>
              prev.map((lot) => {
                if (Number(lot.stockOutLineId) === solId) {
                  return {
                    ...lot,
                    actualPickQty: picked,
                    stockOutLineQty: picked,
                  };
                }
                return lot;
              }),
            );
          }
        } else {
          console.error("  Failed to record pick execution issue:", result);
        }

        setPickExecutionFormOpen(false);
        setSelectedLotForExecutionForm(null);
        setQrScanError(false);
        setQrScanSuccess(false);
        setQrScanInput("");
        // ✅ Keep scanner active after form submission - don't stop scanning
        // Only clear processed QR codes for the specific lot, not all
        // setIsManualScanning(false); // Removed - keep scanner active
        // stopScan(); // Removed - keep scanner active
        // resetScan(); // Removed - keep scanner active
        // Don't clear all processed codes - only clear for this specific lot if needed
        await fetchAllCombinedLotData();
      } catch (error) {
        console.error("Error submitting pick execution form:", error);
      }
    },
    [fetchAllCombinedLotData, session, fgPickOrders],
  );

  //  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);
  }, []);

  // Search criteria
  const searchCriteria: Criterion<any>[] = [
    {
      label: t("Pick Order Code"),
      paramName: "pickOrderCode",
      type: "text",
    },
    {
      label: t("Item Code"),
      paramName: "itemCode",
      type: "text",
    },
    {
      label: t("Item Name"),
      paramName: "itemName",
      type: "text",
    },
    {
      label: t("Lot No"),
      paramName: "lotNo",
      type: "text",
    },
  ];

  const handleSearch = useCallback(
    (query: Record<string, any>) => {
      setSearchQuery({ ...query });
      console.log("Search query:", query);

      if (!originalCombinedData) return;

      const filtered = originalCombinedData.filter((lot: any) => {
        const pickOrderCodeMatch =
          !query.pickOrderCode ||
          lot.pickOrderCode
            ?.toLowerCase()
            .includes((query.pickOrderCode || "").toLowerCase());

        const itemCodeMatch =
          !query.itemCode ||
          lot.itemCode
            ?.toLowerCase()
            .includes((query.itemCode || "").toLowerCase());

        const itemNameMatch =
          !query.itemName ||
          lot.itemName
            ?.toLowerCase()
            .includes((query.itemName || "").toLowerCase());

        const lotNoMatch =
          !query.lotNo ||
          lot.lotNo?.toLowerCase().includes((query.lotNo || "").toLowerCase());

        return (
          pickOrderCodeMatch && itemCodeMatch && itemNameMatch && lotNoMatch
        );
      });

      setCombinedLotData(filtered);
      console.log("Filtered lots count:", filtered.length);
    },
    [originalCombinedData],
  );

  const handleReset = useCallback(() => {
    setSearchQuery({});
    if (originalCombinedData) {
      setCombinedLotData(originalCombinedData);
    }
  }, [originalCombinedData]);

  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 === -1 ? -1 : newPageSize,
      });
    },
    [],
  );

  // Pagination data with sorting by routerIndex
  // Remove the sorting logic and just do pagination
  // ✅ Memoize paginatedData to prevent re-renders when modal opens
  const paginatedData = useMemo(() => {
    if (paginationController.pageSize === -1) {
      return combinedLotData; // Show all items
    }
    const startIndex =
      paginationController.pageNum * paginationController.pageSize;
    const endIndex = startIndex + paginationController.pageSize;
    return combinedLotData.slice(startIndex, endIndex); //  No sorting needed
  }, [
    combinedLotData,
    paginationController.pageNum,
    paginationController.pageSize,
  ]);
  const allItemsReady = useMemo(() => {
    if (combinedLotData.length === 0) return false;

    return combinedLotData.every((lot: any) => {
      const status = lot.stockOutLineStatus?.toLowerCase();
      const isRejected =
        status === "rejected" || lot.lotAvailability === "rejected";
      const isCompleted =
        status === "completed" ||
        status === "partially_completed" ||
        status === "partially_complete";
      const isChecked = status === "checked";
      const isPending = status === "pending";

      // ✅ FIXED: 无库存（noLot）行：pending 状态也应该被视为 ready（可以提交）
      // ✅ 過期批號（未換批）：與 noLot 相同，視為可收尾
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      ) {
        return isChecked || isCompleted || isRejected || isPending;
      }

      // 正常 lot：必须已扫描/提交或者被拒收
      return isChecked || isCompleted || isRejected;
    });
  }, [combinedLotData]);
  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;
      }

      try {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: true }));
        const targetUnavailable = isInventoryLotLineUnavailable(lot);
        const effectiveSubmitQty =
          targetUnavailable && submitQty > 0 ? 0 : submitQty;
        // Just Complete: mark checked only, real posting happens in batch submit
        if (effectiveSubmitQty === 0 && source === "justComplete") {
          console.log(`=== SUBMITTING ALL ZEROS CASE ===`);
          console.log(`Lot: ${lot.lotNo}`);
          console.log(`Stock Out Line ID: ${lot.stockOutLineId}`);
          console.log(`Setting status to 'checked' with qty: 0`);

          const updateResult = await updateStockOutLineStatus({
            id: lot.stockOutLineId,
            status: "checked",
            qty: 0,
          });

          console.log("Update result:", updateResult);
          const r: any = updateResult as any;
          const updateOk =
            r?.code === "SUCCESS" ||
            r?.type === "completed" ||
            typeof r?.id === "number" ||
            typeof r?.entity?.id === "number" ||
            (r?.message && r.message.includes("successfully"));
          if (!updateResult || !updateOk) {
            console.error(
              "Failed to update stock out line status:",
              updateResult,
            );
            throw new Error("Failed to update stock out line status");
          }
          applyLocalStockOutLineUpdate(
            Number(lot.stockOutLineId),
            "checked",
            Number(lot.actualPickQty || 0),
          );

          void fetchAllCombinedLotData();
          console.log(
            "Just Complete marked as checked successfully (waiting for batch submit).",
          );

          setTimeout(() => {
            checkAndAutoAssignNext();
          }, 1000);

          return;
        }
        if (effectiveSubmitQty === 0 && source === "singleSubmit") {
          console.log(`=== SUBMITTING ALL ZEROS CASE ===`);
          console.log(`Lot: ${lot.lotNo}`);
          console.log(`Stock Out Line ID: ${lot.stockOutLineId}`);
          console.log(`Setting status to 'checked' with qty: 0`);

          const updateResult = await updateStockOutLineStatus({
            id: lot.stockOutLineId,
            status: "checked",
            qty: 0,
          });

          console.log("Update result:", updateResult);
          const r: any = updateResult as any;
          const updateOk =
            r?.code === "SUCCESS" ||
            r?.type === "completed" ||
            typeof r?.id === "number" ||
            typeof r?.entity?.id === "number" ||
            (r?.message && r.message.includes("successfully"));
          if (!updateResult || !updateOk) {
            console.error(
              "Failed to update stock out line status:",
              updateResult,
            );
            throw new Error("Failed to update stock out line status");
          }
          applyLocalStockOutLineUpdate(
            Number(lot.stockOutLineId),
            "checked",
            Number(lot.actualPickQty || 0),
          );

          void fetchAllCombinedLotData();
          console.log(
            "Just Complete marked as checked successfully (waiting for batch submit).",
          );

          setTimeout(() => {
            checkAndAutoAssignNext();
          }, 1000);

          return;
        }
        //  FIXED: Calculate cumulative quantity correctly
        const currentActualPickQty = lot.actualPickQty || 0;
        const cumulativeQty = currentActualPickQty + effectiveSubmitQty;

        //  FIXED: Determine status based on cumulative quantity vs required quantity
        let newStatus = "partially_completed";

        if (cumulativeQty >= lot.requiredQty) {
          newStatus = "completed";
        } else if (cumulativeQty > 0) {
          newStatus = "partially_completed";
        } else {
          newStatus = "checked"; // QR scanned but no quantity submitted yet
        }

        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(`=====================================`);

        await updateStockOutLineStatus({
          id: lot.stockOutLineId,
          status: newStatus,
          // 后端 updateStatus 的 qty 是“增量 delta”，不能传 cumulativeQty（否则会重复累加导致 out/hold 大幅偏移）
          qty: effectiveSubmitQty,
        });
        applyLocalStockOutLineUpdate(
          Number(lot.stockOutLineId),
          newStatus,
          cumulativeQty,
        );
        // 注意：库存过账（hold->out）与 ledger 由后端 updateStatus 内部统一处理；
        // 前端不再额外调用 updateInventoryLotLineQuantities(operation='pick')，避免 double posting。

        //  Check if pick order is completed when lot status becomes 'completed'
        if (newStatus === "completed" && lot.pickOrderConsoCode) {
          console.log(
            ` Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`,
          );

          try {
            const completionResponse =
              await checkAndCompletePickOrderByConsoCode(
                lot.pickOrderConsoCode,
              );
            console.log(
              ` Pick order completion check result:`,
              completionResponse,
            );

            if (completionResponse.code === "SUCCESS") {
              console.log(
                ` Pick order ${lot.pickOrderConsoCode} completed successfully!`,
              );
            } else if (completionResponse.message === "not completed") {
              console.log(
                `Pick order not completed yet, more lines remaining`,
              );
            } else {
              console.error(
                `  Error checking completion: ${completionResponse.message}`,
              );
            }
          } catch (error) {
            console.error("Error checking pick order completion:", error);
          }
        }

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

        setTimeout(() => {
          checkAndAutoAssignNext();
        }, 1000);
      } catch (error) {
        console.error("Error submitting pick quantity:", error);
      } finally {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: false }));
      }
    },
    [
      fetchAllCombinedLotData,
      checkAndAutoAssignNext,
      actionBusyBySolId,
      applyLocalStockOutLineUpdate,
    ],
  );

  const handleSkip = useCallback(
    async (lot: any) => {
      try {
        console.log(
          "Just Complete clicked, mark checked with 0 qty for lot:",
          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 === "checked" ||
        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 handleStartScan = useCallback(() => {
    const startTime = performance.now();
    console.log(`  [START SCAN] Called at: ${new Date().toISOString()}`);
    console.log(`  [START SCAN] Starting manual QR scan...`);

    setIsManualScanning(true);
    const setManualScanningTime = performance.now() - startTime;
    console.log(
      `  [START SCAN] setManualScanning time: ${setManualScanningTime.toFixed(
        2,
      )}ms`,
    );

    setProcessedQrCodes(new Set());
    setLastProcessedQr("");
    setQrScanError(false);
    setQrScanSuccess(false);

    const beforeStartScanTime = performance.now();
    startScan();
    const startScanTime = performance.now() - beforeStartScanTime;
    console.log(
      `  [START SCAN] startScan() call time: ${startScanTime.toFixed(2)}ms`,
    );

    const totalTime = performance.now() - startTime;
    console.log(
      `  [START SCAN] Total start scan time: ${totalTime.toFixed(2)}ms`,
    );
    console.log(
      `  [START SCAN] Start scan completed at: ${new Date().toISOString()}`,
    );
  }, [startScan]);
  const handlePickOrderSwitch = useCallback(
    async (pickOrderId: number) => {
      if (pickOrderSwitching) return;

      setPickOrderSwitching(true);
      try {
        console.log("  Switching to pick order:", pickOrderId);
        setSelectedPickOrderId(pickOrderId);

        //  强制刷新数据，确保显示正确的 pick order 数据
        await fetchAllCombinedLotData(currentUserId, pickOrderId);
      } catch (error) {
        console.error("Error switching pick order:", error);
      } finally {
        setPickOrderSwitching(false);
      }
    },
    [pickOrderSwitching, currentUserId, fetchAllCombinedLotData],
  );

  const handleStopScan = useCallback(() => {
    console.log("⏸️ Pausing QR scanner...");
    setIsManualScanning(false);
    setQrScanError(false);
    setQrScanSuccess(false);
    stopScan();
    resetScan();
  }, [stopScan, resetScan]);
  // ... existing code around line 1469 ...
  const handlelotnull = useCallback(
    async (lot: any) => {
      //  优先使用 stockouts 中的 id，如果没有则使用 stockOutLineId
      const stockOutLineId = lot.stockOutLineId;

      if (!stockOutLineId) {
        console.error("  No stockOutLineId found for lot:", lot);
        return;
      }
      const solId = Number(stockOutLineId);
      if (solId > 0 && actionBusyBySolId[solId]) {
        console.warn("Action already in progress for stockOutLineId:", solId);
        return;
      }

      try {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: true }));
        //  Step 1: Update stock out line status
        await updateStockOutLineStatus({
          id: stockOutLineId,
          status: "completed",
          qty: 0,
        });

        //  Step 2: Create pick execution issue for no-lot case
        // Get pick order ID from fgPickOrders or use 0 if not available
        const pickOrderId =
          lot.pickOrderId || fgPickOrders[0]?.pickOrderId || 0;
        const pickOrderCode =
          lot.pickOrderCode ||
          fgPickOrders[0]?.pickOrderCode ||
          lot.pickOrderConsoCode ||
          "";

        const issueData: PickExecutionIssueData = {
          type: "Do", // Delivery Order type
          pickOrderId: pickOrderId,
          pickOrderCode: pickOrderCode,
          pickOrderCreateDate: dayjs().format("YYYY-MM-DD"), //  Use dayjs format
          pickExecutionDate: dayjs().format("YYYY-MM-DD"),
          pickOrderLineId: lot.pickOrderLineId,
          itemId: lot.itemId,
          itemCode: lot.itemCode || "",
          itemDescription: lot.itemName || "",
          lotId: null, //  No lot available
          lotNo: null, //  No lot number
          storeLocation: lot.location || "",
          requiredQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
          actualPickQty: 0, //  No items picked (no lot available)
          missQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0, //  All quantity is missing
          badItemQty: 0,
          issueRemark: `No lot available for this item. Handled via handlelotnull.`,
          pickerName: session?.user?.name || "",
        };

        const result = await recordPickExecutionIssue(issueData);
        console.log(" Pick execution issue created for no-lot item:", result);

        if (result && result.code === "SUCCESS") {
          console.log(" No-lot item handled and issue recorded successfully");
        } else {
          console.error("  Failed to record pick execution issue:", result);
        }

        //  Step 3: Refresh data
        await fetchAllCombinedLotData();
      } catch (error) {
        console.error("  Error in handlelotnull:", error);
      } finally {
        if (solId > 0)
          setActionBusyBySolId((prev) => ({ ...prev, [solId]: false }));
      }
    },
    [
      fetchAllCombinedLotData,
      session,
      currentUserId,
      fgPickOrders,
      actionBusyBySolId,
    ],
  );
  const handleBatchScan = useCallback(async () => {
    const startTime = performance.now();
    console.log(`  [BATCH SCAN START]`);
    console.log(`  Start time: ${new Date().toISOString()}`);

    // 获取所有活跃批次（未扫描的）
    const activeLots = combinedLotData.filter((lot) => {
      return (
        lot.lotAvailability !== "rejected" &&
        lot.stockOutLineStatus !== "rejected" &&
        lot.stockOutLineStatus !== "completed" &&
        lot.stockOutLineStatus !== "checked" && // ✅ 只处理未扫描的
        lot.processingStatus !== "completed" &&
        lot.noLot !== true &&
        lot.lotNo // ✅ 必须有 lotNo
      );
    });

    if (activeLots.length === 0) {
      console.log("No active lots to scan");
      return;
    }

    console.log(
      `📦 Batch scanning ${activeLots.length} active lots using batch API...`,
    );

    try {
      // ✅ 转换为批量扫描 API 所需的格式
      const lines: BatchScanLineRequest[] = activeLots.map((lot) => ({
        pickOrderLineId: Number(lot.pickOrderLineId),
        inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null,
        pickOrderConsoCode: String(lot.pickOrderConsoCode || ""),
        lotNo: lot.lotNo || null,
        itemId: Number(lot.itemId),
        itemCode: String(lot.itemCode || ""),
        stockOutLineId: lot.stockOutLineId ? Number(lot.stockOutLineId) : null, // ✅ 新增
      }));

      const request: BatchScanRequest = {
        userId: currentUserId || 0,
        lines: lines,
      };

      console.log(`📤 Sending batch scan request with ${lines.length} lines`);
      console.log(`📋 Request data:`, JSON.stringify(request, null, 2));

      const scanStartTime = performance.now();

      // ✅ 使用新的批量扫描 API（一次性处理所有请求）
      const result = await batchScan(request);

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

      // ✅ 刷新数据以获取最新的状态
      const refreshStartTime = performance.now();
      await fetchAllCombinedLotData();
      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 SCAN 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);
        setQrScanError(false);
      } else {
        console.error("❌ Batch scan failed:", result);
        setQrScanError(true);
        setQrScanSuccess(false);
      }
    } catch (error) {
      console.error("❌ Error in batch scan:", error);
      setQrScanError(true);
      setQrScanSuccess(false);
    }
  }, [combinedLotData, fetchAllCombinedLotData, currentUserId]);
  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;
      }
      // ✅ noLot / 過期批號：與 noLot 相同，允許 pending（未換批也可批量收尾）
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      ) {
        return (
          status === "checked" ||
          status === "pending" ||
          status === "partially_completed" ||
          status === "PARTIALLY_COMPLETE"
        );
      }
      return (
        status === "checked" ||
        status === "partially_completed" ||
        status === "PARTIALLY_COMPLETE"
      );
    });

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

    setIsSubmittingAll(true);
    console.log(
      `📦 Submitting ${scannedLots.length} scanned items using batchSubmitList...`,
    );

    try {
      // 转换为 batchSubmitList 所需的格式（与后端 QrPickBatchSubmitRequest 匹配）
      const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => {
        // 1. 需求数量
        const requiredQty = Number(
          lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
        );

        // 2. 当前已经拣到的数量
        // issue form 不会写回 SOL.qty，所以如果这条 SOL 有 issue，就用 issue form 的 actualPickQty 作为“已拣到数量”
        const solId = Number(lot.stockOutLineId) || 0;
        const issuePicked =
          solId > 0 ? issuePickedQtyBySolId[solId] : undefined;
        const currentActualPickQty = Number(
          issuePicked ?? lot.actualPickQty ?? 0,
        );

        // 🔹 判断是否走“只改状态模式”
        //   这里先给一个简单条件示例：如果你不想再补拣，只想把当前数量标记完成，
        //   就让这个条件为 true（后面你可以根据业务加 UI 开关或别的 flag）。
        const onlyComplete =
          lot.stockOutLineStatus === "partially_completed" ||
          issuePicked !== undefined;

        const expired = isLotAvailabilityExpired(lot);
        const unavailable = isInventoryLotLineUnavailable(lot);

        let targetActual: number;
        let newStatus: string;

        // ✅ 過期且未在 Issue 填實際量：與 noLot 一樣按 0 完成
        if (unavailable) {
          targetActual = currentActualPickQty;
          newStatus = "completed";
        } else if (expired && issuePicked === undefined) {
          targetActual = 0;
          newStatus = "completed";
        } else if (onlyComplete) {
          targetActual = currentActualPickQty;
          newStatus = "completed";
        } else {
          const remainingQty = Math.max(0, requiredQty - currentActualPickQty);
          const cumulativeQty = currentActualPickQty + remainingQty;

          targetActual = cumulativeQty;

          newStatus = "partially_completed";
          if (requiredQty > 0 && cumulativeQty >= requiredQty) {
            newStatus = "completed";
          }
        }

        return {
          stockOutLineId: Number(lot.stockOutLineId) || 0,
          pickOrderLineId: Number(lot.pickOrderLineId),
          inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null,
          requiredQty,
          // 后端用 targetActual - 当前 qty 算增量，onlyComplete 时就是 0
          actualPickQty: targetActual,
          stockOutLineStatus: newStatus,
          pickOrderConsoCode: String(lot.pickOrderConsoCode || ""),
          noLot: Boolean(lot.noLot === true),
        };
      });

      const request: batchSubmitListRequest = {
        userId: currentUserId || 0,
        lines: lines,
      };

      console.log(`📤 Sending batch submit request with ${lines.length} lines`);
      console.log(`📋 Request data:`, JSON.stringify(request, null, 2));
      const submitStartTime = performance.now();

      // 使用 batchSubmitList API
      const result = await batchSubmitList(request);

      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);

      // Refresh data once after batch submission
      const refreshStartTime = performance.now();
      await fetchAllCombinedLotData();
      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 (onSwitchToRecordTab) {
            onSwitchToRecordTab();
          }
          if (onRefreshReleasedOrderCount) {
            onRefreshReleasedOrderCount();
          }
        }, 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,
    fetchAllCombinedLotData,
    checkAndAutoAssignNext,
    currentUserId,
    onSwitchToRecordTab,
    onRefreshReleasedOrderCount,
    issuePickedQtyBySolId,
  ]);

  //  Calculate scanned items count
  //  Calculate scanned items count (should match handleSubmitAllScanned filter logic)
  const scannedItemsCount = useMemo(() => {
    const filtered = combinedLotData.filter((lot) => {
      const status = lot.stockOutLineStatus;
      const statusLower = String(status || "").toLowerCase();
      if (statusLower === "completed" || statusLower === "complete") {
        return false;
      }
      // ✅ 与 handleSubmitAllScanned 完全保持一致
      if (
        lot.noLot === true ||
        isLotAvailabilityExpired(lot) ||
        isInventoryLotLineUnavailable(lot)
      ) {
        return (
          status === "checked" ||
          status === "pending" ||
          status === "partially_completed" ||
          status === "PARTIALLY_COMPLETE"
        );
      }
      return (
        status === "checked" ||
        status === "partially_completed" ||
        status === "PARTIALLY_COMPLETE"
      );
    });

    //  添加调试日志
    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]);
  /*
  //  ADD THIS: Auto-stop scan when no data available
  useEffect(() => {
    if (isManualScanning && combinedLotData.length === 0) {
      console.log("⏹️ No data available, auto-stopping QR scan...");
      handleStopScan();
    }
  }, [combinedLotData.length, isManualScanning, handleStopScan]);
   

  //  Cleanup effect
  useEffect(() => {
    return () => {
      // Cleanup when component unmounts (e.g., when switching tabs)
      if (isManualScanning) {
        console.log(
          "🧹 Pick execution component unmounting, stopping QR scanner...",
        );
        stopScan();
        resetScan();
      }
    };
  }, [isManualScanning, stopScan, resetScan]);

  const getStatusMessage = useCallback(
    (lot: any) => {
      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}
      onBatchScan={handleBatchScan}
      filterActive={(lot) =>
        lot.lotAvailability !== "rejected" &&
        lot.stockOutLineStatus !== "rejected" &&
        lot.stockOutLineStatus !== "completed"
      }
    >
      <FormProvider {...formProps}>
        <Stack spacing={2}>
          <Box
            sx={{
              position: "fixed",
              top: 0,
              left: 0,
              right: 0,
              zIndex: 1100, // Higher than other elements
              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={t(
                "QR code does not match any item in current orders.",
              )}
              successMessage={t("QR code verified.")}
            />
          </Box>
          {/* DO Header  }

          {/*  保留：Combined Lot Table - 包含所有 QR 扫描功能  }
          <Box>
            <Box
              sx={{
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                mb: 2,
                mt: 10,
              }}
            >
              <Typography variant="h6" gutterBottom sx={{ mb: 0 }}>
                {t("All Pick Order Lots")}
              </Typography>

              <Box sx={{ display: "flex", gap: 2, alignItems: "center" }}>
                {/* Scanner status indicator (always visible)  }
                {/*
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
          <QrCodeIcon 
            sx={{ 
              color: isManualScanning ? '#4caf50' : '#9e9e9e',
              animation: isManualScanning ? 'pulse 2s infinite' : 'none',
              '@keyframes pulse': {
                '0%, 100%': { opacity: 1 },
                '50%': { opacity: 0.5 }
              }
            }} 
          />
          <Typography variant="body2" sx={{ color: isManualScanning ? '#4caf50' : '#9e9e9e' }}>
            {isManualScanning ? t("Scanner Active") : t("Scanner Inactive")}
          </Typography>
        </Box>
         }

                {/* Pause/Resume button instead of Start/Stop  }
                {isManualScanning ? (
                  <Button
                    variant="outlined"
                    startIcon={<QrCodeIcon />}
                    onClick={handleStopScan}
                    color="secondary"
                    sx={{ minWidth: "120px" }}
                  >
                    {t("Stop QR Scan")}
                  </Button>
                ) : (
                  <Button
                    variant="contained"
                    startIcon={<QrCodeIcon />}
                    onClick={handleStartScan}
                    color="primary"
                    sx={{ minWidth: "120px" }}
                  >
                    {t("Start QR Scan")}
                  </Button>
                )}

                {/*  保留：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, color: "white" }}
                      />
                      {t("Submitting...")}
                    </>
                  ) : (
                    `${t("Submit All Scanned")} (${scannedItemsCount})`
                  )}
                </Button>
              </Box>
            </Box>

            {fgPickOrders.length > 0 && (
              <Paper sx={{ p: 2, mb: 2 }}>
                <Stack spacing={2}>
                  {/* 基本信息  }
                  <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap">
                    <Typography variant="subtitle1">
                      <strong>{t("Shop Name")}:</strong>{" "}
                      {fgPickOrders[0].shopName || "-"}
                    </Typography>
                    <Typography variant="subtitle1">
                      <strong>{t("Store ID")}:</strong>{" "}
                      {fgPickOrders[0].storeId || "-"}
                    </Typography>
                    <Typography variant="subtitle1">
                      <strong>{t("Ticket No.")}:</strong>{" "}
                      {fgPickOrders[0].ticketNo || "-"}
                    </Typography>
                    <Typography variant="subtitle1">
                      <strong>{t("Departure Time")}:</strong>{" "}
                      {formatDepartureTime(fgPickOrders[0].DepartureTime)}
                    </Typography>
                  </Stack>

                  {/*  改进：三个字段显示在一起，使用表格式布局  }
                  {/*  改进：三个字段合并显示  }
                  {/*  改进：表格式显示每个 pick order  }
                  <Box
                    sx={{
                      p: 2,
                      backgroundColor: "#f5f5f5",
                      borderRadius: 1,
                    }}
                  >
                    <Typography
                      variant="subtitle2"
                      sx={{ mb: 1, fontWeight: "bold" }}
                    >
                      {t("Pick Orders Details")}:
                    </Typography>

                    {(() => {
                      const pickOrderCodes = fgPickOrders[0].pickOrderCodes as
                        | string[]
                        | string
                        | undefined;
                      const deliveryNos = fgPickOrders[0].deliveryNos as
                        | string[]
                        | string
                        | undefined;
                      const lineCounts = fgPickOrders[0].lineCountsPerPickOrder;

                      const pickOrderCodesArray = Array.isArray(pickOrderCodes)
                        ? pickOrderCodes
                        : typeof pickOrderCodes === "string"
                          ? pickOrderCodes.split(", ")
                          : [];

                      const deliveryNosArray = Array.isArray(deliveryNos)
                        ? deliveryNos
                        : typeof deliveryNos === "string"
                          ? deliveryNos.split(", ")
                          : [];

                      const lineCountsArray = Array.isArray(lineCounts)
                        ? lineCounts
                        : [];

                      const maxLength = Math.max(
                        pickOrderCodesArray.length,
                        deliveryNosArray.length,
                        lineCountsArray.length,
                      );

                      if (maxLength === 0) {
                        return (
                          <Typography variant="body2" color="text.secondary">
                            -
                          </Typography>
                        );
                      }

                      //  使用与外部基本信息相同的样式
                      return Array.from({ length: maxLength }, (_, idx) => (
                        <Stack
                          key={idx}
                          direction="row"
                          spacing={4}
                          useFlexGap
                          flexWrap="wrap"
                          sx={{ mb: idx < maxLength - 1 ? 1 : 0 }} // 除了最后一行，都添加底部间距
                        >
                          <Typography variant="subtitle1">
                            <strong>{t("Delivery Order")}:</strong>{" "}
                            {deliveryNosArray[idx] || "-"}
                          </Typography>
                          <Typography variant="subtitle1">
                            <strong>{t("Pick Order")}:</strong>{" "}
                            {pickOrderCodesArray[idx] || "-"}
                          </Typography>
                          <Typography variant="subtitle1">
                            <strong>{t("Finsihed good items")}:</strong>{" "}
                            {lineCountsArray[idx] || "-"}
                            <strong>{t("kinds")}</strong>
                          </Typography>
                        </Stack>
                      ));
                    })()}
                  </Box>
                </Stack>
              </Paper>
            )}

            <TableContainer component={Paper}>
              <Table>
                <TableHead>
                  <TableRow>
                    <TableCell>{t("Index")}</TableCell>
                    <TableCell>{t("Route")}</TableCell>
                    <TableCell>{t("Item Code")}</TableCell>
                    <TableCell>{t("Item Name")}</TableCell>
                    <TableCell>{t("Lot#")}</TableCell>
                    <TableCell align="right">
                      {t("Lot Required Pick 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>
                  ) : (
                    // 在第 1797-1938 行之间，将整个 map 函数修改为：
                    paginatedData.map((lot, index) => {
                      //  检查是否是 issue lot
                      const isIssueLot =
                        lot.stockOutLineStatus === "rejected" || !lot.lotNo;
                      const rowSolId = Number(lot.stockOutLineId);
                      const lotSwitchErr = Number.isFinite(rowSolId)
                        ? lotSwitchFailByStockOutLineId[rowSolId]
                        : undefined;

                      return (
                        <TableRow
                          key={`${lot.pickOrderLineId}-${lot.lotId || "null"}`}
                          sx={{
                            //backgroundColor: isIssueLot ? '#fff3e0' : 'inherit',
                            // opacity: isIssueLot ? 0.6 : 1,
                            "& .MuiTableCell-root": {
                              //color: isIssueLot ? 'warning.main' : 'inherit'
                            },
                          }}
                        >
                          <TableCell>
                            <Typography variant="body2" fontWeight="bold">
                              {paginationController.pageNum *
                                paginationController.pageSize +
                                index +
                                1}
                            </Typography>
                          </TableCell>
                          <TableCell>
                            <Typography variant="body2">
                              {lot.routerRoute || "-"}
                            </Typography>
                          </TableCell>
                          <TableCell>{lot.itemCode}</TableCell>
                          <TableCell>
                            {lot.itemName + "(" + lot.stockUnit + ")"}
                          </TableCell>
                          <TableCell>
                            <Box>
                              <Typography
                                sx={{
                                  color: isInventoryLotLineUnavailable(lot)
                                    ? "error.main"
                                    : lot.lotAvailability === "expired"
                                      ? "warning.main"
                                      : "inherit",
                                }}
                              >
                                {lot.lotNo ? (
                                  isInventoryLotLineUnavailable(lot) ? (
                                    <>
                                      {lot.lotNo}{" "}
                                      {t(
                                        "is unavable. Please check around have available QR code or not.",
                                      )}
                                    </>
                                  ) : lot.lotAvailability === "expired" ? (
                                    <>
                                      {lot.lotNo}{" "}
                                      {t(
                                        "is expired. Please check around have available QR code or not.",
                                      )}
                                    </>
                                  ) : (
                                    lot.lotNo
                                  )
                                ) : (
                                  t(
                                    "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.",
                                  )
                                )}
                              </Typography>
                              {lotSwitchErr ? (
                                <Typography
                                  variant="body2"
                                  color="error"
                                  sx={{
                                    mt: 0.5,
                                    display: "block",
                                    fontWeight: 500,
                                  }}
                                >
                                  {lotSwitchErr}
                                </Typography>
                              ) : null}
                            </Box>
                          </TableCell>
                          <TableCell align="right">
                            {(() => {
                              const requiredQty = lot.requiredQty || 0;
                              return (
                                requiredQty.toLocaleString() +
                                "(" +
                                lot.uomShortDesc +
                                ")"
                              );
                            })()}
                          </TableCell>

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

                              // rejected lot：显示红色勾选（已扫描但被拒绝）
                              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>
                                );
                              }

                              // 過期批號：與 noLot 同類——視為已掃到/可處理（含 pending），顯示警示色勾選
                              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>
                                );
                              }

                              // 正常 lot：已扫描（checked/partially_completed/completed）
                              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>
                                );
                              }

                              // noLot 且已完成/部分完成：显示红色勾选
                              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">
                            {isInventoryLotLineUnavailable(lot)
                              ? 0
                              : resolveSingleSubmitQty(lot)}
                          </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);

                                // ✅ rejected lot：显示提示文本（换行显示）
                                if (isRejected && !isNoLot) {
                                  return (
                                    <Typography
                                      variant="body2"
                                      color="error.main"
                                      sx={{
                                        textAlign: "center",
                                        whiteSpace: "normal",
                                        wordBreak: "break-word",
                                        maxWidth: "200px",
                                        lineHeight: 1.5,
                                      }}
                                    >
                                      {t(
                                        "This lot is rejected, please scan another lot.",
                                      )}
                                    </Typography>
                                  );
                                }

                                // noLot 情况：只显示 Issue 按钮
                                if (isNoLot) {
                                  return (
                                    <Button
                                      variant="outlined"
                                      size="small"
                                      onClick={() => handlelotnull(lot)}
                                      /*
            disabled={
              status === 'completed' ||
              (Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
            }
             
                                      disabled={true}
                                      sx={{
                                        fontSize: "0.7rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "60px",
                                        borderColor: "warning.main",
                                        color: "warning.main",
                                      }}
                                    >
                                      {t("Issue")}
                                    </Button>
                                  );
                                }

                                // 正常 lot：显示 Submit 和 Issue 按钮
                                return (
                                  <Stack
                                    direction="row"
                                    spacing={1}
                                    alignItems="center"
                                  >
                                    <Button
                                      variant="contained"
                                      onClick={() => {
                                        const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
                                        const submitQty =
                                          resolveSingleSubmitQty(lot);
                                        handlePickQtyChange(lotKey, submitQty);
                                        handleSubmitPickQtyWithQty(
                                          lot,
                                          submitQty,
                                          "singleSubmit",
                                        );
                                      }}
                                      /*
            disabled={
              lot.lotAvailability === 'expired' || 
              isInventoryLotLineUnavailable(lot) || 
              lot.lotAvailability === 'rejected' ||
              lot.stockOutLineStatus === 'completed' ||
              lot.stockOutLineStatus === 'pending' ||
              (Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
            }
 
                                      disabled={true}
                                      sx={{
                                        fontSize: "0.75rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "70px",
                                      }}
                                    >
                                      {t("Submit")}
                                    </Button>

                                    <Button
                                      variant="outlined"
                                      size="small"
                                      onClick={() =>
                                        handlePickExecutionForm(lot)
                                      }
                                      /*
            disabled={
              lot.lotAvailability === 'expired' || 
              lot.stockOutLineStatus === 'completed' ||
              (Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true)
            } 

                                      disabled={true}
                                      sx={{
                                        fontSize: "0.7rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "60px",
                                        borderColor: "warning.main",
                                        color: "warning.main",
                                      }}
                                      title={t("Report missing or bad items")}
                                    >
                                      {t("Edit")}
                                    </Button>
                                    <Button
                                      variant="outlined"
                                      size="small"
                                      onClick={() => handleSkip(lot)}
                                      title={
                                        isUnavailableLot
                                          ? t(
                                              "is unavable. Please check around have available QR code or not.",
                                            )
                                          : undefined
                                      }
                                      disabled={
                                        lot.stockOutLineStatus ===
                                          "completed" ||
                                        lot.stockOutLineStatus === "checked" ||
                                        lot.stockOutLineStatus ===
                                          "partially_completed" ||
                                        lot.lotAvailability === "expired" ||
                                        isUnavailableLot ||
                                        // 使用 issue form 後，禁用「Just Completed」（避免再次点击造成重复提交）
                                        (Number(lot.stockOutLineId) > 0 &&
                                          issuePickedQtyBySolId[
                                            Number(lot.stockOutLineId)
                                          ] !== undefined) ||
                                        (Number(lot.stockOutLineId) > 0 &&
                                          actionBusyBySolId[
                                            Number(lot.stockOutLineId)
                                          ] === true)
                                      }
                                      sx={{
                                        fontSize: "0.7rem",
                                        py: 0.5,
                                        minHeight: "28px",
                                        minWidth: "60px",
                                      }}
                                    >
                                      {t("Just Completed")}
                                    </Button>
                                  </Stack>
                                );
                              })()}
                            </Box>
                          </TableCell>
                        </TableRow>
                      );
                    })
                  )}
                </TableBody>
              </Table>
            </TableContainer>

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

        {/* QR Code Scanner works in background - no modal needed  }
        <ManualLotConfirmationModal
          open={manualLotConfirmationOpen}
          onClose={() => {
            setManualLotConfirmationOpen(false);
          }}
          onConfirm={handleManualLotConfirmation}
          expectedLot={expectedLotData}
          scannedLot={scannedLotData}
          isLoading={isConfirmingLot}
        />
        {/*  保留：Lot Confirmation Modal  }
        {lotConfirmationOpen && expectedLotData && scannedLotData && (
          <LotConfirmationModal
            open={lotConfirmationOpen}
            onClose={() => {
              console.log(
                `  [LOT CONFIRM MODAL] Closing modal, reset scanner and release raw-QR dedupe`,
              );
              // 1) Reset scanner buffer first to avoid immediate reopen from buffered same QR.
              if (resetScanRef.current) {
                resetScanRef.current();
              }
              // 2) Close modal state.
              clearLotConfirmationState(false);
              // 3) Release raw-QR dedupe after a short delay so user can re-scan B/C again.
              setTimeout(() => {
                lastProcessedQrRef.current = "";
                processedQrCodesRef.current.clear();
              }, 250);
            }}
            onConfirm={handleLotConfirmation}
            expectedLot={expectedLotData}
            scannedLot={scannedLotData}
            isLoading={isConfirmingLot}
            errorMessage={lotConfirmationError}
          />
        )}

        {/*  保留：Good Pick Execution Form Modal  }
        {pickExecutionFormOpen && selectedLotForExecutionForm && (
          <GoodPickExecutionForm
            open={pickExecutionFormOpen}
            onClose={() => {
              setPickExecutionFormOpen(false);
              setSelectedLotForExecutionForm(null);
            }}
            onSubmit={handlePickExecutionFormSubmit}
            selectedLot={selectedLotForExecutionForm}
            selectedPickOrderLine={{
              id: selectedLotForExecutionForm.pickOrderLineId,
              itemId: selectedLotForExecutionForm.itemId,
              itemCode: selectedLotForExecutionForm.itemCode,
              itemName: selectedLotForExecutionForm.itemName,
              pickOrderCode: selectedLotForExecutionForm.pickOrderCode,
              availableQty: selectedLotForExecutionForm.availableQty || 0,
              requiredQty: selectedLotForExecutionForm.requiredQty || 0,
              // uomCode: selectedLotForExecutionForm.uomCode || '',
              uomDesc: selectedLotForExecutionForm.uomDesc || "",
              pickedQty: selectedLotForExecutionForm.actualPickQty || 0,
              uomShortDesc: selectedLotForExecutionForm.uomShortDesc || "",
              suggestedList: [],
              noLotLines: [],
            }}
            pickOrderId={selectedLotForExecutionForm.pickOrderId}
            pickOrderCreateDate={new Date()}
          />
        )}

        <LotLabelPrintModal
          open={lotLabelPrintModalOpen}
          onClose={() => {
            setLotLabelPrintModalOpen(false);
            setLotLabelPrintReminderText(null);
          }}
          initialPayload={lotLabelPrintInitialPayload}
          defaultPrinterName={defaultLabelPrinterName}
          hideScanSection
          reminderText={lotLabelPrintReminderText ?? undefined}
          statusTitleText="此批號的已用完/已過期"
          warehouseCodePrefixFilter={lotFloorPrefixFilter}
          hideTriggeredLot
        />
      </FormProvider>
    </TestQrCodeProvider>
  );
};

export default PickExecution;
 */