"use client";

import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  Alert,
  Box,
  Button,
  Checkbox,
  CircularProgress,
  Grid,
  Modal,
  Paper,
  Stack,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TablePagination,
  TableRow,
  TextField,
  Typography,
} from "@mui/material";
import { useSession } from "next-auth/react";
import { useTranslation } from "react-i18next";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import SearchBox, { Criterion } from "../SearchBox";
import { OUTPUT_DATE_FORMAT, arrayToDayjs } from "@/app/utils/formatUtil";
import { SessionWithTokens } from "@/config/authConfig";
import {
  fetchPickOrderWithStockClient,
  fetchWorkbenchPickOrderLineDetailV2,
  confirmLotSubstitution,
  suggestPickOrderWorkbenchV2,
  type PickOrderLotDetailResponse,
} from "@/app/api/pickOrder/actions";
import { workbenchScanPick } from "@/app/api/doworkbench/actions";
import { workbenchScanPickResponseNeedsFullRefresh } from "@/app/api/doworkbench/workbenchScanPickUtils";
import { fetchStockInLineInfo } from "@/app/api/po/actions";
import WorkbenchLotLabelPrintModal from "@/components/DoWorkbench/WorkbenchLotLabelPrintModal";
import TestQrCodeProvider from "../QrCodeScannerProvider/TestQrCodeProvider";
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
import ScanStatusAlert from "@/components/common/ScanStatusAlert";

dayjs.extend(arraySupport);

type TopRow = {
  rowKey: string;
  pickOrderId: number;
  pickOrderLineId: number;
  pickOrderCode: string;
  itemCode: string;
  itemName: string;
  requiredQty: number;
  currentStock: number;
  pickedQty: number;
  stockUnit: string;
  targetDate: string | number[];
  status: string;
};

type LotRow = {
  key: string;
  pickOrderId: number;
  pickOrderLineId: number;
  pickOrderCode: string;
  itemCode: string;
  itemName: string;
  uomDesc: string;
  requiredQty: number;
  availableQty: number;
  itemTotalAvailableQty?: number | null;
  stockOutLineId: number;
  status: string;
  pickedQty: number;
  lotNo: string;
  location: string;
  itemId?: number;
  stockInLineId?: number;
  suggestedPickLotId?: number;
  lotAvailability?: string;
  lotStatus?: string;
  expiryDate?: string;
  stockOutLineRejectMessage?: string;
};

type ConfirmLotState = {
  lotNo: string;
  itemCode: string;
  itemName: string;
  stockInLineId?: number;
  row: LotRow;
};

type LotRowIndexes = {
  byItemId: Map<number, LotRow[]>;
  byStockInLineId: Map<number, LotRow[]>;
  activeLotsByItemId: Map<number, LotRow[]>;
};

const ManualLotConfirmationModal: React.FC<{
  open: boolean;
  onClose: () => void;
  onConfirm: (expectedLotNo: string, scannedLotNo: string) => void;
  expectedLot: { lotNo: string; itemCode: string; itemName: string } | null;
  scannedLot: { lotNo: string; itemCode: string; itemName: string } | null;
  isLoading?: boolean;
}> = ({ open, onClose, onConfirm, expectedLot, scannedLot, isLoading = false }) => {
  const { t } = useTranslation("pickOrder");

  const [expectedLotInput, setExpectedLotInput] = useState("");
  const [scannedLotInput, setScannedLotInput] = useState("");
  const [error, setError] = useState("");
  useEffect(() => {
    if (!open) return;
    setExpectedLotInput(expectedLot?.lotNo || "");
    setScannedLotInput(scannedLot?.lotNo || "");
    setError("");
  }, [expectedLot, open, scannedLot]);
  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>
        <TextField
          fullWidth
          size="small"
          label={t("Expected Lot Number")}
          value={expectedLotInput}
          onChange={(e) => {
            setExpectedLotInput(e.target.value);
            setError("");
          }}
          sx={{ mb: 2 }}
        />
        <TextField
          fullWidth
          size="small"
          label={t("Scanned Lot Number")}
          value={scannedLotInput}
          onChange={(e) => {
            setScannedLotInput(e.target.value);
            setError("");
          }}
        />
        {error ? (
          <Box sx={{ mt: 2, p: 1, borderRadius: 1, bgcolor: "#ffebee" }}>
            <Typography variant="body2" color="error">
              {error}
            </Typography>
          </Box>
        ) : null}
        <Box sx={{ mt: 2, display: "flex", justifyContent: "flex-end", gap: 2 }}>
          <Button onClick={onClose} variant="outlined" disabled={isLoading}>
            {t("Cancel")}
          </Button>
          <Button
            onClick={() => {
              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());
            }}
            variant="contained"
            color="warning"
            disabled={isLoading}
          >
            {isLoading ? t("Processing...") : t("Confirm")}
          </Button>
        </Box>
      </Box>
    </Modal>
  );
};

interface Props {
  filterArgs?: Record<string, unknown>;
}

const toNum = (v: unknown, d = 0): number => {
  const n = Number(v);
  return Number.isFinite(n) ? n : d;
};

const toStr = (v: unknown): string => (typeof v === "string" ? v : "");

const isCompletedStatus = (status: string | undefined): boolean => {
  const s = String(status || "").toLowerCase();
  return s === "completed" || s === "partially_completed" || s === "partially_complete";
};

const isCheckedStatus = (status: string | undefined): boolean =>
  String(status || "").toLowerCase() === "checked";

const isRejectedStatus = (status: string | undefined): boolean =>
  String(status || "").toLowerCase() === "rejected";

const isInventoryLotLineUnavailable = (row: LotRow): boolean => {
  const solSt = String(row.status || "").toLowerCase();
  if (solSt === "completed" || solSt === "partially_completed" || solSt === "partially_complete") return false;
  if (String(row.lotAvailability || "").toLowerCase() === "status_unavailable") return true;
  return String(row.lotStatus || "").toLowerCase() === "unavailable";
};

const isLotExpired = (row: LotRow): boolean => {
  if (String(row.lotAvailability || "").toLowerCase() === "expired") return true;
  if (!row.expiryDate) return false;
  const d = dayjs(row.expiryDate).startOf("day");
  return d.isValid() && d.isBefore(dayjs().startOf("day"));
};

const isNonBlockingSwitchLotReject = (code: unknown, message: unknown): boolean => {
  const c = String(code || "").toUpperCase();
  const m = String(message || "");
  if (c === "SUCCESS_UNAVAILABLE" || c === "BOUND_UNAVAILABLE") return true;
  if (/^Reject switch lot:/i.test(m)) return true;
  if (/available\s*=\s*\d+(\.\d+)?\s*<\s*required\s*=\s*\d+(\.\d+)?/i.test(m)) return true;
  return false;
};

function safeDisplayTargetDate(targetDate: string | number[]): string {
  try {
    if (Array.isArray(targetDate) && targetDate.length >= 3) {
      return arrayToDayjs(targetDate).format(OUTPUT_DATE_FORMAT);
    }
    const value = typeof targetDate === "string" ? targetDate : String(targetDate ?? "");
    const d = dayjs(value);
    return d.isValid() ? d.format(OUTPUT_DATE_FORMAT) : "-";
  } catch {
    return "-";
  }
}

function lineHasStockOutOrSuggestion(details: PickOrderLotDetailResponse[]): boolean {
  if (!details.length) return false;
  return details.some((d) => {
    const sol = toNum(d.stockOutLineId);
    const spl = toNum(d.suggestedPickLotId);
    return sol > 0 || spl > 0 || d.noLot === true;
  });
}

