"use client";

import {
  Box,
  Button,
  Stack,
  TextField,
  Typography,
  Alert,
  CircularProgress,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  Checkbox,
  TablePagination,
  Modal,
} from "@mui/material";
import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider';
import { useCallback, useEffect, useState, useRef, useMemo, startTransition } from "react";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
import {
  updateStockOutLineStatus,
  createStockOutLine,
  recordPickExecutionIssue,
  //applyPickExecutionHoldAndChecked,
  fetchFGPickOrders,
  FGPickOrderResponse, 
  autoAssignAndReleasePickOrder, 
  AutoAssignReleaseResponse,
  checkPickOrderCompletion,
  PickOrderCompletionResponse,
  checkAndCompletePickOrderByConsoCode,
  confirmLotSubstitution,
  updateStockOutLineStatusByQRCodeAndLotNo, // ✅ 添加
  batchSubmitList, // ✅ 添加
  batchSubmitListRequest, // ✅ 添加
  batchSubmitListLineRequest,
} from "@/app/api/pickOrder/actions";
//  修改：使用 Job Order API
import { 
  assignJobOrderPickOrder,
  fetchJobOrderLotsHierarchicalByPickOrderId,
  updateJoPickOrderHandledBy,
  JobOrderLotsHierarchicalResponse,
  applyPickExecutionHoldAndChecked
} from "@/app/api/jo/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, analyzeQrCode, fetchLotDetail  } from "@/app/api/inventory/actions";
import QrCodeIcon from '@mui/icons-material/QrCode';
import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import GoodPickExecutionForm from "./JobPickExecutionForm";
import FGPickOrderCard from "./FGPickOrderCard"; 
import LotConfirmationModal from "./LotConfirmationModal";
import LinearProgressWithLabel from "../common/LinearProgressWithLabel";
import ScanStatusAlert from "../common/ScanStatusAlert";
interface Props {
  filterArgs: Record<string, any>;
  //onSwitchToRecordTab: () => void;
  onBackToList?: () => void;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  const [selectedFloor, setSelectedFloor] = useState<string | null>(null);
  const [manualInputSubmitted, setManualInputSubmitted] = useState<boolean>(false);
  const [manualInputError, setManualInputError] = useState<boolean>(false);
  const [isProcessingQr, setIsProcessingQr] = useState<boolean>(false);
  const [qrScanFailed, setQrScanFailed] = useState<boolean>(false);
  const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
  
  const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set());
  const [scannedQrResult, setScannedQrResult] = useState<string>('');
  
  // Process scanned QR codes
  useEffect(() => {
    if (qrValues.length > 0 && lot && !isProcessingQr && !qrScanSuccess) {
      const latestQr = qrValues[qrValues.length - 1];
      
      if (processedQrCodes.has(latestQr)) {
        console.log("QR code already processed, skipping...");
        return;
      }
      
      setProcessedQrCodes(prev => new Set(prev).add(latestQr));
      
      try {
        const qrData = JSON.parse(latestQr);
        
        if (qrData.stockInLineId && qrData.itemId) {
          setIsProcessingQr(true);
          setQrScanFailed(false);
          
          fetchStockInLineInfo(qrData.stockInLineId)
            .then((stockInLineInfo) => {
              console.log("Stock in line info:", stockInLineInfo);
              setScannedQrResult(stockInLineInfo.lotNo || 'Unknown lot number');
              
              if (stockInLineInfo.lotNo === lot.lotNo) {
                console.log(` QR Code verified for lot: ${lot.lotNo}`);
                setQrScanSuccess(true);
                onQrCodeSubmit(lot.lotNo);
                onClose();
                resetScan();
              } else {
                console.log(`❌ QR Code mismatch. Expected: ${lot.lotNo}, Got: ${stockInLineInfo.lotNo}`);
                setQrScanFailed(true);
                setManualInputError(true);
                setManualInputSubmitted(true);
              }
            })
            .catch((error) => {
              console.error("Error fetching stock in line info:", error);
              setScannedQrResult('Error fetching data');
              setQrScanFailed(true);
              setManualInputError(true);
              setManualInputSubmitted(true);
            })
            .finally(() => {
              setIsProcessingQr(false);
            });
        } else {
          const qrContent = latestQr.replace(/[{}]/g, '');
          setScannedQrResult(qrContent);
          
          if (qrContent === lot.lotNo) {
            setQrScanSuccess(true);
            onQrCodeSubmit(lot.lotNo);
            onClose();
            resetScan();
          } else {
          setQrScanFailed(true);
            setManualInputError(true);
            setManualInputSubmitted(true);
          }
        }
      } catch (error) {
        console.log("QR code is not JSON format, trying direct comparison");
        const qrContent = latestQr.replace(/[{}]/g, '');
        setScannedQrResult(qrContent);
        
        if (qrContent === lot.lotNo) {
          setQrScanSuccess(true);
          onQrCodeSubmit(lot.lotNo);
          onClose();
          resetScan();
        } else {
        setQrScanFailed(true);
          setManualInputError(true);
          setManualInputSubmitted(true);
        }
      }
    }
  }, [qrValues, lot, onQrCodeSubmit, onClose, resetScan, isProcessingQr, qrScanSuccess, processedQrCodes]);

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

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

  // Auto-submit manual input when it matches
  useEffect(() => {
    if (manualInput.trim() === lot?.lotNo && manualInput.trim() !== '' && !qrScanFailed && !qrScanSuccess) {
      console.log(' Auto-submitting manual input:', manualInput.trim());
      
      const timer = setTimeout(() => {
        setQrScanSuccess(true);
        onQrCodeSubmit(lot.lotNo);
    onClose();
        setManualInput('');
        setManualInputError(false);
        setManualInputSubmitted(false);
      }, 200);
      
      return () => clearTimeout(timer);
    }
  }, [manualInput, lot, onQrCodeSubmit, onClose, qrScanFailed, qrScanSuccess]);

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

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

  return (
    <Modal open={open} onClose={onClose}>
      <Box sx={{
        position: 'absolute',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
        bgcolor: 'background.paper',
        p: 3,
        borderRadius: 2,
        minWidth: 400,
      }}>
        <Typography variant="h6" gutterBottom>
          {t("QR Code Scan for Lot")}: {lot?.lotNo}
        </Typography>
        
        {isProcessingQr && (
          <Box sx={{ mb: 2, p: 2, backgroundColor: '#e3f2fd', borderRadius: 1 }}>
            <Typography variant="body2" color="primary">
              {t("Processing QR code...")}
            </Typography>
          </Box>
        )}
        
        <Box sx={{ mb: 2 }}>
          <Typography variant="body2" gutterBottom>
            <strong>{t("Manual Input")}:</strong>
          </Typography>
          <TextField
            fullWidth
            size="small"
            value={manualInput}
            onChange={(e) => {
              setManualInput(e.target.value);
              if (qrScanFailed || manualInputError) {
                setQrScanFailed(false);
                setManualInputError(false);
                setManualInputSubmitted(false);
              }
            }}
            sx={{ mb: 1 }}
            error={manualInputSubmitted && manualInputError}
            helperText={
              manualInputSubmitted && manualInputError
                ? `${t("The input is not the same as the expected lot number.")}`
                : ''
            }
          />

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

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

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

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

const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
  const { t } = useTranslation("jo");
  const { t: tPick } = useTranslation("pickOrder");
  const router = useRouter();
  const { data: session } = useSession() as { data: SessionWithTokens | null };
  
  const currentUserId = session?.id ? parseInt(session.id) : undefined;
  
  //  修改：使用 Job Order 数据结构

  const [combinedDataLoading, setCombinedDataLoading] = useState(false);
 
  //  添加未分配订单状态
  const [unassignedOrders, setUnassignedOrders] = useState<any[]>([]);
  const [isLoadingUnassigned, setIsLoadingUnassigned] = useState(false);
  
  const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
  const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
  const [lotConfirmationError, setLotConfirmationError] = useState<string | null>(null);
  const [expectedLotData, setExpectedLotData] = useState<any>(null);
  const [scannedLotData, setScannedLotData] = useState<any>(null);
  const [isConfirmingLot, setIsConfirmingLot] = useState(false);
  const [qrScanInput, setQrScanInput] = useState<string>('');
  const [qrScanError, setQrScanError] = useState<boolean>(false);
  const [qrScanErrorMsg, setQrScanErrorMsg] = useState<string>('');
  const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
  const [jobOrderData, setJobOrderData] = useState<JobOrderLotsHierarchicalResponse | null>(null);
  const [pickQtyData, setPickQtyData] = useState<Record<string, number>>({});
  const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
  // issue form 里填的 actualPickQty（用于 submit/batch submit 不补拣到 required）
  const [issuePickedQtyBySolId, setIssuePickedQtyBySolId] = useState<Record<number, number>>({});
  const [localSolStatusById, setLocalSolStatusById] = useState<Record<number, string>>({});
  // 防止同一行（以 stockOutLineId/solId 识别）被重复点击提交/完成
  const [actionBusyBySolId, setActionBusyBySolId] = useState<Record<number, boolean>>({});

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

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

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

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

  //  Add QR modal states
  const [qrModalOpen, setQrModalOpen] = useState(false);
  const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
  const [selectedFloor, setSelectedFloor] = useState<string | null>(null);
  //  Add GoodPickExecutionForm states
  const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
  const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null);
  const [fgPickOrders, setFgPickOrders] = useState<FGPickOrderResponse[]>([]);
  const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false);

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

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

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

  // Store callbacks in refs to avoid useEffect dependency issues
  const processOutsideQrCodeRef = useRef<((latestQr: string) => Promise<void>) | null>(null);
  const resetScanRef = useRef<(() => void) | null>(null);
  const lotConfirmLastQrRef = useRef<string>('');
  const lotConfirmSkipNextScanRef = useRef<boolean>(false);
  const lotConfirmOpenedAtRef = useRef<number>(0);

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

    const allLots: any[] = [];
    
    data.pickOrderLines.forEach((line) => {
      // 用来记录这一行已经通过 lots 出现过的 lotId（避免 stockouts 再渲染一次）
      const lotIdSet = new Set<number>();

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

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

        lotMap.forEach((lot: any) => {
          if (lot.lotId != null) lotIdSet.add(lot.lotId);

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

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

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

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

            lotId: stockout.lotId || null,
            lotNo: stockout.lotNo || null,
            expiryDate: null,
            location: stockout.location || null,
            availableQty: stockout.availableQty ?? 0,
            requiredQty: line.requiredQty ?? 0,
            actualPickQty: stockout.qty ?? 0,
            processingStatus: stockout.status || "pending",
            lotAvailability: stockout.noLot ? "insufficient_stock" : "available",
            suggestedPickLotId: null,
            stockOutLineId: stockout.id || null,
            stockOutLineQty: stockout.qty ?? 0,
            stockOutLineStatus: stockout.status || null,
            stockInLineId: null,
            routerIndex: stockout.noLot ? 999999 : null,
            routerArea: null,
            routerRoute: null,
            noLot: !!stockout.noLot,
          });
        });
      }
    });
    
    return allLots;
  }, []);
  const extractFloor = (lot: any): string => {
    const raw = lot.routerRoute || lot.routerArea || lot.location || '';
    const match = raw.match(/^(\d+F?)/i) || raw.split('-')[0];
    return (match?.[1] || match || raw || '').toUpperCase().replace(/(\d)F?/i, '$1F');
  };
  
  // 楼层排序权重：4F > 3F > 2F（数字越大越靠前）
  const floorSortOrder = (floor: string): number => {
    const n = parseInt(floor.replace(/\D/g, ''), 10);
    return isNaN(n) ? 0 : n;
  };
  const combinedLotData = useMemo(() => {
    const lots = getAllLotsFromHierarchical(jobOrderData);
    // 前端覆盖：issue form/submit0 不会立刻改写后端 qty 时，用本地缓存让 UI 与 batch submit 计算一致
    return lots.map((lot: any) => {
      const solId = Number(lot.stockOutLineId) || 0;
      if (solId > 0) {
        const hasPickedOverride = Object.prototype.hasOwnProperty.call(issuePickedQtyBySolId, solId);
        const picked = Number(issuePickedQtyBySolId[solId] ?? lot.actualPickQty ?? 0);
        const statusRaw = localSolStatusById[solId] ?? lot.stockOutLineStatus ?? "";
        const status = String(statusRaw).toLowerCase();
        const isEnded = status === 'completed' || status === 'rejected';
        return {
          ...lot,
          actualPickQty: hasPickedOverride ? picked : lot.actualPickQty,
          stockOutLineQty: hasPickedOverride ? picked : lot.stockOutLineQty,
          stockOutLineStatus: isEnded ? statusRaw : (statusRaw || "checked"),
        };
      }
      return lot;
    });
  }, [jobOrderData, getAllLotsFromHierarchical, issuePickedQtyBySolId, localSolStatusById]);

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

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

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

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

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

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

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

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

  //  修改：分配订单给当前用户
  const handleAssignOrder = useCallback(async (pickOrderId: number) => {
    if (!currentUserId) {
      console.error("Missing user id in session");
      return;
    }
    
    try {
      const result = await assignJobOrderPickOrder(pickOrderId, currentUserId);
      if (result.message === "Successfully assigned") {
        console.log(" Successfully assigned pick order");
        // 刷新数据
        window.dispatchEvent(new CustomEvent('pickOrderAssigned'));
        // 重新加载未分配订单列表
        loadUnassignedOrders();
      } else {
        console.warn("⚠️ Assignment failed:", result.message);
        alert(`Assignment failed: ${result.message}`);
      }
    } catch (error) {
      console.error("❌ Error assigning order:", error);
      alert("Error occurred during assignment");
    }
  }, [currentUserId, loadUnassignedOrders]);

  const fetchFgPickOrdersData = useCallback(async () => {
    if (!currentUserId) return;
    
    setFgPickOrdersLoading(true);
    try {
      // Get all pick order IDs from combinedLotData
      const pickOrderIds = Array.from(new Set(combinedLotData.map(lot => lot.pickOrderId)));
       
      if (pickOrderIds.length === 0) {
        setFgPickOrders([]);
        return;
      }
      
      // Fetch FG pick orders for each pick order ID
      const fgPickOrdersPromises = pickOrderIds.map(pickOrderId => 
        fetchFGPickOrders(pickOrderId)
      );
      
      const fgPickOrdersResults = await Promise.all(fgPickOrdersPromises);
      
      // Flatten the results (each fetchFGPickOrders returns an array)
      const allFgPickOrders = fgPickOrdersResults.flat();
      
      setFgPickOrders(allFgPickOrders);
      console.log(" Fetched FG pick orders:", allFgPickOrders);
    } catch (error) {
      console.error("❌ Error fetching FG pick orders:", error);
      setFgPickOrders([]);
    } finally {
      setFgPickOrdersLoading(false);
    }
  }, [currentUserId, combinedLotData]);

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

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

  //  修改：使用 Job Order API 获取数据
  const fetchJobOrderData = useCallback(async (pickOrderId?: number) => {
    setCombinedDataLoading(true);
    try {
      if (!pickOrderId) {
        console.warn("⚠️ No pickOrderId provided, skipping API call");
        return;
      }
      
      // 直接使用类型化的响应
      const jobOrderData = await fetchJobOrderLotsHierarchicalByPickOrderId(pickOrderId);
      console.log("✅ Job Order data (hierarchical):", jobOrderData);
      
      setJobOrderData(jobOrderData);
      setIssuePickedQtyBySolId(loadIssuePickedMapJo(pickOrderId));
      
      // 使用辅助函数获取所有 lots（不再扁平化）
      getAllLotsFromHierarchical(jobOrderData);
    
      
    } catch (error) {
      console.error("❌ Error fetching job order data:", error);
      setJobOrderData(null);
      setIssuePickedQtyBySolId({});
    } finally {
      setCombinedDataLoading(false);
    }
  }, [getAllLotsFromHierarchical]);

  const updateHandledBy = useCallback(async (pickOrderId: number, itemId: number) => {
    if (!currentUserId || !pickOrderId || !itemId) {
      return;
    }
    
    try {
      console.log(`Updating JoPickOrder.handledBy for pickOrderId: ${pickOrderId}, itemId: ${itemId}, userId: ${currentUserId}`);
      await updateJoPickOrderHandledBy({
        pickOrderId: pickOrderId,
        itemId: itemId,
        userId: currentUserId
      });
      console.log("✅ JoPickOrder.handledBy updated successfully");
    } catch (error) {
      console.error("❌ Error updating JoPickOrder.handledBy:", error);
      // Don't throw - this is not critical for the main flow
    }
  }, [currentUserId]);
  //  修改：初始化时加载数据
  useEffect(() => {
    if (session && currentUserId && !initializationRef.current) {
      console.log("✅ Session loaded, initializing job order...");
      initializationRef.current = true;
      
      // Get pickOrderId from filterArgs if available (when viewing from list)
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      if (pickOrderId) {
        fetchJobOrderData(pickOrderId);
      }
      loadUnassignedOrders();
    }
  }, [session, currentUserId, fetchJobOrderData, loadUnassignedOrders, filterArgs?.pickOrderId]);

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

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

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

  //  Handle QR code submission for matched lot (external scanning)
  const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
    console.log(` Processing QR Code for lot: ${lotNo}`);
    
    //  Use current data without refreshing to avoid infinite loop
    const currentLotData = combinedLotData;
    console.log(`🔍 Available lots:`, currentLotData.map(lot => lot.lotNo));
    
    const matchingLots = currentLotData.filter(lot => 
      lot.lotNo === lotNo || 
      lot.lotNo?.toLowerCase() === lotNo.toLowerCase()
    );
    
    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; 
    }
    
    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...");
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData(pickOrderId);
      
      if (successCount > 0) {
        console.log(` QR Code processing completed: ${successCount} updated/created`);
    setQrScanSuccess(true);
    setQrScanError(false);
        setQrScanInput(''); // Clear input after successful processing
   
      } else {
        console.error(`❌ QR Code processing failed: ${errorCount} errors`);
        setQrScanError(true);
        setQrScanSuccess(false);
      }
    } catch (error) {
      console.error("❌ Error processing QR code:", error);
      setQrScanError(true);
      setQrScanSuccess(false);
      
      //  Still refresh data even on error
      setIsRefreshingData(true);
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData( pickOrderId);
    } finally {
      //  Clear refresh flag after a short delay
      setTimeout(() => {
        setIsRefreshingData(false);
      }, 1000);
    }
  }, [combinedLotData, fetchJobOrderData]);
  const handleLotMismatch = useCallback((expectedLot: any, scannedLot: any) => {
    console.log("⚠️ [LOT MISMATCH] Lot mismatch detected:", { expectedLot, scannedLot });
    console.log("⚠️ [LOT MISMATCH] Opening confirmation modal - NO lot will be marked as scanned until user confirms");

    // ✅ schedule modal open in next tick (avoid flushSync warnings on some builds)
    // ✅ IMPORTANT: This function ONLY opens the modal. It does NOT process any lot.
    setTimeout(() => {
      setExpectedLotData(expectedLot);
      setScannedLotData({
        ...scannedLot,
        lotNo: scannedLot.lotNo || null,
      });
      lotConfirmSkipNextScanRef.current = true;
      lotConfirmOpenedAtRef.current = Date.now();
      setLotConfirmationOpen(true);
      console.log("⚠️ [LOT MISMATCH] Modal opened - waiting for user confirmation");
    }, 0);

    // ✅ Fetch lotNo in background for display purposes (cached)
    // ✅ This is ONLY for display - it does NOT process any lot
    if (!scannedLot.lotNo && scannedLot.stockInLineId) {
      console.log(`⚠️ [LOT MISMATCH] Fetching lotNo for display (stockInLineId: ${scannedLot.stockInLineId})`);
      fetchStockInLineInfoCached(scannedLot.stockInLineId)
        .then((info) => {
          console.log(`⚠️ [LOT MISMATCH] Fetched lotNo for display: ${info.lotNo}`);
          startTransition(() => {
            setScannedLotData((prev: any) => ({
              ...prev,
              lotNo: info.lotNo || null,
            }));
          });
        })
        .catch((error) => {
          console.error(`❌ [LOT MISMATCH] Error fetching lotNo for display (stockInLineId may not exist):`, error);
          // ignore display fetch errors - this does NOT affect processing
        });
    }
  }, [fetchStockInLineInfoCached]);

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

  //  Add handleLotConfirmation function
  const handleLotConfirmation = useCallback(async (overrideScannedLot?: any) => {
    const effectiveScannedLot = overrideScannedLot ?? scannedLotData;
    if (!expectedLotData || !effectiveScannedLot || !selectedLotForQr) {
      console.error("❌ [LOT CONFIRM] Missing required data for lot confirmation");
      return;
    }
    
    console.log("✅ [LOT CONFIRM] User confirmed lot substitution - processing now");
    console.log("✅ [LOT CONFIRM] Expected lot:", expectedLotData);
    console.log("✅ [LOT CONFIRM] Scanned lot:", scannedLotData);
    console.log("✅ [LOT CONFIRM] Selected lot for QR:", selectedLotForQr);
    
    setIsConfirmingLot(true);
    setLotConfirmationError(null);
    try {
      let newLotLineId = effectiveScannedLot?.inventoryLotLineId;
      if (!newLotLineId && effectiveScannedLot?.stockInLineId) {
        try {
          if (currentUserId && selectedLotForQr.pickOrderId && selectedLotForQr.itemId) {
            try {
              await updateHandledBy(selectedLotForQr.pickOrderId, selectedLotForQr.itemId);
              console.log(`✅ [LOT CONFIRM] Handler updated for itemId ${selectedLotForQr.itemId}`);
            } catch (error) {
              console.error(`❌ [LOT CONFIRM] Error updating handler (non-critical):`, error);
            }
          }
          console.log(`🔍 [LOT CONFIRM] Fetching lot detail for stockInLineId: ${effectiveScannedLot.stockInLineId}`);
          const ld = await fetchLotDetail(effectiveScannedLot.stockInLineId);
          newLotLineId = ld.inventoryLotLineId;
          console.log(`✅ [LOT CONFIRM] Fetched lot detail: inventoryLotLineId=${newLotLineId}`);
        } catch (error) {
          console.error("❌ [LOT CONFIRM] Error fetching lot detail (stockInLineId may not exist):", error);
          // If stockInLineId doesn't exist, we can still proceed with lotNo substitution
          // The backend confirmLotSubstitution should handle this case
        }
      }
      if (!newLotLineId) {
        console.warn("⚠️ [LOT CONFIRM] No inventory lot line id for scanned lot, proceeding with lotNo only");
        // Continue anyway - backend may handle lotNo substitution without inventoryLotLineId
      }
  
      console.log("=== [LOT CONFIRM] Lot Confirmation Debug ===");
      console.log("Selected Lot:", selectedLotForQr);
      console.log("Pick Order Line ID:", selectedLotForQr.pickOrderLineId);
      console.log("Stock Out Line ID:", selectedLotForQr.stockOutLineId);
      console.log("Suggested Pick Lot ID:", selectedLotForQr.suggestedPickLotId);
      console.log("Lot ID (fallback):", selectedLotForQr.lotId);
      console.log("New Inventory Lot Line ID:", newLotLineId);
      console.log("Scanned Lot No:", effectiveScannedLot.lotNo);
      console.log("Scanned StockInLineId:", effectiveScannedLot.stockInLineId);

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

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

        // ✅ CRITICAL: substitution failed => DO NOT mark original stockOutLine as checked.
        // Keep modal open so user can cancel/rescan.
        switchedToUnavailable =
          substitutionResult?.code === "SUCCESS_UNAVAILABLE" ||
          substitutionResult?.code === "BOUND_UNAVAILABLE";
        if (!substitutionResult || (substitutionResult.code !== "SUCCESS" && !switchedToUnavailable)) {
          console.error("❌ [LOT CONFIRM] Lot substitution failed. Will NOT update stockOutLine status.");
          const errMsg =
            substitutionResult?.code === "LOT_UNAVAILABLE"
              ? tPick(
                  "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated."
                )
              : substitutionResult?.message ||
                `换批失败：stockInLineId ${effectiveScannedLot?.stockInLineId ?? ""} 不存在或无法匹配`;
          setLotConfirmationError(errMsg);
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(errMsg);
          return;
        }
      }
  
      //  Update stock out line status to 'checked' after substitution
      if (selectedLotForQr?.stockOutLineId && !switchedToUnavailable) {
        console.log(`🔄 [LOT CONFIRM] Updating stockOutLine ${selectedLotForQr.stockOutLineId} to 'checked'`);
        await updateStockOutLineStatus({
          id: selectedLotForQr.stockOutLineId,
          status: 'checked',
          qty: 0
        });
        console.log(`✅ [LOT CONFIRM] Stock out line ${selectedLotForQr.stockOutLineId} status updated to 'checked'`);
      }
      
      //  Close modal and clean up state BEFORE refreshing
      clearLotConfirmationState(false);
      
      //  Clear QR processing state but DON'T clear processedQrCodes yet
      setQrScanError(false);
      setQrScanSuccess(true);
      setQrScanErrorMsg('');
      setQrScanInput('');
      
      //  Set refreshing flag to prevent QR processing during refresh
      setIsRefreshingData(true);
      
      //  Refresh data to show updated lot
      console.log("🔄 Refreshing job order data...");
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData(pickOrderId);
      console.log(" Lot substitution confirmed and data refreshed");
      
      //  Clear processed QR codes and flags immediately after refresh
      // This allows new QR codes to be processed right away
      setTimeout(() => {
        console.log(" Clearing processed QR codes and resuming scan");
        setProcessedQrCodes(new Set());
        setLastProcessedQr('');
        setQrScanSuccess(false);
        setIsRefreshingData(false);
        // ✅ Clear processedQrCombinations to allow reprocessing the same QR if needed
        if (effectiveScannedLot?.stockInLineId && selectedLotForQr?.itemId) {
          setProcessedQrCombinations(prev => {
            const newMap = new Map(prev);
            const itemId = selectedLotForQr.itemId;
            if (itemId && newMap.has(itemId)) {
              newMap.get(itemId)!.delete(effectiveScannedLot.stockInLineId);
              if (newMap.get(itemId)!.size === 0) {
                newMap.delete(itemId);
              }
            }
            return newMap;
          });
        }
      }, 500); //  Reduced from 3000ms to 500ms - just enough for UI update
      
    } catch (error) {
      console.error("Error confirming lot substitution:", error);
      const errMsg = tPick("Lot confirmation failed. Please try again.");
      setLotConfirmationError(errMsg);
      setQrScanError(true);
      setQrScanSuccess(false);
      setQrScanErrorMsg(errMsg);
      //  Clear refresh flag on error
      setIsRefreshingData(false);
    } finally {
      setIsConfirmingLot(false);
    }
  }, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData, currentUserId, updateHandledBy, tPick, clearLotConfirmationState]);

  const handleLotConfirmationByRescan = useCallback(async (rawQr: string): Promise<boolean> => {
    if (!lotConfirmationOpen || !selectedLotForQr || !expectedLotData || !scannedLotData) {
      return false;
    }
    let payload: any = null;
    try {
      payload = JSON.parse(rawQr);
    } catch {
      payload = null;
    }
    const expectedStockInLineId = Number(selectedLotForQr.stockInLineId);
    const mismatchedStockInLineId = Number(scannedLotData?.stockInLineId);
    if (payload?.stockInLineId && payload?.itemId) {
      const rescannedStockInLineId = Number(payload.stockInLineId);
      if (Number.isFinite(expectedStockInLineId) && rescannedStockInLineId === expectedStockInLineId) {
        clearLotConfirmationState(false);
        if (processOutsideQrCodeRef.current) {
          await processOutsideQrCodeRef.current(JSON.stringify(payload));
        }
        return true;
      }
      if (Number.isFinite(mismatchedStockInLineId) && rescannedStockInLineId === mismatchedStockInLineId) {
        await handleLotConfirmation();
        return true;
      }
      await handleLotConfirmation({
        lotNo: null,
        itemCode: expectedLotData?.itemCode,
        itemName: expectedLotData?.itemName,
        inventoryLotLineId: null,
        stockInLineId: rescannedStockInLineId
      });
      return true;
    } else {
      const scannedText = rawQr?.trim();
      const expectedLotNo = expectedLotData?.lotNo?.trim();
      const mismatchedLotNo = scannedLotData?.lotNo?.trim();
      if (expectedLotNo && scannedText === expectedLotNo) {
        clearLotConfirmationState(false);
        if (processOutsideQrCodeRef.current) {
          await processOutsideQrCodeRef.current(JSON.stringify({
            itemId: selectedLotForQr.itemId,
            stockInLineId: selectedLotForQr.stockInLineId,
          }));
        }
        return true;
      }
      if (mismatchedLotNo && scannedText === mismatchedLotNo) {
        await handleLotConfirmation();
        return true;
      }
    }
    return false;
  }, [lotConfirmationOpen, selectedLotForQr, expectedLotData, scannedLotData, handleLotConfirmation, clearLotConfirmationState]);

  const processOutsideQrCode = useCallback(async (latestQr: string) => {
    // ✅ Only JSON QR supported for outside scanner (avoid false positive with lotNo)
    let qrData: any = null;
    try {
      qrData = JSON.parse(latestQr);
    } catch {
      startTransition(() => {
        setQrScanError(true);
        setQrScanSuccess(false);
      });
      return;
    }

    if (!(qrData?.stockInLineId && qrData?.itemId)) {
      startTransition(() => {
        setQrScanError(true);
        setQrScanSuccess(false);
      });
      return;
    }

    const scannedItemId = Number(qrData.itemId);
    const scannedStockInLineId = Number(qrData.stockInLineId);

    // ✅ avoid duplicate processing by itemId+stockInLineId
    const itemProcessedSet = processedQrCombinations.get(scannedItemId);
    if (itemProcessedSet?.has(scannedStockInLineId)) return;

    const indexes = lotDataIndexes;
    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) || [];
    
    // ✅ 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}）已过期，无法使用。请扫描其他批次。`
          );
        });
        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. Scanned lot is not rejected.`);
      
      // 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' ||
        lot.lotAvailability === 'status_unavailable'
      );
      const expectedLot = rejectedLot || allLotsForItem[0]; // Use rejected lot if exists, otherwise first lot
      
      // ✅ Always open confirmation modal when no active lots (user needs to confirm switching)
      // handleLotMismatch will fetch lotNo from backend using stockInLineId if needed
      console.log(`⚠️ [QR PROCESS] Opening confirmation modal for lot switch (no active lots)`);
      setSelectedLotForQr(expectedLot);
      handleLotMismatch(
        {
          lotNo: expectedLot.lotNo,
          itemCode: expectedLot.itemCode,
          itemName: expectedLot.itemName
        },
        {
          lotNo: scannedLot?.lotNo || null, // Will be fetched by handleLotMismatch if null
          itemCode: expectedLot.itemCode,
          itemName: expectedLot.itemName,
          inventoryLotLineId: scannedLot?.lotId || null,
          stockInLineId: scannedStockInLineId // handleLotMismatch will use this to fetch lotNo
        }
      );
      return;
    }

    // ✅ direct stockInLineId match (O(1))
    const stockInLineLots = indexes.byStockInLineId.get(scannedStockInLineId) || [];
    let exactMatch: any = null;
    for (let i = 0; i < stockInLineLots.length; i++) {
      const lot = stockInLineLots[i];
      if (lot.itemId === scannedItemId && activeSuggestedLots.includes(lot)) {
        exactMatch = lot;
        break;
      }
    }

    console.log(`🔍 [QR PROCESS] Scanned stockInLineId: ${scannedStockInLineId}, itemId: ${scannedItemId}`);
    console.log(`🔍 [QR PROCESS] Found ${stockInLineLots.length} lots with stockInLineId ${scannedStockInLineId}`);
    console.log(`🔍 [QR PROCESS] Exact match found: ${exactMatch ? `YES (lotNo: ${exactMatch.lotNo}, stockOutLineId: ${exactMatch.stockOutLineId})` : '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
    if (!exactMatch && scannedLot && !activeSuggestedLots.includes(scannedLot)) {
      // Scanned lot is not in active suggested lots, open confirmation modal
      const expectedLot = activeSuggestedLots[0] || allLotsForItem[0]; // Use first active lot or first lot as expected
      if (expectedLot && scannedLot.stockInLineId !== expectedLot.stockInLineId) {
        console.log(`⚠️ [QR PROCESS] Scanned lot ${scannedLot.lotNo} is not in active suggested lots, opening confirmation modal`);
        setSelectedLotForQr(expectedLot);
        handleLotMismatch(
          {
            lotNo: expectedLot.lotNo,
            itemCode: expectedLot.itemCode,
            itemName: expectedLot.itemName
          },
          {
            lotNo: scannedLot.lotNo || null,
            itemCode: expectedLot.itemCode,
            itemName: expectedLot.itemName,
            inventoryLotLineId: scannedLot.lotId || null,
            stockInLineId: scannedStockInLineId
          }
        );
        return;
      }
    }

    if (exactMatch) {
      if (!exactMatch.stockOutLineId) {
        console.error(`❌ [QR PROCESS] Exact match found but no stockOutLineId`);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
        });
        return;
      }

      console.log(`✅ [QR PROCESS] Processing exact match: lotNo=${exactMatch.lotNo}, stockOutLineId=${exactMatch.stockOutLineId}`);
      try {
        if (currentUserId && exactMatch.pickOrderId && exactMatch.itemId) {
          try {
            await updateHandledBy(exactMatch.pickOrderId, exactMatch.itemId);
            console.log(`✅ [QR PROCESS] Handler updated for itemId ${exactMatch.itemId}`);
          } catch (error) {
            console.error(`❌ [QR PROCESS] Error updating handler (non-critical):`, error);
          }
        }
        const res = await updateStockOutLineStatusByQRCodeAndLotNo({
          pickOrderLineId: exactMatch.pickOrderLineId,
          inventoryLotNo: exactMatch.lotNo,
          stockInLineId: exactMatch.stockInLineId ?? null,
          stockOutLineId: exactMatch.stockOutLineId,
          itemId: exactMatch.itemId,
          status: "checked",
        });

        if (res.code === "checked" || res.code === "SUCCESS") {
          console.log(`✅ [QR PROCESS] Successfully updated stockOutLine ${exactMatch.stockOutLineId} to checked`);
          const entity = res.entity as any;
          startTransition(() => {
            setQrScanError(false);
            setQrScanSuccess(true);
          });

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

          // refresh to keep consistency with server & handler updates
          const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
          await fetchJobOrderData(pickOrderId);
        } else {
          console.error(`❌ [QR PROCESS] Update failed: ${res.code}`);
          startTransition(() => {
            setQrScanError(true);
            setQrScanSuccess(false);
          });
        }
      } catch (error) {
        console.error(`❌ [QR PROCESS] Error updating stockOutLine:`, error);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
        });
      }
      return;
    }

    // ✅ mismatch: align with GoodPickExecutiondetail — open LotConfirmationModal first;
    // handleLotMismatch loads scanned lotNo via fetchStockInLineInfoCached in the background when lotNo is null.
    console.log(
      `⚠️ [QR PROCESS] No exact match found (itemId ${scannedItemId}, scanned stockInLineId ${scannedStockInLineId})`
    );
    console.log(
      `⚠️ [QR PROCESS] Active suggested lots for itemId ${scannedItemId}:`,
      activeSuggestedLots.map((l) => ({ lotNo: l.lotNo, stockInLineId: l.stockInLineId }))
    );

    if (activeSuggestedLots.length === 0) {
      console.error(`❌ [QR PROCESS] No active suggested lots found for itemId ${scannedItemId}`);
      startTransition(() => {
        setQrScanError(true);
        setQrScanSuccess(false);
        setQrScanErrorMsg(`当前订单中没有 itemId ${scannedItemId} 的可用批次`);
      });
      return;
    }

    const expectedLot = activeSuggestedLots[0];
    console.log(
      `⚠️ [QR PROCESS] Expected lot: ${expectedLot.lotNo} (stockInLineId: ${expectedLot.stockInLineId}), Scanned stockInLineId: ${scannedStockInLineId}`
    );
    console.log(
      `⚠️ [QR PROCESS] Opening confirmation modal - user must confirm before any lot is marked as scanned`
    );
    setSelectedLotForQr(expectedLot);
    handleLotMismatch(
      {
        lotNo: expectedLot.lotNo,
        itemCode: expectedLot.itemCode,
        itemName: expectedLot.itemName,
      },
      {
        lotNo: null,
        itemCode: expectedLot.itemCode,
        itemName: expectedLot.itemName,
        inventoryLotLineId: null,
        stockInLineId: scannedStockInLineId,
      }
    );
  }, [filterArgs?.pickOrderId, fetchJobOrderData, handleLotMismatch, lotDataIndexes, processedQrCombinations, combinedLotData, currentUserId, updateHandledBy]);

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


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

  //  Handle QR code submission from modal (internal scanning)
  const handleQrCodeSubmitFromModal = useCallback(async (lotNo: string) => {
    if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) {
      console.log(` QR Code verified for lot: ${lotNo}`);
      
      const requiredQty = selectedLotForQr.requiredQty;
      const lotId = selectedLotForQr.lotId;
      
      // Create stock out line
      const stockOutLineData: CreateStockOutLine = {
        consoCode: selectedLotForQr.pickOrderConsoCode,
        pickOrderLineId: selectedLotForQr.pickOrderLineId,
        inventoryLotLineId: selectedLotForQr.lotId,
        qty: 0.0
      };
      
      try {
        await createStockOutLine(stockOutLineData);
        console.log("Stock out line created successfully!");
        
        // Close modal
        setQrModalOpen(false);
        setSelectedLotForQr(null);
        
        // Set pick quantity
        const lotKey = `${selectedLotForQr.pickOrderLineId}-${lotId}`;
        setTimeout(() => {
          setPickQtyData(prev => ({
            ...prev,
            [lotKey]: requiredQty
          }));
          console.log(` Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`);
        }, 500);
        
        // Refresh data
        const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
        await fetchJobOrderData(pickOrderId);
      } catch (error) {
        console.error("Error creating stock out line:", error);
      }
    }
  }, [selectedLotForQr, fetchJobOrderData]);

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

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

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

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

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

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

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

    if (lotConfirmationOpen) {
      if (isConfirmingLot) return;
      if (lotConfirmSkipNextScanRef.current) {
        lotConfirmSkipNextScanRef.current = false;
        lotConfirmLastQrRef.current = latestQr || '';
        return;
      }
      if (!latestQr) return;
      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 modal open for same QR
    if (manualLotConfirmationOpen) {
      if (latestQr === lastProcessedQrRef.current) return;
    }

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

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

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

    processOutsideQrCodeRef.current?.(latestQr);

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

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

  const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
    if (value === '' || value === null || value === undefined) {
      setPickQtyData(prev => ({
        ...prev,
        [lotKey]: 0
      }));
      return;
    }
    
    const numericValue = typeof value === 'string' ? parseFloat(value) : value;
    
    if (isNaN(numericValue)) {
      setPickQtyData(prev => ({
        ...prev,
        [lotKey]: 0
      }));
      return;
    }
    
    setPickQtyData(prev => ({
      ...prev,
      [lotKey]: numericValue
    }));
  }, []);

  /** 单笔「提交」数量：Issue 改数 → pickQtyData → noLot/過期 → 0 → 否则 required（對齊 GoodPickExecutiondetail） */
  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 (isInventoryLotLineUnavailable(lot)) {
        return 0;
      }
      if (lot.noLot === true || !lot.lotId) {
        return 0;
      }
      if (isLotAvailabilityExpired(lot)) {
        return 0;
      }
      return required;
    },
    [issuePickedQtyBySolId, pickQtyData]
  );

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

  //  Handle submit pick quantity
  const handleSubmitPickQty = useCallback(async (lot: any) => {
    const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
    const newQty = pickQtyData[lotKey] || 0;
    
    if (!lot.stockOutLineId) {
      console.error("No stock out line found for this lot");
      return;
    }
    
    try {
      const currentActualPickQty = lot.actualPickQty || 0;
      const cumulativeQty = currentActualPickQty + newQty;
      
      let newStatus = 'partially_completed';
      
      if (cumulativeQty >= lot.requiredQty) {
        newStatus = 'completed';
      }
      
      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: ${newQty}`);
      console.log(`Cumulative Qty: ${cumulativeQty}`);
      console.log(`New Status: ${newStatus}`);
      console.log(`=====================================`);
      
      await updateStockOutLineStatus({
        id: lot.stockOutLineId,
        status: newStatus,
        qty: cumulativeQty
      });
      
      if (newQty > 0) {
        await updateInventoryLotLineQuantities({
          inventoryLotLineId: lot.lotId,
          qty: newQty,
          status: 'available',
          operation: 'pick'
        });
      }
      
      //  FIXED: Use the proper API function instead of direct fetch
      if (newStatus === 'completed' && lot.pickOrderConsoCode) {
        console.log(` Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`);
        
        try {
          //  Use the imported API function instead of direct fetch
          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);
        }
      }
      
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData(pickOrderId);
      console.log("Pick quantity submitted successfully!");
      
      setTimeout(() => {
        checkAndAutoAssignNext();
      }, 1000);
      
    } catch (error) {
      console.error("Error submitting pick quantity:", error);
    }
  }, [pickQtyData, fetchJobOrderData, checkAndAutoAssignNext]);
  const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: number) => {
    if (!lot.stockOutLineId) {
      console.error("No stock out line found for this lot");
      return;
    }
    const solId = Number(lot.stockOutLineId) || 0;
    
    try {
      if (currentUserId && lot.pickOrderId && lot.itemId) {
        try {
          await updateHandledBy(lot.pickOrderId, lot.itemId);
        } catch (error) {
          console.error("❌ Error updating handler (non-critical):", error);
          // Continue even if handler update fails
        }
      }
      const unavailableLot = isInventoryLotLineUnavailable(lot);
      const effectiveSubmitQty = unavailableLot ? 0 : submitQty;
      // ✅ 两步完成（与 DO 对齐）：
      // 1) Skip/Submit0 只把 SOL 标记为 checked（不直接 completed）
      // 2) 之后由 batch submit 把 SOL 推到 completed（允许 0）
      
      if (effectiveSubmitQty === 0) {
        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 (will complete in batch submit)`);
        
        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');
          }
          
        // 與 DO（GoodPickExecutiondetail）對齊：只標 checked，不寫 issuePickedQtyBySolId=0，
        // 否則 combinedLotData 會把實揀量覆寫為 0，且 batch submit 的 resolveSingleSubmitQty 會錯。
        if (solId > 0) {
          setLocalSolStatusById(prev => ({ ...prev, [solId]: 'checked' }));
        }
        
        const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
        void fetchJobOrderData(pickOrderId);
        console.log("All zeros submission marked as checked successfully (waiting for batch submit).");
        
        setTimeout(() => {
          checkAndAutoAssignNext();
        }, 1000);
        
        return;
      }
      
      // Normal case: Calculate cumulative quantity correctly
      const currentActualPickQty = lot.actualPickQty || 0;
      const cumulativeQty = currentActualPickQty + effectiveSubmitQty;
      
      // 短拣一次 completed（對齊 GoodPickExecutiondetail）
      let newStatus = "partially_completed";
      if (cumulativeQty >= lot.requiredQty) {
        newStatus = "completed";
      } else if (cumulativeQty > 0) {
        newStatus = "partially_completed";
      } else {
        newStatus = "checked";
      }
      
      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
      });
      if (solId > 0) {
        setIssuePickedQtyBySolId((prev) => {
          const next = { ...prev, [solId]: cumulativeQty };
          const pid = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
          if (pid) saveIssuePickedMapJo(pid, next);
          return next;
        });
        setLocalSolStatusById(prev => ({ ...prev, [solId]: newStatus }));
      }
      // 注意：库存过账（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!`);
            setTimeout(() => {
              if (onBackToList) {
                onBackToList();
              }
            }, 1500);
          } 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);
        }
      }
      
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      void fetchJobOrderData(pickOrderId);
      console.log("Pick quantity submitted successfully!");
      
      setTimeout(() => {
        checkAndAutoAssignNext();
      }, 1000);
      
    } catch (error) {
      console.error("Error submitting pick quantity:", error);
    }
  }, [fetchJobOrderData, checkAndAutoAssignNext, filterArgs]);
  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);
    } 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 handleSubmitAllScanned = useCallback(async () => {
    const scannedLots = combinedLotData.filter((lot) => {
      const status = lot.stockOutLineStatus;
      const statusLower = String(status || "").toLowerCase();
      if (statusLower === "completed" || statusLower === "complete") {
        return false;
      }
      // noLot / 過期批號：允許 pending（對齊 GoodPickExecutiondetail）
      if (lot.noLot === true || !lot.lotId || 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 {
      // ✅ 批量更新所有相关行的 handler（在提交前）
      if (currentUserId) {
        const uniqueItemIds = new Set(scannedLots.map(lot => lot.itemId));
        const updatePromises = Array.from(uniqueItemIds).map(itemId => {
          const lot = scannedLots.find(l => l.itemId === itemId);
          if (lot && lot.pickOrderId) {
            return updateHandledBy(lot.pickOrderId, itemId).catch(err => {
              console.error(`❌ Error updating handler for itemId ${itemId}:`, err);
            });
          }
          return Promise.resolve();
        });
        await Promise.all(updatePromises);
        console.log(`✅ Updated handlers for ${uniqueItemIds.size} unique items`);
      }

      const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => {
        const requiredQty = Number(lot.requiredQty || lot.pickOrderLineRequiredQty || 0);
        const solId = Number(lot.stockOutLineId) || 0;
        const issuePicked = solId > 0 ? issuePickedQtyBySolId[solId] : undefined;
        const currentActualPickQty = Number(issuePicked ?? lot.actualPickQty ?? 0);
        const onlyComplete =
          lot.stockOutLineStatus === "partially_completed" || issuePicked !== undefined;
        const expired = isLotAvailabilityExpired(lot);
        const unavailable = isInventoryLotLineUnavailable(lot);

        let targetActual: number;
        let newStatus: string;

        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,
          actualPickQty: targetActual,
          stockOutLineStatus: newStatus,
          pickOrderConsoCode: String(lot.pickOrderConsoCode || ""),
          noLot: Boolean(lot.noLot === true),
        };
      });
      
      const request: batchSubmitListRequest = {
        userId: currentUserId || 0,
        lines: lines
      };
      
      // ✅ 使用 batchSubmitList API
      const result = await batchSubmitList(request);
      console.log(`📥 Batch submit result:`, result);

      // ✅ After batch submit, explicitly trigger completion check per consoCode.
      // Otherwise pick_order/job_order may stay RELEASED even when all lines are completed.
      try {
        const consoCodes = Array.from(
          new Set(
            lines
              .map((l) => (l.pickOrderConsoCode || "").trim())
              .filter((c) => c.length > 0),
          ),
        );
        if (consoCodes.length > 0) {
          await Promise.all(
            consoCodes.map(async (code) => {
              try {
                const completionResponse = await checkAndCompletePickOrderByConsoCode(code);
                console.log(`✅ Pick order completion check (${code}):`, completionResponse);
              } catch (e) {
                console.error(`❌ Error checking completion for ${code}:`, e);
              }
            }),
          );
        }
      } catch (e) {
        console.error("❌ Error triggering completion checks after batch submit:", e);
      }
      
      // 刷新数据
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData(pickOrderId);
      
      if (result && result.code === "SUCCESS") {
        setQrScanSuccess(true);
        setTimeout(() => {
          setQrScanSuccess(false);
          checkAndAutoAssignNext();
          if (onBackToList) {
            onBackToList();
          }
        }, 2000);
      } else {
        console.error("Batch submit failed:", result);
        setQrScanError(true);
      }
      
    } catch (error) {
      console.error("Error submitting all scanned items:", error);
      setQrScanError(true);
    } finally {
      setIsSubmittingAll(false);
    }
  }, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList, updateHandledBy, issuePickedQtyBySolId])
  const scannedItemsCount = useMemo(() => {
    return combinedLotData.filter((lot) => {
      const status = lot.stockOutLineStatus;
      const statusLower = String(status || "").toLowerCase();
      if (statusLower === "completed" || statusLower === "complete") {
        return false;
      }
      if (lot.noLot === true || !lot.lotId || isLotAvailabilityExpired(lot) || isInventoryLotLineUnavailable(lot)) {
        return (
          status === "checked" ||
          status === "pending" ||
          status === "partially_completed" ||
          status === "PARTIALLY_COMPLETE"
        );
      }
      return (
        status === "checked" ||
        status === "partially_completed" ||
        status === "PARTIALLY_COMPLETE"
      );
    }).length;
  }, [combinedLotData]);
  
  // 先定义 filteredByFloor 和 availableFloors
  const availableFloors = useMemo(() => {
    const floors = new Set<string>();
    combinedLotData.forEach(lot => {
      const f = extractFloor(lot);
      if (f) floors.add(f);
    });
    return Array.from(floors).sort((a, b) => floorSortOrder(b) - floorSortOrder(a));
  }, [combinedLotData]);
  
  const filteredByFloor = useMemo(() => {
    if (!selectedFloor) return combinedLotData;
    return combinedLotData.filter(lot => extractFloor(lot) === selectedFloor);
  }, [combinedLotData, selectedFloor]);
  
  // 與批量篩選一致：noLot / 過期 的 pending 也算已處理（對齊 GoodPickExecutiondetail）
  const progress = useMemo(() => {
    const data = selectedFloor ? filteredByFloor : combinedLotData;
    if (data.length === 0) return { completed: 0, total: 0 };
    const nonPendingCount = data.filter((lot) => {
      const status = lot.stockOutLineStatus?.toLowerCase();
      if (status !== "pending") return true;
      if (lot.noLot === true || isLotAvailabilityExpired(lot) || isInventoryLotLineUnavailable(lot)) return true;
      return false;
    }).length;
    return { completed: nonPendingCount, total: data.length };
  }, [selectedFloor, filteredByFloor, combinedLotData]);
  //  Handle reject lot
  const handleRejectLot = useCallback(async (lot: any) => {
    if (!lot.stockOutLineId) {
      console.error("No stock out line found for this lot");
      return;
    }
  
    try {
      await updateStockOutLineStatus({
        id: lot.stockOutLineId,
        status: 'rejected',
        qty: 0
      });
      
      const pickOrderId = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
      await fetchJobOrderData(pickOrderId);
      console.log("Lot rejected successfully!");
      
      setTimeout(() => {
        checkAndAutoAssignNext();
      }, 1000);
      
    } catch (error) {
      console.error("Error rejecting lot:", error);
    }
  }, [fetchJobOrderData, checkAndAutoAssignNext]);

  //  Handle pick execution form
  const handlePickExecutionForm = useCallback((lot: any) => {
    console.log("=== Pick Execution Form ===");
    console.log("Lot data:", lot);
    
    if (!lot) {
      console.warn("No lot data provided for pick execution form");
      return;
    }
    
    console.log("Opening pick execution form for lot:", lot.lotNo);
    
    setSelectedLotForExecutionForm(lot);
    setPickExecutionFormOpen(true);
    
    console.log("Pick execution form opened for lot ID:", lot.lotId);
  }, []);

  const handlePickExecutionFormSubmit = useCallback(
    async (data: any) => {
      const lotSnap = selectedLotForExecutionForm;
      const pickOrderIdEarly =
        filterArgs?.pickOrderId
          ? Number(filterArgs.pickOrderId)
          : Number(lotSnap?.pickOrderId || 0) || undefined;
  
      try {
        if (currentUserId && lotSnap?.pickOrderId && lotSnap?.itemId) {
          try {
            await updateHandledBy(lotSnap.pickOrderId, lotSnap.itemId);
            console.log(`✅ [ISSUE FORM] Handler updated for itemId ${lotSnap.itemId}`);
          } catch (error) {
            console.error(`❌ [ISSUE FORM] Error updating handler (non-critical):`, error);
          }
        }
  
        console.log("Pick execution form submitted:", data);
        const issueData = {
          ...data,
          type: "Jo",
          pickerName: session?.user?.name || undefined,
          handledBy: currentUserId || undefined,
        };
  
        const missN = Number(issueData.missQty ?? 0) || 0;
        const badN = Number(issueData.badItemQty ?? 0) || 0;
        const badPkgN = Number(issueData.badPackageQty ?? 0) || 0;
        const useHoldOnlyApi = missN === 0 && badN === 0 && badPkgN === 0;
  
        const result = useHoldOnlyApi
          ? await applyPickExecutionHoldAndChecked(issueData)
          : await recordPickExecutionIssue(issueData);
  
        console.log(
          useHoldOnlyApi ? "Pick hold/checked applied:" : "Pick execution issue recorded:",
          result,
        );
  
        if (!result || result.code !== "SUCCESS") {
          console.error("❌ Pick execution submit failed:", result);
          throw new Error(result?.message || "Submit failed");
        }
  
        const solId = Number(issueData.stockOutLineId || data?.stockOutLineId);
        const picked = Number(issueData.actualPickQty ?? 0);
  
        if (solId > 0) {
          setIssuePickedQtyBySolId((prev) => {
            const next = { ...prev, [solId]: picked };
            const pid = filterArgs?.pickOrderId ? Number(filterArgs.pickOrderId) : undefined;
            if (pid) saveIssuePickedMapJo(pid, next);
            return next;
          });
        }
  
        // Hold-only：與整批相同規則，只送一筆 batch，把實揈/完成狀態寫回 DB
        if (useHoldOnlyApi && pickOrderIdEarly && solId > 0) {
          const freshData = await fetchJobOrderLotsHierarchicalByPickOrderId(pickOrderIdEarly);
          const flatLots = getAllLotsFromHierarchical(freshData);
          const lotRow = flatLots.find((l: any) => Number(l.stockOutLineId) === solId);
          if (!lotRow) {
            throw new Error("Could not find lot row after refresh for batch submit");
          }
  
          const requiredQty = Number(lotRow.requiredQty || lotRow.pickOrderLineRequiredQty || 0);
          const issuePickedVal = picked;
          const currentActualPickQty = Number(issuePickedVal ?? lotRow.actualPickQty ?? 0);
          const onlyComplete =
            lotRow.stockOutLineStatus === "partially_completed" ||
            lotRow.stockOutLineStatus === "PARTIALLY_COMPLETE" ||
            issuePickedVal !== undefined;
          const expired = isLotAvailabilityExpired(lotRow);
          const unavailable = isInventoryLotLineUnavailable(lotRow);
  
          let targetActual: number;
          let newStatus: string;
  
          if (unavailable) {
            targetActual = currentActualPickQty;
            newStatus = "completed";
          } else if (expired && issuePickedVal === 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";
            }
          }
  
          const line: batchSubmitListLineRequest = {
            stockOutLineId: solId,
            pickOrderLineId: Number(lotRow.pickOrderLineId),
            inventoryLotLineId: lotRow.lotId ? Number(lotRow.lotId) : null,
            requiredQty,
            actualPickQty: targetActual,
            stockOutLineStatus: newStatus,
            pickOrderConsoCode: String(lotRow.pickOrderConsoCode || ""),
            noLot: Boolean(lotRow.noLot === true),
          };
  
          const batchResult = await batchSubmitList({
            userId: currentUserId || 0,
            lines: [line],
          });
  
          if (!batchResult || batchResult.code !== "SUCCESS") {
            throw new Error(batchResult?.message || "Batch submit failed after hold adjustment");
          }
  
          const conso = String(lotRow.pickOrderConsoCode || "").trim();
          if (conso) {
            try {
              await checkAndCompletePickOrderByConsoCode(conso);
            } catch (e) {
              console.error("❌ completion check after single batch:", e);
            }
          }
        }
  
        setPickExecutionFormOpen(false);
        setSelectedLotForExecutionForm(null);
  
        await fetchJobOrderData(pickOrderIdEarly);
      } catch (error) {
        console.error("Error submitting pick execution form:", error);
        throw error;
      }
    },
    [
      fetchJobOrderData,
      getAllLotsFromHierarchical,
      currentUserId,
      selectedLotForExecutionForm,
      updateHandledBy,
      filterArgs,
      session?.user?.name,
      batchSubmitList,
      checkAndCompletePickOrderByConsoCode,
      isLotAvailabilityExpired,
      isInventoryLotLineUnavailable,
    ],
  );
  //  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 handlePageChange = useCallback((event: unknown, newPage: number) => {
    setPaginationController(prev => ({
      ...prev,
      pageNum: newPage,
    }));
  }, []);

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

  // Pagination data with sorting by routerIndex
  const paginatedData = useMemo(() => {

    const sourceData = selectedFloor ? filteredByFloor : combinedLotData;
const sortedData = [...sourceData].sort((a, b) => {
      const floorA = extractFloor(a);
      const floorB = extractFloor(b);
      const orderA = floorSortOrder(floorA);
      const orderB = floorSortOrder(floorB);
      if (orderA !== orderB) return orderB - orderA; // 4F, 3F, 2F
      // 同楼层再按 routerIndex、pickOrderCode、lotNo
      const aIndex = a.routerIndex ?? 0;
      const bIndex = b.routerIndex ?? 0;
      if (aIndex !== bIndex) return aIndex - bIndex;
      
      // Secondary sort: by pickOrderCode if routerIndex is the same
      if (a.pickOrderCode !== b.pickOrderCode) {
        return a.pickOrderCode.localeCompare(b.pickOrderCode);
      }
      
      // Tertiary sort: by lotNo if everything else is the same
      return (a.lotNo || '').localeCompare(b.lotNo || '');
    });
    
    const startIndex = paginationController.pageNum * paginationController.pageSize;
    const endIndex = startIndex + paginationController.pageSize;
    return sortedData.slice(startIndex, endIndex);
  }, [selectedFloor, filteredByFloor, combinedLotData, paginationController]);

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

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

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

  return (
    <TestQrCodeProvider
      lotData={combinedLotData}
      onScanLot={handleQrCodeSubmit}
      filterActive={(lot) => (
        lot.lotAvailability !== 'rejected' &&
        lot.stockOutLineStatus !== 'rejected' &&
        lot.stockOutLineStatus !== 'completed'
      )}
    >
      <FormProvider {...formProps}>
        <Stack spacing={2}>
          {/* Progress bar + scan status fixed at top */}
          <Box
            sx={{
              position: 'fixed',
              top: 0,
              left: 0,
              right: 0,
              zIndex: 1100,
              backgroundColor: 'background.paper',
              pt: 2,
              pb: 1,
              px: 2,
              borderBottom: '1px solid',
              borderColor: 'divider',
              boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
            }}
          >
            <LinearProgressWithLabel
              completed={progress.completed}
              total={progress.total}
              label={t("Progress")}
            />
            <ScanStatusAlert
              error={qrScanError}
              success={qrScanSuccess}
              errorMessage={qrScanErrorMsg || t("QR code does not match any item in current orders.")}
              successMessage={t("QR code verified.")}
            />
          </Box>
          <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap' }}>
  <Button
    variant={selectedFloor === null ? 'contained' : 'outlined'}
    size="small"
    onClick={() => setSelectedFloor(null)}
  >
    {t("All")}
  </Button>
  {availableFloors.map(floor => (
    <Button
      key={floor}
      variant={selectedFloor === floor ? 'contained' : 'outlined'}
      size="small"
      onClick={() => setSelectedFloor(floor)}
    >
      {floor}
    </Button>
  ))}
</Box>
        {/* Job Order Header */}
        {jobOrderData && (
          <Paper sx={{ p: 2 }}>
            <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap">
              <Typography variant="subtitle1">
                <strong>{t("Job Order")}:</strong> {jobOrderData.pickOrder?.jobOrder?.code || '-'}
         </Typography>
              <Typography variant="subtitle1">
                <strong>{t("Pick Order Code")}:</strong> {jobOrderData.pickOrder?.code || '-'}
              </Typography>
              <Typography variant="subtitle1">
                <strong>{t("Target Date")}:</strong> {jobOrderData.pickOrder?.targetDate || '-'}
              </Typography>
          
            </Stack>
          </Paper>
        )}


        {/* Combined Lot Table */}
        <Box sx={{ mt: 10 }}>
          <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
          
            
            <Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
              {!isManualScanning ? (
             <Button
                  variant="contained"
                  startIcon={<QrCodeIcon />}
                  onClick={handleStartScan}
                  color="primary"
                  sx={{ minWidth: '120px' }}
                >
                  {t("Start QR Scan")}
                </Button>
              ) : (
                <Button
               variant="outlined"
                  startIcon={<QrCodeIcon />}
                  onClick={handleStopScan}
                  color="secondary"
                  sx={{ minWidth: '120px' }}
                >
                  {t("Stop QR Scan")}
             </Button>
              )}
               {/*  ADD THIS: Submit All Scanned Button */}
               <Button
                variant="contained"
                color="success"
                onClick={handleSubmitAllScanned}
                disabled={scannedItemsCount === 0 || isSubmittingAll}
                sx={{ minWidth: '160px' }}
              >
                {isSubmittingAll ? (
                  <>
                    <CircularProgress size={16} sx={{ mr: 1 }} />
                    {t("Submitting...")}
                  </>
                ) : (
                  `${t("Submit All Scanned")} (${scannedItemsCount})`
                )}
              </Button>
             
        </Box>
            </Box>
          
            <TableContainer component={Paper}>
              <Table>
                <TableHead>
                  <TableRow>
                  <TableCell>{t("Index")}</TableCell>
                  <TableCell>{t("Route")}</TableCell>
                  <TableCell>{t("Handler")}</TableCell>
                    <TableCell>{t("Item Code")}</TableCell>
                    <TableCell>{t("Item Name")}</TableCell>
                  <TableCell>{t("Lot No")}</TableCell>
                  <TableCell align="right">{t("Lot Required Pick Qty")}</TableCell>
                  <TableCell align="right">{t("Available Qty")}</TableCell>
                  <TableCell align="center">{t("Scan Result")}</TableCell>
                  <TableCell align="center">{t("Qty will submit")}</TableCell>
                  <TableCell align="center">{t("Submit Required Pick Qty")}</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                {paginatedData.length === 0 ? (
                  <TableRow>
                    <TableCell colSpan={11} align="center">
                      <Typography variant="body2" color="text.secondary">
                        {t("No data available")}
                      </Typography>
                    </TableCell>
                  </TableRow>
                ) : (
                  paginatedData.map((lot, index) => (
                    <TableRow 
                      key={`${lot.pickOrderLineId}-${lot.lotId}`}
                      sx={{
                       // backgroundColor: lot.lotAvailability === 'rejected' ? 'grey.100' : 'inherit',
                        //opacity: lot.lotAvailability === 'rejected' ? 0.6 : 1,
                        '& .MuiTableCell-root': {
                         // color: lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit'
                        }
                      }}
                    >
                      <TableCell>
                        <Typography variant="body2" fontWeight="bold">
                          {index + 1}
                        </Typography>
                      </TableCell>
                      <TableCell>
                        <Typography variant="body2">
                          {lot.routerRoute || '-'}
                        </Typography>
                      </TableCell>
                      <TableCell>{lot.handler || '-'}</TableCell>
                      <TableCell>{lot.itemCode}</TableCell>
                      <TableCell>{lot.itemName+'('+lot.uomDesc+')'}</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>
                        </Box>
                      </TableCell>
                      <TableCell align="right">
                        {(() => {
                          const requiredQty = lot.requiredQty || 0;
                          const unit = (lot.noLot === true || !lot.lotId)
                            ? (lot.uomDesc || "")
                            : ( lot.uomDesc || "");
                          return `${requiredQty.toLocaleString()}(${unit})`;
                        })()}
                      </TableCell>
                      <TableCell align="right">
                        {(() => {
                          const avail = lot.itemTotalAvailableQty;
                          if (avail == null) return "-";
                          const unit = lot.uomDesc ||  "";
                          return `${Number(avail).toLocaleString()}(${unit})`;
                        })()}
                      </TableCell>
                    
                      <TableCell align="center">
            {(() => {
              const status = lot.stockOutLineStatus?.toLowerCase();
              const isRejected = status === 'rejected' || lot.lotAvailability === 'rejected';
              const isNoLot = !lot.lotNo;

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

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

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

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

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

                      <TableCell align="center">
                        {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>
          );
        }
        
        // 正常 lot：显示按钮
        return (
          
          <Stack direction="row" spacing={1} alignItems="center">
            <Button
              variant="contained"
              onClick={async () => {
                const solId = Number(lot.stockOutLineId) || 0;
                if (solId > 0) {
                  setActionBusyBySolId(prev => ({ ...prev, [solId]: true }));
                }
                try {
                  const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
                  const submitQty = resolveSingleSubmitQty(lot);
                  handlePickQtyChange(lotKey, submitQty);

                  await handleSubmitPickQtyWithQty(lot, submitQty);
                } finally {
                  if (solId > 0) {
                    setActionBusyBySolId(prev => ({ ...prev, [solId]: false }));
                  }
                }
              }}
              /*
              disabled={
                (Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true) ||
                (lot.lotAvailability === 'expired' || 
                isInventoryLotLineUnavailable(lot) || 
                lot.lotAvailability === 'rejected') ||
                lot.stockOutLineStatus === 'completed' ||
                lot.stockOutLineStatus === 'pending'
              }
              */
              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" ||
                lot.noLot === true ||
                !lot.lotId ||
                (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="Report missing or bad items"
            >
              {t("Edit")}
            </Button>
            <Button
              variant="outlined"
              size="small"
              onClick={async () => {
                const solId = Number(lot.stockOutLineId) || 0;
                if (solId > 0) {
                  setActionBusyBySolId(prev => ({ ...prev, [solId]: true }));
                }
                try {
                  // ✅ 更新 handler 后再提交
                  if (currentUserId && lot.pickOrderId && lot.itemId) {
                    try {
                      await updateHandledBy(lot.pickOrderId, lot.itemId);
                    } catch (error) {
                      console.error("❌ Error updating handler (non-critical):", error);
                    }
                  }
                  await handleSubmitPickQtyWithQty(lot, 0);
                } finally {
                  if (solId > 0) {
                    setActionBusyBySolId(prev => ({ ...prev, [solId]: false }));
                  }
                }
              }}
              disabled={
                (Number(lot.stockOutLineId) > 0 && actionBusyBySolId[Number(lot.stockOutLineId)] === true) ||
                lot.stockOutLineStatus === 'completed' ||
                lot.stockOutLineStatus === 'checked' ||
                lot.stockOutLineStatus === 'partially_completed' ||
                lot.lotAvailability === 'expired' ||
                isUnavailableLot ||
                lot.noLot === true ||
                !lot.lotId ||
                (Number(lot.stockOutLineId) > 0 &&
                  issuePickedQtyBySolId[Number(lot.stockOutLineId)] !== undefined)
              }
              title={isUnavailableLot ? t("is unavable. Please check around have available QR code or not.") : undefined}
              sx={{ fontSize: '0.7rem', py: 0.5, minHeight: '28px', minWidth: '90px' }}
            >
              {t("Just Complete")}
            </Button>
          </Stack>
        );
      })()}
    </Box>
  </TableCell>
                    </TableRow>
                  ))
                )}
                </TableBody>
              </Table>
          </TableContainer>
          
              <TablePagination
                component="div"
                count={selectedFloor ? filteredByFloor.length : combinedLotData.length}
                page={paginationController.pageNum}
            rowsPerPage={paginationController.pageSize}
                onPageChange={handlePageChange}
                onRowsPerPageChange={handlePageSizeChange}
            rowsPerPageOptions={[10, 25, 50]}
            labelRowsPerPage={t("Rows per page")}
            labelDisplayedRows={({ from, to, count }) => 
              `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
            }
              />
        </Box>
      </Stack>

      {/*  QR Code Modal */}
      {!lotConfirmationOpen && (
      <QrCodeModal
        open={qrModalOpen}
        onClose={() => {
          setQrModalOpen(false);
          setSelectedLotForQr(null);
          // Keep scanner active like GoodPickExecutiondetail.
          resetScan();
        }}
        lot={selectedLotForQr}
        combinedLotData={combinedLotData}
        onQrCodeSubmit={handleQrCodeSubmitFromModal}
      />
      )}
       {/*  Add Lot Confirmation Modal */}
       {lotConfirmationOpen && expectedLotData && scannedLotData && (
        <LotConfirmationModal
          open={lotConfirmationOpen}
          onClose={() => {
            console.log(`⏱️ [LOT CONFIRM MODAL] Closing modal, reset scanner and release raw-QR dedupe`);
            if (resetScanRef.current) {
              resetScanRef.current();
            }
            clearLotConfirmationState(false);
            setTimeout(() => {
              lastProcessedQrRef.current = '';
              processedQrCodesRef.current.clear();
            }, 250);
          }}
          onConfirm={handleLotConfirmation}
          expectedLot={expectedLotData}
          scannedLot={scannedLotData}
          isLoading={isConfirmingLot}
          errorMessage={lotConfirmationError}
        />
      )}

      {/* Manual Lot Confirmation Modal (test shortcut {2fic}) */}
      <ManualLotConfirmationModal
        open={manualLotConfirmationOpen}
        onClose={() => setManualLotConfirmationOpen(false)}
        // Reuse existing handler: expectedLotInput=current lot, scannedLotInput=new lot
        onConfirm={(currentLotNo, newLotNo) => {
          // Use existing manual flow from handleManualLotConfirmation in other screens:
          // Here we route through updateStockOutLineStatusByQRCodeAndLotNo via handleManualLotConfirmation-like inline logic.
          // For now: open LotConfirmationModal path by setting expected/scanned and letting user confirm substitution.
          setExpectedLotData({ lotNo: currentLotNo, itemCode: '', itemName: '' });
          setScannedLotData({ lotNo: newLotNo, itemCode: '', itemName: '', inventoryLotLineId: null, stockInLineId: null });
          setManualLotConfirmationOpen(false);
          setLotConfirmationOpen(true);
        }}
        expectedLot={expectedLotData}
        scannedLot={scannedLotData}
        isLoading={isConfirmingLot}
      />
      {/*  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,
            //  Add missing required properties from GetPickOrderLineInfo interface
            availableQty: selectedLotForExecutionForm.availableQty || 0,
            requiredQty: selectedLotForExecutionForm.requiredQty || 0,
            uomDesc: selectedLotForExecutionForm.uomDesc || '',
            uomShortDesc: selectedLotForExecutionForm.uomShortDesc || '',
            pickedQty: selectedLotForExecutionForm.actualPickQty || 0,
            suggestedList: [],
            noLotLines: []
          }}
          pickOrderId={selectedLotForExecutionForm.pickOrderId}
          pickOrderCreateDate={new Date()}
        />
      )}
    </FormProvider>
    </TestQrCodeProvider>
  );
};

export default JobPickExecution