function mapLotDetailsToRows(
  details: PickOrderLotDetailResponse[],
  ctx: {
    pickOrderId: number;
    pickOrderLineId: number;
    pickOrderCode: string;
    itemCode: string;
    itemName: string;
    totalAvailableQty?: number | null;
  },
): LotRow[] {
  return details.map((d, i) => {
    const solId = toNum(d.stockOutLineId);
    const lotId = toNum(d.lotId, i);
    const stockInLineId = toNum(d.stockInLineId);
    return {
      key: solId > 0 ? `sol:${solId}` : `lot:${lotId}:${i}`,
      pickOrderId: ctx.pickOrderId,
      pickOrderLineId: ctx.pickOrderLineId,
      pickOrderCode: ctx.pickOrderCode,
      itemCode: ctx.itemCode,
      itemName: ctx.itemName,
      uomDesc: toStr(d.stockUnit),
      requiredQty: toNum(d.requiredQty),
      availableQty: toNum(d.remainingAfterAllPickOrders ?? d.availableQty),
      itemTotalAvailableQty: toNum(ctx.totalAvailableQty),
      stockOutLineId: solId,
      status: toStr(d.stockOutLineStatus ?? "pending"),
      pickedQty: toNum(d.actualPickQty ?? d.stockOutLineQty),
      lotNo: toStr(d.lotNo),
      location: toStr(d.location),
      itemId: toNum(d.itemId) || undefined,
      stockInLineId: stockInLineId > 0 ? stockInLineId : undefined,
      suggestedPickLotId: toNum(d.suggestedPickLotId) || undefined,
      lotAvailability: toStr((d as any).lotAvailability),
      lotStatus: toStr((d as any).lotStatus),
      expiryDate: toStr((d as any).expiryDate),
      stockOutLineRejectMessage: toStr((d as any).stockOutLineRejectMessage),
    };
  });
}

const WorkbenchPickExecution: React.FC<Props> = ({ filterArgs }) => {
  const { t } = useTranslation("pickOrder");
  const { data: session } = useSession() as { data: SessionWithTokens | null };
  const userId = session?.id ? parseInt(session.id, 10) : 0;

  const [originalTopRows, setOriginalTopRows] = useState<TopRow[]>([]);
  const [filteredTopRows, setFilteredTopRows] = useState<TopRow[]>([]);
  const [pickOrderLoading, setPickOrderLoading] = useState(false);
  const [pagingController, setPagingController] = useState({ pageNum: 1, pageSize: 10 });
  const [totalCountItems, setTotalCountItems] = useState(0);
  const localizeBackendMessage = (msg: unknown, fallbackKey: string) => {
    const text = typeof msg === "string" ? msg.trim() : "";
    if (!text) return t(fallbackKey);
    return t(text, { defaultValue: text });
  };
  const [selectedPickOrderLineId, setSelectedPickOrderLineId] = useState<number | null>(null);
  const [selectedPickOrderId, setSelectedPickOrderId] = useState<number | null>(null);
  const [selectedTopMeta, setSelectedTopMeta] = useState<{
    pickOrderCode: string;
    itemCode: string;
    itemName: string;
    totalAvailableQty?: number;
  } | null>(null);

  const [lotRows, setLotRows] = useState<LotRow[]>([]);
  const [qtyBySolId, setQtyBySolId] = useState<Record<number, number>>({});
  const [qtyEditableBySolId, setQtyEditableBySolId] = useState<Record<number, boolean>>({});
  const [lotPagingController, setLotPagingController] = useState({ pageNum: 0, pageSize: 10 });
  const [loading, setLoading] = useState(false);
  const [submittingSolId, setSubmittingSolId] = useState<number | null>(null);
  const [message, setMessage] = useState("");
  const [error, setError] = useState("");
  const [workbenchLotLabelModalOpen, setWorkbenchLotLabelModalOpen] = useState(false);
  const [workbenchLotLabelContextLot, setWorkbenchLotLabelContextLot] = useState<LotRow | null>(null);
  const [workbenchLotLabelInitialPayload, setWorkbenchLotLabelInitialPayload] =
    useState<{ itemId: number; stockInLineId: number } | null>(null);
  const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
  const [lotConfirmationError, setLotConfirmationError] = useState<string | null>(null);
  const [expectedLotData, setExpectedLotData] = useState<ConfirmLotState | null>(null);
  const [scannedLotData, setScannedLotData] = useState<ConfirmLotState | null>(null);
  const [isConfirmingLot, setIsConfirmingLot] = useState(false);
  const [manualLotConfirmationOpen, setManualLotConfirmationOpen] = useState(false);
  const [qrScanError, setQrScanError] = useState(false);
  const [qrScanSuccess, setQrScanSuccess] = useState(false);
  const [qrScanErrorMsg, setQrScanErrorMsg] = useState("");
  const [qrScanSuccessMsg, setQrScanSuccessMsg] = useState("");
  const lastProcessedQrRef = useRef<string>("");
  const processedQrCodesRef = useRef<Set<string>>(new Set());
  const lotConfirmLastQrRef = useRef<string>("");
  const lotConfirmSkipNextScanRef = useRef<boolean>(false);
  const lotConfirmOpenedAtRef = useRef<number>(0);

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

  const paginatedTopRows = useMemo(() => {
    const start = (pagingController.pageNum - 1) * pagingController.pageSize;
    return filteredTopRows.slice(start, start + pagingController.pageSize);
  }, [filteredTopRows, pagingController]);

  const paginatedLotRows = useMemo(() => {
    const start = lotPagingController.pageNum * lotPagingController.pageSize;
    return lotRows.slice(start, start + lotPagingController.pageSize);
  }, [lotRows, lotPagingController]);

  const lotRowIndexes = useMemo<LotRowIndexes>(() => {
    const byItemId = new Map<number, LotRow[]>();
    const byStockInLineId = new Map<number, LotRow[]>();
    const activeLotsByItemId = new Map<number, LotRow[]>();

    for (const row of lotRows) {
      const itemId = Number(row.itemId);
      const stockInLineId = Number(row.stockInLineId);
      const isActive =
        row.stockOutLineId > 0 &&
        !isCompletedStatus(row.status) &&
        !isCheckedStatus(row.status);

      if (Number.isFinite(itemId) && itemId > 0) {
        if (!byItemId.has(itemId)) byItemId.set(itemId, []);
        byItemId.get(itemId)!.push(row);
        if (isActive) {
          if (!activeLotsByItemId.has(itemId)) activeLotsByItemId.set(itemId, []);
          activeLotsByItemId.get(itemId)!.push(row);
        }
      }

      if (Number.isFinite(stockInLineId) && stockInLineId > 0) {
        if (!byStockInLineId.has(stockInLineId)) byStockInLineId.set(stockInLineId, []);
        byStockInLineId.get(stockInLineId)!.push(row);
      }
    }

    return { byItemId, byStockInLineId, activeLotsByItemId };
  }, [lotRows]);

  const fetchNewPageItems = useCallback(
    async (paging: { pageNum: number; pageSize: number }, extra: Record<string, unknown>) => {
      if (!userId) return;
      setPickOrderLoading(true);
      setError("");
      try {
        const params = {
          ...extra,
          pageNum: 0,
          pageSize: 9999,
          status: "released",
          type: "consumable",
          assignTo: userId,
        };
        const res = await fetchPickOrderWithStockClient(params);
        const records = Array.isArray(res?.records) ? res.records : [];
        const rows: TopRow[] = records.flatMap((r: any) => {
          const pickOrderId = toNum(r?.id);
          const code = toStr(r?.code);
          const status = toStr(r?.status);
          const targetDate = r?.targetDate;
          const lines = Array.isArray(r?.pickOrderLines) ? r.pickOrderLines : [];
          return lines.map((line: any, idx: number) => ({
            rowKey: `po:${pickOrderId}:line:${toNum(line?.id, idx)}`,
            pickOrderId,
            pickOrderLineId: toNum(line?.id),
            pickOrderCode: code,
            itemCode: toStr(line?.itemCode),
            itemName: toStr(line?.itemName),
            requiredQty: toNum(line?.requiredQty),
            currentStock: toNum(line?.availableQty),
            pickedQty: toNum(line?.pickedQty),
            stockUnit: toStr(line?.uomDesc ?? line?.uomShortDesc),
            targetDate: targetDate ?? "",
            status,
          }));
        });
        setOriginalTopRows(rows);
        setFilteredTopRows(rows);
        const pageSize = paging.pageSize || 10;
        const pageNum = paging.pageNum || 1;
        setTotalCountItems(rows.length);
        setPagingController({ pageNum, pageSize });
        return rows;
      } catch (e) {
        console.error(e);
        setError(t("Load released pick orders failed"));
        setOriginalTopRows([]);
        setFilteredTopRows([]);
        setTotalCountItems(0);
        return [] as TopRow[];
      } finally {
        setPickOrderLoading(false);
      }
    },
    [t, userId],
  );

  const refreshReleasedTopRowsAfterMutation = useCallback(async () => {
    const latestRows =
      (await fetchNewPageItems(
        pagingController,
        (filterArgs || {}) as Record<string, unknown>,
      )) || [];
    if (
      selectedPickOrderLineId != null &&
      !latestRows.some((r) => r.pickOrderLineId === selectedPickOrderLineId)
    ) {
      setSelectedPickOrderLineId(null);
      setSelectedPickOrderId(null);
      setSelectedTopMeta(null);
      setLotRows([]);
      setQtyBySolId({});
      setQtyEditableBySolId({});
      setLotPagingController({ pageNum: 0, pageSize: 10 });
    }
  }, [fetchNewPageItems, filterArgs, pagingController, selectedPickOrderLineId]);

  const searchCriteria: Criterion<any>[] = useMemo(
    () => [
      { label: t("Item Code"), paramName: "itemCode", type: "text" },
      { label: t("Pick Order Code"), paramName: "pickOrderCode", type: "text" },
      { label: t("Item Name"), paramName: "itemName", type: "text" },
      { label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" },
    ],
    [t],
  );

  const handleSearch = useCallback(
    (query: Record<string, string>) => {
      const filtered = originalTopRows.filter((row) => {
        const itemCodeMatch = !query.itemCode || row.itemCode.toLowerCase().includes(query.itemCode.toLowerCase());
        const pickOrderCodeMatch =
          !query.pickOrderCode || row.pickOrderCode.toLowerCase().includes(query.pickOrderCode.toLowerCase());
        const itemNameMatch = !query.itemName || row.itemName.toLowerCase().includes(query.itemName.toLowerCase());
        const targetDate = Array.isArray(row.targetDate)
          ? arrayToDayjs(row.targetDate)
          : dayjs(typeof row.targetDate === "string" ? row.targetDate : "");
        let dateMatch = true;
        if (query.targetDate || query.targetDateTo) {
          const fromDate = query.targetDate ? dayjs(query.targetDate) : null;
          const toDate = query.targetDateTo ? dayjs(query.targetDateTo) : null;
          if (targetDate.isValid()) {
            if (fromDate && fromDate.isValid()) dateMatch = dateMatch && (targetDate.isSame(fromDate, "day") || targetDate.isAfter(fromDate, "day"));
            if (toDate && toDate.isValid()) dateMatch = dateMatch && (targetDate.isSame(toDate, "day") || targetDate.isBefore(toDate, "day"));
          }
        }
        return itemCodeMatch && pickOrderCodeMatch && itemNameMatch && dateMatch;
      });
      setFilteredTopRows(filtered);
      setTotalCountItems(filtered.length);
      setPagingController((prev) => ({ ...prev, pageNum: 1 }));
    },
    [originalTopRows],
  );

  const handleReset = useCallback(() => {
    setFilteredTopRows(originalTopRows);
    setTotalCountItems(originalTopRows.length);
    setPagingController((prev) => ({ ...prev, pageNum: 1 }));
  }, [originalTopRows]);

  useEffect(() => {
    if (userId) fetchNewPageItems(pagingController, (filterArgs || {}) as Record<string, unknown>);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [userId, filterArgs, fetchNewPageItems]);

  const loadLineDetailV2 = useCallback(
    async (
      pickOrderId: number,
      pickOrderLineId: number,
      meta: {
        pickOrderCode: string;
        itemCode: string;
        itemName: string;
        totalAvailableQty?: number;
      },
    ) => {
      if (!userId || pickOrderLineId <= 0) return;
      setLoading(true);
      setError("");
      setMessage("");
      try {
        let details = await fetchWorkbenchPickOrderLineDetailV2(pickOrderLineId);
        let list = Array.isArray(details) ? details : [];
        if (!lineHasStockOutOrSuggestion(list)) {
          const suggestRes = await suggestPickOrderWorkbenchV2(pickOrderId, userId);
          if (suggestRes.code !== "SUCCESS") {
            setError(t("Suggest pick failed"));
            setLotRows([]);
            return;
          }
          details = await fetchWorkbenchPickOrderLineDetailV2(pickOrderLineId);
          list = Array.isArray(details) ? details : [];
          setMessage(t("Suggestion success"));
        }
        setLotRows(
          mapLotDetailsToRows(list, {
            pickOrderId,
            pickOrderLineId,
            pickOrderCode: meta.pickOrderCode,
            itemCode: meta.itemCode,
            itemName: meta.itemName,
            totalAvailableQty: meta.totalAvailableQty,
          }),
        );
        setQtyEditableBySolId({});
      } catch (e) {
        console.error(e);
        setError(t("Load workbench data failed"));
        setLotRows([]);
      } finally {
        setLoading(false);
      }
    },
    [t, userId],
  );

  const submitRow = useCallback(
    async (row: LotRow, forceQty?: number) => {
      if (!userId) return;
      if (!row.stockOutLineId) {
        setError(t("No stock out line for this lot"));
        return;
      }
      const qtyInput = qtyBySolId[row.stockOutLineId];
      const qtyValue = forceQty ?? (typeof qtyInput === "number" && Number.isFinite(qtyInput) ? qtyInput : undefined);
      setSubmittingSolId(row.stockOutLineId);
      setError("");
      setMessage("");
      try {
        const res = await workbenchScanPick({
          stockOutLineId: row.stockOutLineId,
          lotNo: row.lotNo.trim(),
          ...(Number.isFinite(Number(row.stockInLineId)) && Number(row.stockInLineId) > 0
            ? { stockInLineId: Number(row.stockInLineId) }
            : {}),
          ...(typeof qtyValue === "number" && Number.isFinite(qtyValue) ? { qty: qtyValue } : {}),
          userId,
        });
        const errMsg = localizeBackendMessage(res.message, "Scan pick failed");
        setError(errMsg);
        setQrScanErrorMsg(errMsg);
        
        const okMsg = localizeBackendMessage(res.message, "Scan pick success");
        setMessage(okMsg);
        setQrScanSuccessMsg(okMsg);
        if (workbenchScanPickResponseNeedsFullRefresh(res)) {
          if (selectedPickOrderId != null && selectedPickOrderLineId != null && selectedTopMeta) {
            await loadLineDetailV2(selectedPickOrderId, selectedPickOrderLineId, selectedTopMeta);
          }
        } else {
          const entity = res.entity as any;
          setLotRows((prev) =>
            prev.map((r) =>
              r.stockOutLineId === row.stockOutLineId
                ? { ...r, status: toStr(entity?.status || r.status), pickedQty: toNum(entity?.qty, r.pickedQty) }
                : r,
            ),
          );
        }
        setWorkbenchLotLabelModalOpen(false);
        setWorkbenchLotLabelContextLot(null);
        setWorkbenchLotLabelInitialPayload(null);
        await refreshReleasedTopRowsAfterMutation();
      } catch (e) {
        console.error(e);
        setError(t("Scan pick failed"));
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(t("Scan pick failed"));
        });
      } finally {
        setSubmittingSolId(null);
      }
    },
    [qtyBySolId, loadLineDetailV2, refreshReleasedTopRowsAfterMutation, selectedPickOrderId, selectedPickOrderLineId, selectedTopMeta, t, userId],
  );

  const hasQtyOverrideBySolId = useCallback(
    (stockOutLineId: number) => Object.prototype.hasOwnProperty.call(qtyBySolId, stockOutLineId),
    [qtyBySolId],
  );

  const resolveSingleSubmitQty = useCallback(
    (lot: LotRow): number => {
      const override = qtyBySolId[lot.stockOutLineId];
      if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
        return override;
      }
      return Number(lot.requiredQty) || 0;
    },
    [qtyBySolId],
  );

  const workbenchScanPickQtyFromLot = useCallback(
    (lot: LotRow) => {
      const hasExplicitOverride = hasQtyOverrideBySolId(lot.stockOutLineId);
      const n = Number(resolveSingleSubmitQty(lot));
      if (hasExplicitOverride && Number.isFinite(n) && n === 0) return { qty: 0 } as const;
      if (!Number.isFinite(n) || n <= 0) return {};
      return { qty: n } as const;
    },
    [hasQtyOverrideBySolId, resolveSingleSubmitQty],
  );

  const handleJustComplete = useCallback(
    async (row: LotRow) => {
      if (!row.stockOutLineId) {
        setError(t("No stock out line for this lot"));
        return;
      }

      const lotNo = String(row.lotNo || "").trim();
      const isUnavailable = isInventoryLotLineUnavailable(row);
      const isExpired = isLotExpired(row);
      const hasExplicitOverride = hasQtyOverrideBySolId(row.stockOutLineId);
      const explicitQty = hasExplicitOverride ? Number(qtyBySolId[row.stockOutLineId]) : NaN;
      const qtyPayload = workbenchScanPickQtyFromLot(row);
      const wbJustQty = qtyPayload.qty;

      const canPostScanPick =
        isUnavailable ||
        (lotNo !== "" &&
          ((hasExplicitOverride && Number.isFinite(explicitQty) && explicitQty === 0) ||
            (wbJustQty != null && wbJustQty > 0)));

      if (!canPostScanPick) {
        const msg = t(
          "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.",
        );
        setError(msg);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(msg);
        });
        return;
      }

      if (isExpired && !isUnavailable) {
        const msg = t(
          "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.",
        );
        setError(msg);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(msg);
        });
        return;
      }

      const qtyToSend =
        isUnavailable || (hasExplicitOverride && Number.isFinite(explicitQty) && explicitQty === 0)
          ? 0
          : Number(wbJustQty);

      await submitRow(row, qtyToSend);
    },
    [hasQtyOverrideBySolId, qtyBySolId, submitRow, t, workbenchScanPickQtyFromLot],
  );

  const handleLineSelect = useCallback(
    async (row: TopRow, checked: boolean) => {
      if (!checked) {
        if (selectedPickOrderLineId === row.pickOrderLineId) {
          setSelectedPickOrderLineId(null);
          setSelectedPickOrderId(null);
          setSelectedTopMeta(null);
          setLotRows([]);
          setQtyBySolId({});
          setQtyEditableBySolId({});
          setLotPagingController({ pageNum: 0, pageSize: 10 });
        }
        return;
      }
      setSelectedPickOrderLineId(row.pickOrderLineId);
      setSelectedPickOrderId(row.pickOrderId);
      setSelectedTopMeta({
        pickOrderCode: row.pickOrderCode,
        itemCode: row.itemCode,
        itemName: row.itemName,
        totalAvailableQty: row.currentStock,
      });
      setLotRows([]);
      setQtyBySolId({});
      setQtyEditableBySolId({});
      setLotPagingController({ pageNum: 0, pageSize: 10 });
      setMessage("");
      await loadLineDetailV2(row.pickOrderId, row.pickOrderLineId, {
        pickOrderCode: row.pickOrderCode,
        itemCode: row.itemCode,
        itemName: row.itemName,
        totalAvailableQty: row.currentStock,
      });
    },
    [loadLineDetailV2, selectedPickOrderLineId],
  );

  const openWorkbenchLotLabelModalForLot = useCallback((lot: LotRow) => {
    const itemId = Number(lot.itemId);
    const stockInLineId = Number(lot.stockInLineId);
    setWorkbenchLotLabelContextLot(lot);
    if (Number.isFinite(itemId) && itemId > 0 && Number.isFinite(stockInLineId) && stockInLineId > 0) {
      setWorkbenchLotLabelInitialPayload({ itemId, stockInLineId });
    } else {
      setWorkbenchLotLabelInitialPayload(null);
    }
    setWorkbenchLotLabelModalOpen(true);
  }, []);

  const handleWorkbenchLotLabelScanPick = useCallback(
    async ({ inventoryLotLineId, lotNo, qty }: { inventoryLotLineId: number; lotNo: string; qty?: number }) => {
      if (!userId) throw new Error(t("User not found"));
      if (!workbenchLotLabelContextLot?.stockOutLineId) {
        throw new Error(t("No stock out line for this lot"));
      }
      const fallbackQty = Number(
        resolveSingleSubmitQty(workbenchLotLabelContextLot),
      );
      const res = await workbenchScanPick({
        stockOutLineId: workbenchLotLabelContextLot.stockOutLineId,
        inventoryLotLineId,
        lotNo,
        ...(typeof qty === "number" && Number.isFinite(qty)
          ? { qty }
          : Number.isFinite(fallbackQty) && fallbackQty >= 0
            ? { qty: fallbackQty }
            : {}),
        userId,
      });
      if (res.code !== "SUCCESS") {
        throw new Error((res.message as string) || t("Scan pick failed"));
      }
      if (selectedPickOrderId != null && selectedPickOrderLineId != null && selectedTopMeta) {
        await loadLineDetailV2(selectedPickOrderId, selectedPickOrderLineId, selectedTopMeta);
      }
      setWorkbenchLotLabelModalOpen(false);
      setWorkbenchLotLabelContextLot(null);
      setWorkbenchLotLabelInitialPayload(null);
    },
    [
      loadLineDetailV2,
      qtyBySolId,
      selectedPickOrderId,
      selectedPickOrderLineId,
      selectedTopMeta,
      t,
      userId,
      workbenchLotLabelContextLot,
    ],
  );

  const handleScanLotByLotNo = useCallback(
    async (lotNo: string) => {
      const normalized = String(lotNo || "").trim();
      if (!normalized) return;
      const target = lotRows.find(
        (r) =>
          String(r.lotNo || "").trim() === normalized &&
          r.stockOutLineId > 0 &&
          !isCompletedStatus(r.status) &&
          !isCheckedStatus(r.status),
      );
      if (!target) {
        setError(t("Lot not found in current line"));
        return;
      }
      await submitRow(target);
    },
    [lotRows, submitRow, t],
  );

  const resolveScanCandidate = useCallback(
    (rawQr: string): ConfirmLotState | null => {
      const latest = String(rawQr || "").trim();
      if (!latest) return null;
      try {
        const parsed = JSON.parse(latest);
        const stockInLineId = toNum(parsed?.stockInLineId);
        if (stockInLineId > 0) {
          const row = lotRows.find((r) => Number(r.stockInLineId) === stockInLineId && r.stockOutLineId > 0);
          if (!row) return null;
          return {
            lotNo: String(row.lotNo || "").trim(),
            itemCode: row.itemCode,
            itemName: row.itemName,
            stockInLineId,
            row,
          };
        }
      } catch {
        // non-json; fallback to lotNo match
      }
      const lotNo = latest.replace(/[{}]/g, "").trim();
      if (!lotNo) return null;
      const row = lotRows.find((r) => String(r.lotNo || "").trim() === lotNo && r.stockOutLineId > 0);
      if (!row) return null;
      return {
        lotNo,
        itemCode: row.itemCode,
        itemName: row.itemName,
        stockInLineId: row.stockInLineId,
        row,
      };
    },
    [lotRows],
  );

  const toConfirmLotState = useCallback((row: LotRow): ConfirmLotState => {
    return {
      lotNo: String(row.lotNo || "").trim(),
      itemCode: row.itemCode,
      itemName: row.itemName,
      stockInLineId: row.stockInLineId,
      row,
    };
  }, []);

  const toConfirmLotStateWithOverrides = useCallback(
    (row: LotRow, override: { lotNo?: string; stockInLineId?: number }): ConfirmLotState => {
      return {
        lotNo: String(override.lotNo ?? row.lotNo ?? "").trim(),
        itemCode: row.itemCode,
        itemName: row.itemName,
        stockInLineId: override.stockInLineId ?? row.stockInLineId,
        row,
      };
    },
    [],
  );

  const pickExpectedRowForSubstitution = useCallback((rows: LotRow[]): LotRow | null => {
    if (!rows.length) return null;
    const withLotNo = rows.filter((r) => String(r.lotNo || "").trim() !== "");
    if (withLotNo.length === 1) return withLotNo[0];
    if (withLotNo.length > 1) {
      const pending = withLotNo.find((r) => String(r.status || "").toLowerCase() === "pending");
      return pending || withLotNo[0];
    }
    return rows[0];
  }, []);

  const clearLotConfirmationState = useCallback((clearProcessedRefs = false) => {
    setLotConfirmationOpen(false);
    setLotConfirmationError(null);
    setExpectedLotData(null);
    setScannedLotData(null);
    lotConfirmLastQrRef.current = "";
    lotConfirmSkipNextScanRef.current = false;
    lotConfirmOpenedAtRef.current = 0;
    if (clearProcessedRefs) {
      setTimeout(() => {
        lastProcessedQrRef.current = "";
        processedQrCodesRef.current.clear();
      }, 100);
    }
  }, []);

  const handleLotConfirmation = useCallback(
    async (overrideScanned?: ConfirmLotState, overrideExpected?: ConfirmLotState) => {
      const expected = overrideExpected ?? expectedLotData;
      const scanned = overrideScanned ?? scannedLotData;
      if (!expected || !scanned) return;
      setIsConfirmingLot(true);
      setLotConfirmationError(null);
      setError("");
      setMessage("");
      try {
        const originalSuggestedPickLotId = Number(expected.row.suggestedPickLotId || 0);
        let switchedToUnavailable = false;
        if (originalSuggestedPickLotId > 0) {
          const res = await confirmLotSubstitution({
            pickOrderLineId: expected.row.pickOrderLineId,
            stockOutLineId: expected.row.stockOutLineId,
            originalSuggestedPickLotId,
            newInventoryLotNo: scanned.lotNo,
            newStockInLineId: Number(scanned.stockInLineId ?? 0),
          });
          switchedToUnavailable = res.code === "SUCCESS_UNAVAILABLE" || res.code === "BOUND_UNAVAILABLE";
          const nonBlockingReject = isNonBlockingSwitchLotReject(res.code, res.message);
          if (res.code !== "SUCCESS" && !switchedToUnavailable && !nonBlockingReject) {
            const msg = (res.message as string) || t("Lot switch failed");
            setLotConfirmationError(msg);
            setError(msg);
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(msg);
            });
            return;
          }
          if (nonBlockingReject && !switchedToUnavailable) {
            const warnMsg = (res.message as string) || t("Lot switch rejected. Continue with scan-pick.");
            setMessage(warnMsg);
          }
        }

        if (!switchedToUnavailable) {
          const res = await workbenchScanPick({
            stockOutLineId: expected.row.stockOutLineId,
            lotNo: scanned.lotNo,
            ...(Number.isFinite(Number(scanned.stockInLineId)) && Number(scanned.stockInLineId) > 0
              ? { stockInLineId: Number(scanned.stockInLineId) }
              : {}),
            ...workbenchScanPickQtyFromLot(expected.row),
            userId,
          });
          if (res.code !== "SUCCESS") {
            const msg = (res.message as string) || t("Workbench scan-pick failed.");
            setLotConfirmationError(msg);
            setError(msg);
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(msg);
            });
            return;
          }
        }
        setMessage(t("Scan pick success"));
        startTransition(() => {
          setQrScanError(false);
          setQrScanSuccess(true);
          setQrScanSuccessMsg(t("Scan pick success"));
        });
        if (selectedPickOrderId != null && selectedPickOrderLineId != null && selectedTopMeta) {
          await loadLineDetailV2(selectedPickOrderId, selectedPickOrderLineId, selectedTopMeta);
        }
        await refreshReleasedTopRowsAfterMutation();
        clearLotConfirmationState(true);
      } catch (e) {
        console.error(e);
        const msg = t("Lot confirmation failed. Please try again.");
        setLotConfirmationError(msg);
        setError(msg);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(msg);
        });
      } finally {
        setIsConfirmingLot(false);
      }
    },
    [
      clearLotConfirmationState,
      expectedLotData,
      loadLineDetailV2,
      refreshReleasedTopRowsAfterMutation,
      scannedLotData,
      selectedPickOrderId,
      selectedPickOrderLineId,
      selectedTopMeta,
      t,
      userId,
      workbenchScanPickQtyFromLot,
    ],
  );

  const handleLotConfirmationByRescan = useCallback(
    async (rawQr: string): Promise<boolean> => {
      if (!lotConfirmationOpen || !expectedLotData || !scannedLotData) return false;
      const latest = String(rawQr || "").trim();
      if (!latest) return false;

      let parsed: any;
      try {
        parsed = JSON.parse(latest);
      } catch {
        return false;
      }

      const rescannedItemId = toNum(parsed?.itemId);
      const rescannedStockInLineId = toNum(parsed?.stockInLineId);
      if (rescannedItemId <= 0 || rescannedStockInLineId <= 0) return false;

      const expectedItemId = Number(expectedLotData.row.itemId || 0);
      if (expectedItemId > 0 && rescannedItemId !== expectedItemId) return false;

      const expectedStockInLineId = Number(expectedLotData.stockInLineId || expectedLotData.row.stockInLineId || 0);
      const scannedStockInLineId = Number(scannedLotData.stockInLineId || scannedLotData.row.stockInLineId || 0);

      if (expectedStockInLineId > 0 && rescannedStockInLineId === expectedStockInLineId) {
        clearLotConfirmationState(false);
        await submitRow(expectedLotData.row);
        return true;
      }

      if (scannedStockInLineId > 0 && rescannedStockInLineId === scannedStockInLineId) {
        await handleLotConfirmation();
        return true;
      }

      const itemRows = lotRowIndexes.byItemId.get(rescannedItemId) || [];
      const rowByStockInLineId = itemRows.find(
        (r) =>
          Number(r.stockInLineId) === rescannedStockInLineId &&
          r.stockOutLineId > 0 &&
          !isCompletedStatus(r.status) &&
          !isCheckedStatus(r.status),
      );

      if (rowByStockInLineId) {
        await handleLotConfirmation(toConfirmLotState(rowByStockInLineId));
        return true;
      }

      try {
        const info = await fetchStockInLineInfo(rescannedStockInLineId);
        const rescannedLotNo = String(info?.lotNo || "").trim();
        if (!rescannedLotNo) return false;
        await handleLotConfirmation(
          toConfirmLotStateWithOverrides(expectedLotData.row, {
            lotNo: rescannedLotNo,
            stockInLineId: rescannedStockInLineId,
          }),
        );
      } catch {
        return false;
      }
      return true;
    },
    [
      clearLotConfirmationState,
      expectedLotData,
      handleLotConfirmation,
      lotConfirmationOpen,
      lotRowIndexes,
      scannedLotData,
      submitRow,
      toConfirmLotState,
      toConfirmLotStateWithOverrides,
    ],
  );

  const processOutsideQrCode = useCallback(
    async (rawQr: string) => {
      const latest = String(rawQr || "").trim();
      if (!latest) return;
      setError("");
      setMessage("");
      startTransition(() => {
        setQrScanError(false);
        setQrScanSuccess(false);
      });

      if (latest === "{2fic}") {
        setManualLotConfirmationOpen(true);
        return;
      }

      if (lotConfirmationOpen) {
        const handled = await handleLotConfirmationByRescan(latest);
        if (handled) return;
      }

      let parsed: any;
      try {
        parsed = JSON.parse(latest);
      } catch {
        setError(t("Invalid QR format. Expected JSON with itemId and stockInLineId."));
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(t("Invalid QR format. Expected JSON with itemId and stockInLineId."));
        });
        resetScan();
        return;
      }

      const scannedItemId = toNum(parsed?.itemId);
      const scannedStockInLineId = toNum(parsed?.stockInLineId);
      if (scannedItemId <= 0 || scannedStockInLineId <= 0) {
        setError(t("Invalid QR data. itemId and stockInLineId are required."));
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(t("Invalid QR data. itemId and stockInLineId are required."));
        });
        resetScan();
        return;
      }

      const activeSuggestedLots = lotRowIndexes.activeLotsByItemId.get(scannedItemId) || [];
      const allLotsForItem = lotRowIndexes.byItemId.get(scannedItemId) || [];
      if (allLotsForItem.length === 0) {
        setError(t("Scanned item is not found in current line"));
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(t("Scanned item is not found in current line"));
        });
        resetScan();
        return;
      }

      const expectedPool = activeSuggestedLots.length > 0 ? activeSuggestedLots : allLotsForItem;
      const expectedRow = pickExpectedRowForSubstitution(expectedPool) || allLotsForItem[0];

      const scannedRows = lotRowIndexes.byStockInLineId.get(scannedStockInLineId) || [];
      const scannedRowInItem =
        scannedRows.find(
          (r) =>
            Number(r.itemId) === scannedItemId &&
            r.stockOutLineId > 0,
        ) ||
        null;

      if (scannedRowInItem && isRejectedStatus(scannedRowInItem.status)) {
        const rejectMsg =
          scannedRowInItem.stockOutLineRejectMessage ||
          t("This lot is rejected. Please scan another lot.");
        setError(rejectMsg);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(rejectMsg);
        });
        return;
      }

      if (scannedRowInItem && isInventoryLotLineUnavailable(scannedRowInItem)) {
        startTransition(() => {
          setQrScanError(false);
          setQrScanSuccess(false);
        });
        setMessage(t("This lot is unavailable, please scan another lot."));
        openWorkbenchLotLabelModalForLot(scannedRowInItem);
        return;
      }

      if (scannedRowInItem && isLotExpired(scannedRowInItem)) {
        const expiredMsg = t("Lot is expired");
        setError(expiredMsg);
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(
            scannedRowInItem.expiryDate
              ? `${expiredMsg} (expiry=${scannedRowInItem.expiryDate})`
              : expiredMsg,
          );
        });
        openWorkbenchLotLabelModalForLot(scannedRowInItem);
        return;
      }

      if (scannedRowInItem && (isCompletedStatus(scannedRowInItem.status) || isCheckedStatus(scannedRowInItem.status))) {
        setError(t("Scanned lot is already completed or checked"));
        startTransition(() => {
          setQrScanError(true);
          setQrScanSuccess(false);
          setQrScanErrorMsg(t("Scanned lot is already completed or checked"));
        });
        return;
      }

      let scannedState: ConfirmLotState | null = null;
      if (scannedRowInItem) {
        scannedState = toConfirmLotState(scannedRowInItem);
      } else {
        try {
          const info = await fetchStockInLineInfo(scannedStockInLineId);
          const scannedLotNo = String(info?.lotNo || "").trim();
          if (!scannedLotNo) {
            setError(t("Scanned lot is not found for current item"));
            startTransition(() => {
              setQrScanError(true);
              setQrScanSuccess(false);
              setQrScanErrorMsg(t("Scanned lot is not found for current item"));
            });
            resetScan();
            return;
          }
          scannedState = toConfirmLotStateWithOverrides(expectedRow, {
            lotNo: scannedLotNo,
            stockInLineId: scannedStockInLineId,
          });
        } catch {
          setError(t("Scanned lot is not found for current item"));
          startTransition(() => {
            setQrScanError(true);
            setQrScanSuccess(false);
            setQrScanErrorMsg(t("Scanned lot is not found for current item"));
          });
          resetScan();
          return;
        }
      }

      if (
        Number(expectedRow.stockInLineId) > 0 &&
        Number(scannedState.stockInLineId) > 0 &&
        Number(expectedRow.stockInLineId) === Number(scannedState.stockInLineId)
      ) {
        await submitRow(expectedRow);
        return;
      }

      await handleLotConfirmation(scannedState, toConfirmLotState(expectedRow));
    },
    [
      handleLotConfirmation,
      handleLotConfirmationByRescan,
      lotConfirmationOpen,
      pickExpectedRowForSubstitution,
      lotRowIndexes,
      resetScan,
      submitRow,
      t,
      toConfirmLotState,
      toConfirmLotStateWithOverrides,
    ],
  );

  useEffect(() => {
    if (!userId) return;
    if (!isScanning) startScan();
  }, [isScanning, startScan, userId]);

  useEffect(() => {
    if (!selectedPickOrderLineId) {
      lastProcessedQrRef.current = "";
      processedQrCodesRef.current.clear();
    }
  }, [selectedPickOrderLineId]);

  useEffect(() => {
    if (!qrValues.length || lotRows.length === 0) return;
    const latest = String(qrValues[qrValues.length - 1] || "");
    if (!latest) return;

    if (lotConfirmationOpen) {
      if (isConfirmingLot) return;
      if (lotConfirmSkipNextScanRef.current) {
        lotConfirmSkipNextScanRef.current = false;
        lotConfirmLastQrRef.current = latest;
        return;
      }
      const sameQr = latest === lotConfirmLastQrRef.current;
      const justOpened =
        lotConfirmOpenedAtRef.current > 0 &&
        Date.now() - lotConfirmOpenedAtRef.current < 800;
      if (sameQr && justOpened) return;
      lotConfirmLastQrRef.current = latest;
      void (async () => {
        try {
          const handled = await handleLotConfirmationByRescan(latest);
          if (handled) resetScan();
        } catch (e) {
          console.error("Lot confirmation rescan failed:", e);
        }
      })();
      return;
    }

    if (latest === lastProcessedQrRef.current || processedQrCodesRef.current.has(latest)) return;
    lastProcessedQrRef.current = latest;
    processedQrCodesRef.current.add(latest);
    if (processedQrCodesRef.current.size > 100) {
      const firstValue = processedQrCodesRef.current.values().next().value;
      if (firstValue !== undefined) processedQrCodesRef.current.delete(firstValue);
    }

    const run = async () => {
      try {
        // JO shortcut: {2fitestx,y} -> simulate JSON qr
        if (
          (latest.startsWith("{2fitest") || latest.startsWith("{2fittest")) &&
          latest.endsWith("}")
        ) {
          let content = "";
          if (latest.startsWith("{2fittest")) content = latest.substring(9, latest.length - 1);
          else content = latest.substring(8, latest.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 (!Number.isNaN(itemId) && !Number.isNaN(stockInLineId)) {
              await processOutsideQrCode(JSON.stringify({ itemId, stockInLineId }));
              return;
            }
          }
        }
        await processOutsideQrCode(latest);
      } finally {
        resetScan();
      }
    };
    void run();
  }, [
    handleLotConfirmationByRescan,
    isConfirmingLot,
    lotConfirmationOpen,
    lotRows.length,
    processOutsideQrCode,
    qrValues,
    resetScan,
  ]);

  return (
    <TestQrCodeProvider
      lotData={lotRows}
      onScanLot={handleScanLotByLotNo}
      filterActive={(lot) =>
        lot.stockOutLineId > 0 &&
        !isCompletedStatus(lot.status) &&
        !isCheckedStatus(lot.status) &&
        String(lot.lotNo || "").trim() !== ""
      }
    >
      <Stack spacing={2}>
      <Paper variant="outlined" sx={{ p: 2 }}>
        <Stack spacing={1}>
          <SearchBox criteria={searchCriteria} onSearch={handleSearch} onReset={handleReset} />
          <Grid container rowGap={1}>
            <Grid item xs={12}>
              {pickOrderLoading ? (
                <CircularProgress size={40} />
              ) : (
                <TableContainer component={Paper}>
                  <Table size="small">
                    <TableHead>
                      <TableRow>
                        <TableCell>{t("Selected")}</TableCell>
                        <TableCell>{t("Pick Order Code")}</TableCell>
                        <TableCell>{t("Item Code")}</TableCell>
                        <TableCell>{t("Item Name")}</TableCell>
                        <TableCell align="right">{t("Order Quantity")}</TableCell>
                        <TableCell align="right">{t("Current Stock")}</TableCell>
                        <TableCell align="right">{t("Picked Qty")}</TableCell>
                        <TableCell>{t("Stock Unit")}</TableCell>
                        <TableCell>{t("Target Date")}</TableCell>
                        <TableCell>{t("Status")}</TableCell>
                      </TableRow>
                    </TableHead>
                    <TableBody>
                      {paginatedTopRows.length === 0 ? (
                        <TableRow>
                          <TableCell colSpan={10}>
                            <Typography variant="body2" color="text.secondary">
                              {t("No data available")}
                            </Typography>
                          </TableCell>
                        </TableRow>
                      ) : (
                        paginatedTopRows.map((row) => (
                          <TableRow key={row.rowKey}>
                            <TableCell padding="checkbox">
                              <Checkbox
                                checked={selectedPickOrderLineId === row.pickOrderLineId}
                                onChange={(_, checked) => void handleLineSelect(row, checked)}
                              />
                            </TableCell>
                            <TableCell>{row.pickOrderCode || "-"}</TableCell>
                            <TableCell>{row.itemCode || "-"}</TableCell>
                            <TableCell>{row.itemName || "-"}</TableCell>
                            <TableCell align="right">{row.requiredQty.toLocaleString()}</TableCell>
                            <TableCell align="right">{row.currentStock.toLocaleString()}</TableCell>
                            <TableCell align="right">{row.pickedQty.toLocaleString()}</TableCell>
                            <TableCell>{row.stockUnit || "-"}</TableCell>
                            <TableCell>{safeDisplayTargetDate(row.targetDate)}</TableCell>
                            <TableCell>{t(row.status || "-")}</TableCell>
                          </TableRow>
                        ))
                      )}
                    </TableBody>
                  </Table>
                </TableContainer>
              )}
            </Grid>
            <Grid item xs={12}>
              <TablePagination
                component="div"
                count={totalCountItems}
                page={pagingController.pageNum - 1}
                rowsPerPage={pagingController.pageSize}
                onPageChange={(_e, newPage) => setPagingController((prev) => ({ ...prev, pageNum: newPage + 1 }))}
                onRowsPerPageChange={(e) =>
                  setPagingController({
                    pageNum: 1,
                    pageSize: parseInt(e.target.value, 10),
                  })
                }
                rowsPerPageOptions={[10, 25, 50, 100]}
                labelRowsPerPage={t("Rows per page")}
              />
            </Grid>
          </Grid>
        </Stack>
      </Paper>

      {loading ? (
        <Stack direction="row" alignItems="center" spacing={1}>
          <CircularProgress size={24} />
          <Typography variant="body2">{t("Loading")}</Typography>
        </Stack>
      ) : null}
      <ScanStatusAlert
        error={qrScanError}
        success={qrScanSuccess}
        errorMessage={qrScanErrorMsg || t("QR code does not match any item in current orders.")}
        successMessage={qrScanSuccessMsg || t("QR code verified.")}
      />
      {error ? <Alert severity="error">{error}</Alert> : null}
      {message ? <Alert severity="success">{message}</Alert> : null}

      <Paper variant="outlined">
        <TableContainer>
          <Table size="small">
            <TableHead>
              <TableRow>
                <TableCell>{t("Index")}</TableCell>
                <TableCell>{t("Item Code")}</TableCell>
                <TableCell>{t("Route")}</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>
              {paginatedLotRows.map((r, idx) => (
                <TableRow key={r.key}>
                  <TableCell>{idx === 0 ? lotPagingController.pageNum * lotPagingController.pageSize + 1 : ""}</TableCell>
                  <TableCell>
                    {idx === 0 ? (
                      <>
                        {r.itemCode || "-"} <br />
                        {r.itemName || "-"} <br />
                        {r.uomDesc || "-"}
                      </>
                    ) : (
                      ""
                    )}
                  </TableCell>
                  <TableCell>{r.location || "-"}</TableCell>
                  <TableCell>
                    <Stack direction="row" spacing={1} alignItems="center" justifyContent="space-between">
                      <Typography variant="body2">{r.lotNo || "-"}</Typography>
                      {r.stockOutLineId > 0 ? (
                        <Button
                          variant="outlined"
                          size="small"
                          onClick={() => openWorkbenchLotLabelModalForLot(r)}
                          sx={{ flexShrink: 0, fontSize: "0.7rem", py: 0.25, minWidth: "auto", px: 1, whiteSpace: "nowrap" }}
                        >
                          {t("挑號 QR 碼")}
                        </Button>
                      ) : null}
                    </Stack>
                  </TableCell>
                  <TableCell align="right">{`${r.requiredQty.toLocaleString()}(${r.uomDesc || ""})`}</TableCell>
                  <TableCell align="right">
                    {`${Number(
                      r.itemTotalAvailableQty ?? r.availableQty ?? 0,
                    ).toLocaleString()}(${r.uomDesc || ""})`}
                  </TableCell>
                  <TableCell align="center">
                    <Checkbox
                      checked={isCompletedStatus(r.status) || isCheckedStatus(r.status)}
                      disabled
                      size="small"
                      sx={{
                        color: isCompletedStatus(r.status) ? "success.main" : isCheckedStatus(r.status) ? "warning.main" : "action.disabled",
                        "&.Mui-checked": {
                          color: isCompletedStatus(r.status) ? "success.main" : isCheckedStatus(r.status) ? "warning.main" : "action.disabled",
                        },
                      }}
                    />
                  </TableCell>
                  <TableCell align="center">
                    <Stack direction="row" spacing={1} justifyContent="center" alignItems="center">
                      <TextField
                        size="small"
                        type="number"
                        value={qtyBySolId[r.stockOutLineId] ?? Number(r.requiredQty)}
                        onKeyDown={(e) => {
                          const editable = qtyEditableBySolId[r.stockOutLineId] === true;
                          if (!editable) return;
                          if (e.key !== "{") return;
                          e.preventDefault();
                          setQtyEditableBySolId((prev) => ({
                            ...prev,
                            [r.stockOutLineId]: false,
                          }));
                          (e.currentTarget as HTMLInputElement).blur();
                        }}
                        onChange={(e) => {
                          const v = e.target.value;
                          setQtyBySolId((prev) => {
                            if (v === "" || v == null) {
                              if (!Object.prototype.hasOwnProperty.call(prev, r.stockOutLineId)) return prev;
                              const next = { ...prev };
                              delete next[r.stockOutLineId];
                              return next;
                            }
                            const n = Number(v);
                            if (!Number.isFinite(n) || n < 0) {
                              if (!Object.prototype.hasOwnProperty.call(prev, r.stockOutLineId)) return prev;
                              const next = { ...prev };
                              delete next[r.stockOutLineId];
                              return next;
                            }
                            return { ...prev, [r.stockOutLineId]: n };
                          });
                        }}
                        sx={{ width: 96 }}
                        disabled={!r.stockOutLineId || qtyEditableBySolId[r.stockOutLineId] !== true}
                        inputProps={{ min: 0, step: 1 }}
                      />
                      <Button
                        variant="outlined"
                        size="small"
                        onClick={() =>
                          setQtyEditableBySolId((prev) => ({
                            ...prev,
                            [r.stockOutLineId]: !(prev[r.stockOutLineId] === true),
                          }))
                        }
                        disabled={!r.stockOutLineId || isCompletedStatus(r.status)}
                        sx={{ fontSize: "0.7rem", py: 0.5, minHeight: "28px", minWidth: "60px", borderColor: "warning.main", color: "warning.main" }}
                      >
                        {t("Edit")}
                      </Button>
                    </Stack>
                  </TableCell>
                  <TableCell align="center">
                    <Stack direction="row" spacing={1} justifyContent="center">
                      <Button
                        size="small"
                        variant="outlined"
                        disabled={!r.stockOutLineId || isCompletedStatus(r.status) || isCheckedStatus(r.status)}
                        onClick={() => void handleJustComplete(r)}
                      >
                        {t("Just Complete")}
                      </Button>
                    </Stack>
                  </TableCell>
                </TableRow>
              ))}
              {lotRows.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={9}>
                    <Typography variant="body2" color="text.secondary">
                      {t("No lot rows. Select a line in the table above.")}
                    </Typography>
                  </TableCell>
                </TableRow>
              ) : null}
            </TableBody>
          </Table>
        </TableContainer>
        <TablePagination
          component="div"
          count={lotRows.length}
          page={lotPagingController.pageNum}
          rowsPerPage={lotPagingController.pageSize}
          onPageChange={(_e, newPage) => setLotPagingController((prev) => ({ ...prev, pageNum: newPage }))}
          onRowsPerPageChange={(e) =>
            setLotPagingController({
              pageNum: 0,
              pageSize: parseInt(e.target.value, 10),
            })
          }
          rowsPerPageOptions={[10, 25, 50]}
          labelRowsPerPage={t("Rows per page")}
        />
      </Paper>
      <WorkbenchLotLabelPrintModal
        open={workbenchLotLabelModalOpen}
        onClose={() => {
          setWorkbenchLotLabelModalOpen(false);
          setWorkbenchLotLabelContextLot(null);
          setWorkbenchLotLabelInitialPayload(null);
        }}
        initialPayload={workbenchLotLabelInitialPayload}
        initialItemId={workbenchLotLabelContextLot?.itemId ?? null}
        hideScanSection={workbenchLotLabelInitialPayload != null || workbenchLotLabelContextLot != null}
        triggerLotAvailableQty={workbenchLotLabelContextLot?.availableQty ?? null}
        triggerLotUom={workbenchLotLabelContextLot?.uomDesc ?? null}
        submitQty={
          workbenchLotLabelContextLot?.stockOutLineId
            ? Number(resolveSingleSubmitQty(workbenchLotLabelContextLot))
            : null
        }
        onSubmitQtyChange={(qty) => {
          const solId = Number(workbenchLotLabelContextLot?.stockOutLineId);
          if (!Number.isFinite(solId) || solId <= 0) return;
          if (!Number.isFinite(qty) || qty < 0) {
            setQtyBySolId((prev) => {
              if (!Object.prototype.hasOwnProperty.call(prev, solId)) return prev;
              const next = { ...prev };
              delete next[solId];
              return next;
            });
            return;
          }
          setQtyBySolId((prev) => ({ ...prev, [solId]: qty }));
        }}
        onWorkbenchScanPick={handleWorkbenchLotLabelScanPick}
      />
      <ManualLotConfirmationModal
        open={manualLotConfirmationOpen}
        onClose={() => setManualLotConfirmationOpen(false)}
        onConfirm={(expectedLotNo, scannedLotNo) => {
          const expected = resolveScanCandidate(expectedLotNo);
          const scanned = resolveScanCandidate(scannedLotNo);
          if (!expected || !scanned) {
            setError(t("Lot not found in current line"));
            return;
          }
          setManualLotConfirmationOpen(false);
          void handleLotConfirmation(scanned, expected);
        }}
        expectedLot={
          expectedLotData
            ? {
                lotNo: expectedLotData.lotNo,
                itemCode: expectedLotData.itemCode,
                itemName: expectedLotData.itemName,
              }
            : null
        }
        scannedLot={
          scannedLotData
            ? {
                lotNo: scannedLotData.lotNo,
                itemCode: scannedLotData.itemCode,
                itemName: scannedLotData.itemName,
              }
            : null
        }
        isLoading={isConfirmingLot}
      />
      </Stack>
    </TestQrCodeProvider>
  );
};

export default WorkbenchPickExecution;
