"use client";

import React, { useState, useEffect, useMemo, useRef } from "react";
import Search from "@mui/icons-material/Search";
import Visibility from "@mui/icons-material/Visibility";
import FormatListNumbered from "@mui/icons-material/FormatListNumbered";
import ShowChart from "@mui/icons-material/ShowChart";
import Download from "@mui/icons-material/Download";
import Hub from "@mui/icons-material/Hub";
import Settings from "@mui/icons-material/Settings";
import Clear from "@mui/icons-material/Clear";
import Refresh from "@mui/icons-material/Refresh";
import { CircularProgress } from "@mui/material";
import PageTitleBar from "@/components/PageTitleBar";
import dayjs from "dayjs";
import { NEXT_PUBLIC_API_URL } from "@/config/api";
import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
import { exportChartToXlsx } from "@/app/(main)/chart/_components/exportChartToXlsx";
import * as XLSX from "xlsx";

type ItemDailyOutRow = {
  itemCode: string;
  itemName: string;
  unit?: string;
  onHandQty?: number | null;
  fakeOnHandQty?: number | null;
  avgQtyLastMonth?: number;
  dailyQty?: number | null;
  isCoffee?: number;
  isTea?: number;
  isLemon?: number;
};

export default function ProductionSchedulePage() {
  const [searchDate, setSearchDate] = useState(dayjs().format("YYYY-MM-DD"));
  const [schedules, setSchedules] = useState<any[]>([]);
  const [selectedLines, setSelectedLines] = useState<any[]>([]);
  const [isDetailOpen, setIsDetailOpen] = useState(false);
  const [selectedPs, setSelectedPs] = useState<any>(null);
  const [loading, setLoading] = useState(false);
  const [isGenerating, setIsGenerating] = useState(false);

  const [isForecastDialogOpen, setIsForecastDialogOpen] = useState(false);
  const [forecastStartDate, setForecastStartDate] = useState(
    dayjs().format("YYYY-MM-DD")
  );
  const [forecastDays, setForecastDays] = useState<number | "">(7);

  const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
  const [exportFromDate, setExportFromDate] = useState(
    dayjs().format("YYYY-MM-DD")
  );

  const [isDoQtyExportDialogOpen, setIsDoQtyExportDialogOpen] = useState(false);
  const [doQtyExportFromDate, setDoQtyExportFromDate] = useState(
    dayjs().subtract(6, "day").format("YYYY-MM-DD")
  );
  const [doQtyExportToDate, setDoQtyExportToDate] = useState(
    dayjs().format("YYYY-MM-DD")
  );
  const [isDoQtyExporting, setIsDoQtyExporting] = useState(false);
  const doQtyExportInFlightRef = useRef(false);

  const [isDailyOutPanelOpen, setIsDailyOutPanelOpen] = useState(false);
  const [itemDailyOutList, setItemDailyOutList] = useState<ItemDailyOutRow[]>([]);
  const [itemDailyOutLoading, setItemDailyOutLoading] = useState(false);
  const [dailyOutSavingCode, setDailyOutSavingCode] = useState<string | null>(null);
  const [dailyOutClearingCode, setDailyOutClearingCode] = useState<string | null>(null);
  const [coffeeOrTeaUpdating, setCoffeeOrTeaUpdating] = useState<string | null>(null);
  const [fakeOnHandSavingCode, setFakeOnHandSavingCode] = useState<string | null>(null);
  const [fakeOnHandClearingCode, setFakeOnHandClearingCode] = useState<string | null>(null);
  const [isImportingFakeOnHand, setIsImportingFakeOnHand] = useState(false);
  const [isRefreshingOnHand, setIsRefreshingOnHand] = useState(false);
  const refreshOnHandInFlightRef = useRef(false);
  const itemDailyOutRequestRef = useRef(0);

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

  const formatBackendDate = (dateVal: any) => {
    if (Array.isArray(dateVal)) {
      const [year, month, day] = dateVal;
      return dayjs(new Date(year, month - 1, day)).format("DD MMM (dddd)");
    }
    return dayjs(dateVal).format("DD MMM (dddd)");
  };

  const formatNum = (num: any) => {
    return new Intl.NumberFormat("en-US").format(Number(num) || 0);
  };

  const handleSearch = async () => {
    setLoading(true);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/search-ps?produceAt=${searchDate}`,
        { method: "GET" }
      );
      if (response.status === 401 || response.status === 403) return;
      const data = await response.json();
      setSchedules(Array.isArray(data) ? data : []);
    } catch (e) {
      console.error("Search Error:", e);
    } finally {
      setLoading(false);
    }
  };

  const handleConfirmForecast = async () => {
    if (!forecastStartDate || forecastDays === "" || forecastDays < 1) {
      alert("Please enter a valid start date and number of days (≥1).");
      return;
    }
    setLoading(true);
    setIsForecastDialogOpen(false);
    try {
      const params = new URLSearchParams({
        startDate: forecastStartDate,
        days: forecastDays.toString(),
      });
      const url = `${NEXT_PUBLIC_API_URL}/productionSchedule/testDetailedSchedule?${params.toString()}`;
      const response = await clientAuthFetch(url, { method: "GET" });
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        await handleSearch();
        alert("成功計算排期!");
      } else {
        const errorText = await response.text();
        console.error("Forecast failed:", errorText);
        alert(`計算錯誤: ${response.status} - ${errorText.substring(0, 120)}`);
      }
    } catch (e) {
      console.error("Forecast Error:", e);
      alert("發生不明狀況.");
    } finally {
      setLoading(false);
    }
  };

  const handleConfirmDoQtyExport = async () => {
    if (doQtyExportInFlightRef.current) return;
    if (!doQtyExportFromDate || !doQtyExportToDate) {
      alert("請選擇開始及結束日期。");
      return;
    }
    if (dayjs(doQtyExportFromDate).isAfter(dayjs(doQtyExportToDate))) {
      alert("開始日期不可晚於結束日期。");
      return;
    }
    doQtyExportInFlightRef.current = true;
    setIsDoQtyExporting(true);
    setIsDoQtyExportDialogOpen(false);
    try {
      const params = new URLSearchParams({
        fromDate: doQtyExportFromDate,
        toDate: doQtyExportToDate,
      });
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/export-do-qty-by-date?${params.toString()}`,
        { method: "GET" }
      );
      if (response.status === 401 || response.status === 403) return;
      if (!response.ok) {
        const errText = await response.text().catch(() => "");
        throw new Error(errText || `Export failed: ${response.status}`);
      }
      const blob = await response.blob();
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `do_qty_${doQtyExportFromDate.replace(/-/g, "")}_${doQtyExportToDate.replace(/-/g, "")}.xlsx`;
      document.body.appendChild(a);
      a.click();
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    } catch (e) {
      console.error("DO qty export Error:", e);
      alert("匯出送貨單數量失敗。");
    } finally {
      setIsDoQtyExporting(false);
      doQtyExportInFlightRef.current = false;
    }
  };

  const handleConfirmExport = async () => {
    if (!exportFromDate) {
      alert("Please select a from date.");
      return;
    }
    setLoading(true);
    setIsExportDialogOpen(false);
    try {
      const params = new URLSearchParams({ fromDate: exportFromDate });
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/productionSchedule/export-prod-schedule?${params.toString()}`,
        { method: "GET" }
      );
      if (response.status === 401 || response.status === 403) return;
      if (!response.ok) throw new Error(`Export failed: ${response.status}`);
      const blob = await response.blob();
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `production_schedule_from_${exportFromDate.replace(/-/g, "")}.xlsx`;
      document.body.appendChild(a);
      a.click();
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    } catch (e) {
      console.error("Export Error:", e);
      alert("Failed to export file.");
    } finally {
      setLoading(false);
    }
  };

  const handleViewDetail = async (ps: any) => {
    if (!ps?.id) {
      alert("Cannot open details: missing schedule ID");
      return;
    }
    setSelectedPs(ps);
    setLoading(true);
    try {
      const url = `${NEXT_PUBLIC_API_URL}/ps/search-ps-line?psId=${ps.id}`;
      const response = await clientAuthFetch(url, { method: "GET" });
      if (response.status === 401 || response.status === 403) return;
      if (!response.ok) {
        const errorText = await response.text().catch(() => "(no text)");
        alert(`Server error ${response.status}: ${errorText}`);
        return;
      }
      const data = await response.json();
      setSelectedLines(Array.isArray(data) ? data : []);
      setIsDetailOpen(true);
    } catch (err) {
      console.error("Fetch failed:", err);
      alert("Network or fetch error – check console");
    } finally {
      setLoading(false);
    }
  };

  const handleAutoGenJob = async () => {
    setIsGenerating(true);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/productionSchedule/detail/detailed/release`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ id: selectedPs.id }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        const data = await response.json();
        alert(data.message || "Operation completed.");
        setIsDetailOpen(false);
      } else {
        alert("Failed to generate jobs.");
      }
    } catch (e) {
      console.error("Release Error:", e);
    } finally {
      setIsGenerating(false);
    }
  };

  const fromDateDefault = dayjs().subtract(6, "day").format("YYYY-MM-DD");
  const toDateDefault = dayjs().add(1, "day").format("YYYY-MM-DD");

  const fetchItemDailyOut = async (force: boolean = false) => {
    // Avoid starting a new fetch while an import is in progress,
    // unless explicitly forced (after a successful import).
    if (!force && isImportingFakeOnHand) return;

    const currentReq = itemDailyOutRequestRef.current + 1;
    itemDailyOutRequestRef.current = currentReq;
    setItemDailyOutLoading(true);
    try {
      const params = new URLSearchParams({
        fromDate: fromDateDefault,
        toDate: toDateDefault,
      });
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/itemDailyOut.json?${params.toString()}`,
        { method: "GET" }
      );
      if (response.status === 401 || response.status === 403) return;
      const data = await response.json();
      // If a newer request has started, ignore this response to avoid overwriting with stale data
      if (itemDailyOutRequestRef.current !== currentReq) {
        return;
      }
      const rows: ItemDailyOutRow[] = (Array.isArray(data) ? data : []).map(
        (r: any) => ({
          itemCode: r.itemCode ?? "",
          itemName: r.itemName ?? "",
          unit: r.unit != null ? String(r.unit) : "",
          onHandQty: r.onHandQty != null ? Number(r.onHandQty) : null,
          fakeOnHandQty:
            r.fakeOnHandQty != null && r.fakeOnHandQty !== ""
              ? Number(r.fakeOnHandQty)
              : null,
          avgQtyLastMonth:
            r.avgQtyLastMonth != null ? Number(r.avgQtyLastMonth) : undefined,
          dailyQty:
            r.dailyQty != null && r.dailyQty !== ""
              ? Number(r.dailyQty)
              : null,
          isCoffee: r.isCoffee != null ? Number(r.isCoffee) : 0,
          isTea: r.isTea != null ? Number(r.isTea) : 0,
          isLemon: r.isLemon != null ? Number(r.isLemon) : 0,
        })
      );
      setItemDailyOutList(rows);
    } catch (e) {
      console.error("itemDailyOut Error:", e);
      setItemDailyOutList([]);
    } finally {
      // Only clear loading state if this is the latest request
      if (itemDailyOutRequestRef.current === currentReq) {
        setItemDailyOutLoading(false);
      }
    }
  };

  const openSettingsPanel = () => {
    setIsDailyOutPanelOpen(true);
    fetchItemDailyOut();
  };

  const handleRefreshInventoryOnHand = async () => {
    if (refreshOnHandInFlightRef.current) return;
    refreshOnHandInFlightRef.current = true;
    setIsRefreshingOnHand(true);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/refresh-inventory-onhand`,
        { method: "POST" }
      );
      if (response.status === 401 || response.status === 403) return;
      if (!response.ok) {
        const msg = await response.text().catch(() => "");
        alert(`刷新庫存失敗（${response.status}）${msg ? `: ${msg.slice(0, 120)}` : ""}`);
        return;
      }
      await response.json().catch(() => null);
      await fetchItemDailyOut(true);
    } catch (e) {
      console.error("refreshInventoryOnHand Error:", e);
      alert("刷新庫存失敗。");
    } finally {
      setIsRefreshingOnHand(false);
      refreshOnHandInFlightRef.current = false;
    }
  };

  /** Download current fake on-hand overrides (item_fake_onhand) as Excel template. */
  const handleExportFakeOnHand = () => {
    const rows = itemDailyOutList
      .filter((row) => row.fakeOnHandQty != null)
      .map((row) => ({
        itemCode: row.itemCode,
        onHandQty: row.fakeOnHandQty,
      }));
    exportChartToXlsx(rows, "item_fake_onhand", "item_fake_onhand");
  };

  /** Upload Excel and bulk update item_fake_onhand via /ps/setFakeOnHand. */
  const handleImportFakeOnHand = async (file: File) => {
    try {
      setIsImportingFakeOnHand(true);

      const data = await file.arrayBuffer();
      const workbook = XLSX.read(data, { type: "array" });
      const sheetName = workbook.SheetNames[0];
      if (!sheetName) {
        alert("Excel 沒有工作表。");
        return;
      }
      const sheet = workbook.Sheets[sheetName];
      const rows: any[] = XLSX.utils.sheet_to_json(sheet, { defval: null });
      if (!rows.length) {
        alert("Excel 內容為空。");
        return;
      }

      // Build allowed itemCodes (BOM scope) from current list
      const allowedCodes = new Set(itemDailyOutList.map((r) => r.itemCode));

      const invalidCodes: string[] = [];

      // Map Excel rows to backend payload format
      const payload = rows
        .map((row) => {
          const itemCode = (row.itemCode ?? row.ItemCode ?? row["Item Code"])?.toString().trim();
          if (!itemCode) return null;
          if (!allowedCodes.has(itemCode)) {
            invalidCodes.push(itemCode);
          }
          const rawQty = row.onHandQty ?? row.OnHandQty ?? row["On Hand Qty"];
          const qtyNum =
            rawQty === null || rawQty === "" || typeof rawQty === "undefined"
              ? null
              : Number(rawQty);
          return { itemCode, onHandQty: qtyNum };
        })
        .filter((r): r is { itemCode: string; onHandQty: number | null } => r !== null);

      if (!payload.length) {
        alert("找不到任何有效的 itemCode。");
        return;
      }

      // Warn user about itemCodes that are not in BOM scope (won't affect forecast)
      if (invalidCodes.length) {
        const preview = invalidCodes.slice(0, 10).join(", ");
        alert(
          `注意：以下物料編號不在排期 BOM 範圍內，預測不會受影響，只會寫入覆蓋表。\n\n` +
            `${preview}${invalidCodes.length > 10 ? ` 等共 ${invalidCodes.length} 筆` : ""}`
        );
      }

      const resp = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/ps/importFakeOnHand`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });

      if (resp.status === 401 || resp.status === 403) {
        alert("登入已過期或沒有權限，請重新登入後再試。");
        return;
      }

      if (!resp.ok) {
        const msg = await resp.text().catch(() => "");
        alert(
          `匯入失敗（狀態碼 ${resp.status}）。${
            msg ? `\n伺服器訊息：${msg.slice(0, 120)}` : ""
          }`
        );
        return;
      }

      const result = await resp.json().catch(() => ({ count: payload.length }));

      // Backend clears item_fake_onhand then inserts payload rows,
      // so after success the table should exactly match the uploaded Excel.
      await fetchItemDailyOut(true);
      alert(`已成功匯入並更新 ${result.count ?? payload.length} 筆排期庫存 (item_fake_onhand)。`);
    } catch (e) {
      console.error("Import fake on hand error:", e);
      alert("匯入失敗，請檢查檔案格式。");
    } finally {
      setIsImportingFakeOnHand(false);
    }
  };

  const handleSaveDailyQty = async (itemCode: string, dailyQty: number) => {
    setDailyOutSavingCode(itemCode);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/setDailyQtyOut`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ itemCode, dailyQty }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        setItemDailyOutList((prev) =>
          prev.map((r) =>
            r.itemCode === itemCode ? { ...r, dailyQty } : r
          )
        );
      } else {
        alert("儲存失敗");
      }
    } catch (e) {
      console.error("setDailyQtyOut Error:", e);
      alert("儲存失敗");
    } finally {
      setDailyOutSavingCode(null);
    }
  };

  const handleClearDailyQty = async (itemCode: string) => {
    if (!confirm(`確定要清除${itemCode}的設定排期每天出貨量嗎？`)) return;
    setDailyOutClearingCode(itemCode);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/clearDailyQtyOut`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ itemCode }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        setItemDailyOutList((prev) =>
          prev.map((r) =>
            r.itemCode === itemCode ? { ...r, dailyQty: null } : r
          )
        );
      } else {
        alert("清除失敗");
      }
    } catch (e) {
      console.error("clearDailyQtyOut Error:", e);
      alert("清除失敗");
    } finally {
      setDailyOutClearingCode(null);
    }
  };

  const handleSetCoffeeOrTea = async (
    itemCode: string,
    systemType: "coffee" | "tea" | "lemon",
    enabled: boolean
  ) => {
    const key = `${itemCode}-${systemType}`;
    setCoffeeOrTeaUpdating(key);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/setCoffeeOrTea`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ itemCode, systemType, enabled }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        setItemDailyOutList((prev) =>
          prev.map((r) => {
            if (r.itemCode !== itemCode) return r;
            const next = { ...r };
            if (systemType === "coffee") next.isCoffee = enabled ? 1 : 0;
            if (systemType === "tea") next.isTea = enabled ? 1 : 0;
            if (systemType === "lemon") next.isLemon = enabled ? 1 : 0;
            return next;
          })
        );
      } else {
        alert("設定失敗");
      }
    } catch (e) {
      console.error("setCoffeeOrTea Error:", e);
      alert("設定失敗");
    } finally {
      setCoffeeOrTeaUpdating(null);
    }
  };

  const handleSetFakeOnHand = async (itemCode: string, onHandQty: number) => {
    setFakeOnHandSavingCode(itemCode);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/setFakeOnHand`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ itemCode, onHandQty }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        setItemDailyOutList((prev) =>
          prev.map((r) =>
            r.itemCode === itemCode ? { ...r, fakeOnHandQty: onHandQty } : r
          )
        );
      } else {
        alert("設定失敗");
      }
    } catch (e) {
      console.error("setFakeOnHand Error:", e);
      alert("設定失敗");
    } finally {
      setFakeOnHandSavingCode(null);
    }
  };

  const handleClearFakeOnHand = async (itemCode: string) => {
    if (!confirm("確定要清除此物料的設定排期庫存嗎？")) return;
    setFakeOnHandClearingCode(itemCode);
    try {
      const response = await clientAuthFetch(
        `${NEXT_PUBLIC_API_URL}/ps/setFakeOnHand`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ itemCode, onHandQty: null }),
        }
      );
      if (response.status === 401 || response.status === 403) return;
      if (response.ok) {
        setItemDailyOutList((prev) =>
          prev.map((r) =>
            r.itemCode === itemCode ? { ...r, fakeOnHandQty: null } : r
          )
        );
      } else {
        alert("清除失敗");
      }
    } catch (e) {
      console.error("clearFakeOnHand Error:", e);
      alert("清除失敗");
    } finally {
      setFakeOnHandClearingCode(null);
    }
  };

  return (
    <div className="space-y-4">
      <PageTitleBar
        title="生產排程"
        actions={
          <>
            <button
              type="button"
              onClick={openSettingsPanel}
              className="inline-flex items-center gap-2 rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700"
            >
              <Settings sx={{ fontSize: 16 }} />
              排期設定
            </button>
            <button
              type="button"
              onClick={() => setIsExportDialogOpen(true)}
              className="inline-flex items-center gap-2 rounded-lg border border-emerald-500/70 bg-white px-4 py-2 text-sm font-semibold text-emerald-600 shadow-sm transition hover:bg-emerald-50 dark:border-emerald-500/50 dark:bg-slate-800 dark:text-emerald-400 dark:hover:bg-emerald-500/10"
            >
              <Download sx={{ fontSize: 16 }} />
              匯出計劃/物料需求Excel
            </button>
            <button
              type="button"
              onClick={() => setIsDoQtyExportDialogOpen(true)}
              disabled={isDoQtyExporting}
              className="inline-flex items-center gap-2 rounded-lg border border-violet-500/70 bg-white px-4 py-2 text-sm font-semibold text-violet-600 shadow-sm transition hover:bg-violet-50 disabled:opacity-50 dark:border-violet-500/50 dark:bg-slate-800 dark:text-violet-400 dark:hover:bg-violet-500/10"
            >
              {isDoQtyExporting ? (
                <CircularProgress size={16} sx={{ display: "block" }} />
              ) : (
                <Download sx={{ fontSize: 16 }} />
              )}
              匯出送貨單數量
            </button>
            <button
              type="button"
              onClick={() => setIsForecastDialogOpen(true)}
              disabled={loading}
              className="inline-flex items-center gap-2 rounded-lg bg-blue-500 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-blue-600 disabled:opacity-50"
            >
              {loading ? (
                <CircularProgress size={16} sx={{ display: "block" }} />
              ) : (
                <ShowChart sx={{ fontSize: 16 }} />
              )}
              預測排期
            </button>
          </>
        }
        className="mb-4"
      />

      {/* Query Bar */}
      <div className="app-search-criteria mb-4 flex flex-wrap items-center gap-2 p-4">
        <label className="sr-only" htmlFor="ps-search-date">
          生產日期
        </label>
        <input
          id="ps-search-date"
          type="date"
          value={searchDate}
          onChange={(e) => setSearchDate(e.target.value)}
          className="rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 placeholder-slate-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
        />
        <button
          type="button"
          onClick={handleSearch}
          className="inline-flex items-center gap-2 rounded-lg bg-blue-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-600"
        >
          <Search sx={{ fontSize: 16 }} />
          搜索
        </button>
      </div>

      {/* Main Table */}
      <div className="overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-700 dark:bg-slate-800">
        <div className="overflow-x-auto">
          <table className="w-full min-w-[320px] text-left text-sm">
            <thead className="sticky top-0 bg-slate-50 dark:bg-slate-700">
              <tr>
                <th className="w-[100px] px-4 py-3 text-center font-bold text-slate-700 dark:text-slate-200">
                  詳細
                </th>
                <th className="px-4 py-3 font-bold text-slate-700 dark:text-slate-200">
                  生產日期
                </th>
                <th className="px-4 py-3 text-right font-bold text-slate-700 dark:text-slate-200">
                  預計生產數
                </th>
                <th className="px-4 py-3 text-right font-bold text-slate-700 dark:text-slate-200">
                  成品款數
                </th>
              </tr>
            </thead>
            <tbody>
              {schedules.map((ps) => (
                <tr
                  key={ps.id}
                  className="border-t border-slate-200 text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700/50"
                >
                  <td className="px-4 py-3 text-center">
                    <button
                      type="button"
                      onClick={() => handleViewDetail(ps)}
                      className="rounded p-1 text-blue-500 hover:bg-blue-50 hover:text-blue-600 dark:text-blue-400 dark:hover:bg-blue-500/20"
                    >
                      <Visibility sx={{ fontSize: 16 }} />
                    </button>
                  </td>
                  <td className="px-4 py-3">
                    {formatBackendDate(ps.produceAt)}
                  </td>
                  <td className="px-4 py-3 text-right">
                    {formatNum(ps.totalEstProdCount)}
                  </td>
                  <td className="px-4 py-3 text-right">
                    {formatNum(ps.totalFGType)}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {/* Detail Modal – z-index above sidebar drawer (1200) so they don't overlap on small windows */}
      {isDetailOpen && (
        <div
          className="fixed inset-0 z-[1300] flex items-center justify-center p-4"
          role="dialog"
          aria-modal="true"
          aria-labelledby="detail-title"
        >
          <div
            className="absolute inset-0 bg-black/50"
            onClick={() => !isGenerating && setIsDetailOpen(false)}
          />
          <div className="relative z-10 flex max-h-[90vh] w-full max-w-4xl flex-col overflow-hidden rounded-lg border border-slate-200 bg-white shadow-xl dark:border-slate-700 dark:bg-slate-800">
            <div className="flex items-center gap-2 border-b border-slate-200 bg-blue-500 px-4 py-3 text-white dark:border-slate-700">
              <FormatListNumbered sx={{ fontSize: 20, flexShrink: 0 }} />
              <h2 id="detail-title" className="text-lg font-semibold">
                排期詳細: {selectedPs?.id} (
                {formatBackendDate(selectedPs?.produceAt)})
              </h2>
            </div>
            <div className="max-h-[65vh] overflow-auto">
              <table className="w-full text-left text-sm">
                <thead className="sticky top-0 bg-slate-50 dark:bg-slate-700">
                  <tr>
                    <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">
                      工單號
                    </th>
                    <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">
                      物料編號
                    </th>
                    <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">
                      物料名稱
                    </th>
                    <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">
                      每日平均出貨量
                    </th>
                    <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">
                      出貨前預計存貨量
                    </th>
                    <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">
                      單位
                    </th>
                    <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">
                      可用日
                    </th>
                    <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">
                      生產量(批)
                    </th>
                    <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">
                      預計生產包數
                    </th>
                    <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">
                      優先度
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {selectedLines.map((line: any) => (
                    <tr
                      key={line.id}
                      className="border-t border-slate-200 text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700/30"
                    >
                      <td className="px-4 py-2 font-semibold text-blue-600 dark:text-blue-400">
                        {line.joCode || "-"}
                      </td>
                      <td className="px-4 py-2 font-semibold">
                        {line.itemCode}
                      </td>
                      <td className="px-4 py-2">{line.itemName}</td>
                      <td className="px-4 py-2 text-right">
                        {formatNum(line.avgQtyLastMonth)}
                      </td>
                      <td className="px-4 py-2 text-right">
                        {formatNum(line.stockQty)}
                      </td>
                      <td className="px-4 py-2">{line.stockUnit}</td>
                      <td
                        className={`px-4 py-2 text-right ${
                          line.daysLeft < 5
                            ? "font-bold text-red-600 dark:text-red-400"
                            : ""
                        }`}
                      >
                        {line.daysLeft}
                      </td>
                      <td className="px-4 py-2 text-right">
                        {formatNum(line.batchNeed)}
                      </td>
                      <td className="px-4 py-2 text-right font-semibold">
                        {formatNum(line.prodQty)}
                      </td>
                      <td className="px-4 py-2 text-center">
                        {line.itemPriority}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div className="flex gap-2 border-t border-slate-200 bg-slate-50 p-4 dark:border-slate-700 dark:bg-slate-800">
              <button
                type="button"
                onClick={handleAutoGenJob}
                disabled={isGenerating}
                className="inline-flex items-center gap-2 rounded-lg bg-blue-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-600 disabled:opacity-50"
              >
                {isGenerating ? (
                  <CircularProgress size={16} sx={{ display: "block" }} />
                ) : (
                  <Hub sx={{ fontSize: 16 }} />
                )}
                自動生成工單
              </button>
              <button
                type="button"
                onClick={() => setIsDetailOpen(false)}
                disabled={isGenerating}
                className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-100 disabled:opacity-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600"
              >
                關閉
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Forecast Dialog */}
      {isForecastDialogOpen && (
        <div
          className="fixed inset-0 z-[1300] flex items-center justify-center p-4"
          role="dialog"
          aria-modal="true"
        >
          <div
            className="absolute inset-0 bg-black/50"
            onClick={() => setIsForecastDialogOpen(false)}
          />
          <div className="relative z-10 w-full max-w-sm rounded-lg border border-slate-200 bg-white p-4 shadow-xl dark:border-slate-700 dark:bg-slate-800 sm:max-w-md">
            <h3 className="mb-4 text-lg font-semibold text-slate-900 dark:text-white">
              準備生成預計排期
            </h3>
            <div className="flex flex-col gap-4">
              <div>
                <label
                  htmlFor="forecast-start"
                  className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
                >
                  開始日期
                </label>
                <input
                  id="forecast-start"
                  type="date"
                  value={forecastStartDate}
                  onChange={(e) => setForecastStartDate(e.target.value)}
                  min={dayjs().subtract(30, "day").format("YYYY-MM-DD")}
                  className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
                />
              </div>
              <div>
                <label
                  htmlFor="forecast-days"
                  className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
                >
                  排期日數
                </label>
                <input
                  id="forecast-days"
                  type="number"
                  min={1}
                  max={365}
                  value={forecastDays}
                  onChange={(e) => {
                    const val =
                      e.target.value === "" ? "" : Number(e.target.value);
                    if (
                      val === "" ||
                      (Number.isInteger(val) && val >= 1 && val <= 365)
                    ) {
                      setForecastDays(val);
                    }
                  }}
                  className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
                />
              </div>
            </div>
            <div className="mt-6 flex justify-end gap-2">
              <button
                type="button"
                onClick={() => setIsForecastDialogOpen(false)}
                className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600"
              >
                取消
              </button>
              <button
                type="button"
                onClick={handleConfirmForecast}
                disabled={
                  !forecastStartDate || forecastDays === "" || loading
                }
                className="inline-flex items-center gap-2 rounded-lg bg-blue-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-600 disabled:opacity-50"
              >
                {loading ? (
                  <CircularProgress size={16} sx={{ display: "block" }} />
                ) : (
                  <ShowChart sx={{ fontSize: 16 }} />
                )}
                計算預測排期
              </button>
            </div>
          </div>
        </div>
      )}

      {/* DO Qty Export Dialog */}
      {isDoQtyExportDialogOpen && (
        <div
          className="fixed inset-0 z-[1300] flex items-center justify-center p-4"
          role="dialog"
          aria-modal="true"
        >
          <div
            className="absolute inset-0 bg-black/50"
            onClick={() => !isDoQtyExporting && setIsDoQtyExportDialogOpen(false)}
          />
          <div className="relative z-10 w-full max-w-sm rounded-lg border border-slate-200 bg-white p-4 shadow-xl dark:border-slate-700 dark:bg-slate-800">
            <h3 className="mb-2 text-lg font-semibold text-slate-900 dark:text-white">
              匯出送貨單數量
            </h3>
            <p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
              選擇日期範圍（僅含具 BOM 的物料，數量為庫存單位）
            </p>
            <div className="flex flex-col gap-4">
              <div>
                <label
                  htmlFor="do-qty-export-from"
                  className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
                >
                  開始日期
                </label>
                <input
                  id="do-qty-export-from"
                  type="date"
                  value={doQtyExportFromDate}
                  onChange={(e) => setDoQtyExportFromDate(e.target.value)}
                  className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 focus:border-violet-500 focus:outline-none focus:ring-2 focus:ring-violet-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
                />
              </div>
              <div>
                <label
                  htmlFor="do-qty-export-to"
                  className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
                >
                  結束日期
                </label>
                <input
                  id="do-qty-export-to"
                  type="date"
                  value={doQtyExportToDate}
                  onChange={(e) => setDoQtyExportToDate(e.target.value)}
                  className="w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 focus:border-violet-500 focus:outline-none focus:ring-2 focus:ring-violet-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
                />
              </div>
            </div>
            <div className="mt-6 flex justify-end gap-2">
              <button
                type="button"
                onClick={() => setIsDoQtyExportDialogOpen(false)}
                disabled={isDoQtyExporting}
                className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 disabled:opacity-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600"
              >
                取消
              </button>
              <button
                type="button"
                onClick={handleConfirmDoQtyExport}
                disabled={
                  !doQtyExportFromDate ||
                  !doQtyExportToDate ||
                  isDoQtyExporting
                }
                className="inline-flex items-center gap-2 rounded-lg bg-violet-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-violet-600 disabled:opacity-50"
              >
                {isDoQtyExporting ? (
                  <CircularProgress size={16} sx={{ display: "block" }} />
                ) : (
                  <Download sx={{ fontSize: 16 }} />
                )}
                匯出
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Export Dialog */}
      {isExportDialogOpen && (
        <div
          className="fixed inset-0 z-[1300] flex items-center justify-center p-4"
          role="dialog"
          aria-modal="true"
        >
          <div
            className="absolute inset-0 bg-black/50"
            onClick={() => setIsExportDialogOpen(false)}
          />
          <div className="relative z-10 w-full max-w-xs rounded-lg border border-slate-200 bg-white p-4 shadow-xl dark:border-slate-700 dark:bg-slate-800">
            <h3 className="mb-2 text-lg font-semibold text-slate-900 dark:text-white">
              匯出排期/物料用量預計
            </h3>
            <p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
              選擇要匯出的起始日期
            </p>
            <label
              htmlFor="export-from"
              className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
            >
              起始日期
            </label>
            <input
              id="export-from"
              type="date"
              value={exportFromDate}
              onChange={(e) => setExportFromDate(e.target.value)}
              min={dayjs().subtract(90, "day").format("YYYY-MM-DD")}
              className="mb-4 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
            />
            <div className="flex justify-end gap-2">
              <button
                type="button"
                onClick={() => setIsExportDialogOpen(false)}
                className="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600"
              >
                取消
              </button>
              <button
                type="button"
                onClick={handleConfirmExport}
                disabled={!exportFromDate || loading}
                className="inline-flex items-center gap-2 rounded-lg bg-emerald-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-emerald-600 disabled:opacity-50"
              >
                {loading ? (
                  <CircularProgress size={16} sx={{ display: "block" }} />
                ) : (
                  <Download sx={{ fontSize: 16 }} />
                )}
                匯出
              </button>
            </div>
          </div>
        </div>
      )}

      {/* 排期設定 Dialog */}
      {isDailyOutPanelOpen && (
        <div
          className="fixed inset-0 z-[1300] flex items-center justify-center p-4"
          role="dialog"
          aria-modal="true"
          aria-labelledby="settings-panel-title"
        >
          <div
            className="absolute inset-0 bg-black/50"
            onClick={() => setIsDailyOutPanelOpen(false)}
          />
          <div className="relative z-10 flex max-h-[90vh] w-full max-w-6xl flex-col overflow-hidden rounded-lg border border-slate-200 bg-white shadow-xl dark:border-slate-700 dark:bg-slate-800">
            <div className="flex items-center justify-between border-b border-slate-200 bg-slate-100 px-4 py-3 dark:border-slate-700 dark:bg-slate-700/50">
              <h2
                id="settings-panel-title"
                className="text-lg font-semibold text-slate-900 dark:text-white"
              >
                排期設定
              </h2>
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  onClick={() => void handleRefreshInventoryOnHand()}
                  disabled={isRefreshingOnHand || itemDailyOutLoading}
                  className="inline-flex items-center gap-1 rounded border border-sky-500/80 bg-sky-50 px-3 py-1 text-xs font-semibold text-sky-700 shadow-sm hover:bg-sky-100 disabled:opacity-50 dark:border-sky-400/80 dark:bg-slate-800 dark:text-sky-300 dark:hover:bg-sky-500/20"
                >
                  {isRefreshingOnHand ? (
                    <CircularProgress size={14} sx={{ display: "block" }} />
                  ) : (
                    <Refresh sx={{ fontSize: 14 }} />
                  )}
                  刷新系統庫存
                </button>
                <button
                  type="button"
                  onClick={handleExportFakeOnHand}
                  className="inline-flex items-center gap-1 rounded border border-amber-500/80 bg-amber-50 px-3 py-1 text-xs font-semibold text-amber-700 shadow-sm hover:bg-amber-100 dark:border-amber-400/80 dark:bg-slate-800 dark:text-amber-300 dark:hover:bg-amber-500/20"
                >
                  匯出排期庫存 Excel
                </button>
                <label className="inline-flex cursor-pointer items-center gap-1 rounded border border-blue-500/70 bg-white px-3 py-1 text-xs font-semibold text-blue-600 shadow-sm hover:bg-blue-50 dark:border-blue-500/50 dark:bg-slate-800 dark:text-blue-400 dark:hover:bg-blue-500/10">
                  <span className="inline-flex h-2 w-2 rounded-full bg-amber-500" />
                  <span className="text-amber-700 dark:text-amber-300">匯入排期庫存 Excel（覆蓋設定）</span>
                  <input
                    type="file"
                    accept=".xlsx,.xls"
                    className="hidden"
                    onChange={(e) => {
                      const file = e.target.files?.[0];
                      if (file) {
                        void handleImportFakeOnHand(file);
                        e.target.value = "";
                      }
                    }}
                  />
                </label>
                <button
                  type="button"
                  onClick={() => setIsDailyOutPanelOpen(false)}
                  className="rounded p-1 text-slate-500 hover:bg-slate-200 hover:text-slate-700 dark:hover:bg-slate-600 dark:hover:text-slate-200"
                >
                  關閉
                </button>
              </div>
            </div>
            <p className="px-4 py-2 text-sm text-slate-600 dark:text-slate-400">
              預設為過去 30 天（含今日）。設定排期每天出貨量、設定排期庫存可編輯並按列儲存。
            </p>
            <div className="max-h-[60vh] overflow-auto">
              {itemDailyOutLoading ? (
                <div className="flex items-center justify-center py-12">
                  <CircularProgress />
                </div>
              ) : (
                <table className="w-full min-w-[900px] text-left text-sm">
                  <thead className="sticky top-0 bg-slate-50 dark:bg-slate-700">
                    <tr>
                      <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">物料編號</th>
                      <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">物料名稱</th>
                      <th className="px-4 py-2 font-bold text-slate-700 dark:text-slate-200">單位</th>
                      <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">庫存</th>
                      <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期庫存</th>
                      <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">過去平均出貨量</th>
                      <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期每天出貨量</th>
                      <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">咖啡</th>
                      <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">茶</th>
                      <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">檸檬</th>
                    </tr>
                  </thead>
                  <tbody>
                    {itemDailyOutList.map((row, idx) => (
                      <DailyOutRow
                        key={`${row.itemCode}-${idx}`}
                        row={row}
                        onSave={handleSaveDailyQty}
                        onClear={handleClearDailyQty}
                        onSetCoffeeOrTea={handleSetCoffeeOrTea}
                        onSetFakeOnHand={handleSetFakeOnHand}
                        onClearFakeOnHand={handleClearFakeOnHand}
                        saving={dailyOutSavingCode === row.itemCode}
                        clearing={dailyOutClearingCode === row.itemCode}
                        coffeeOrTeaUpdating={coffeeOrTeaUpdating}
                        fakeOnHandSaving={fakeOnHandSavingCode === row.itemCode}
                        fakeOnHandClearing={fakeOnHandClearingCode === row.itemCode}
                        formatNum={formatNum}
                      />
                    ))}
                  </tbody>
                </table>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function DailyOutRow({
  row,
  onSave,
  onClear,
  onSetCoffeeOrTea,
  onSetFakeOnHand,
  onClearFakeOnHand,
  saving,
  clearing,
  coffeeOrTeaUpdating,
  fakeOnHandSaving,
  fakeOnHandClearing,
  formatNum,
}: {
  row: ItemDailyOutRow;
  onSave: (itemCode: string, dailyQty: number) => void;
  onClear: (itemCode: string) => void;
  onSetCoffeeOrTea: (itemCode: string, systemType: "coffee" | "tea" | "lemon", enabled: boolean) => void;
  onSetFakeOnHand: (itemCode: string, onHandQty: number) => void;
  onClearFakeOnHand: (itemCode: string) => void;
  saving: boolean;
  clearing: boolean;
  coffeeOrTeaUpdating: string | null;
  fakeOnHandSaving: boolean;
  fakeOnHandClearing: boolean;
  formatNum: (n: any) => string;
}) {
  const [editQty, setEditQty] = useState<string>(
    row.dailyQty != null ? String(row.dailyQty) : ""
  );
  const [editFakeOnHand, setEditFakeOnHand] = useState<string>(
    row.fakeOnHandQty != null ? String(row.fakeOnHandQty) : ""
  );
  useEffect(() => {
    setEditQty(row.dailyQty != null ? String(row.dailyQty) : "");
  }, [row.dailyQty]);
  useEffect(() => {
    setEditFakeOnHand(row.fakeOnHandQty != null ? String(row.fakeOnHandQty) : "");
  }, [row.fakeOnHandQty]);
  const numVal = parseFloat(editQty);
  const isValid = !Number.isNaN(numVal) && numVal >= 0;
  const hasSetQty = row.dailyQty != null;
  const fakeOnHandNum = parseFloat(editFakeOnHand);
  const isValidFakeOnHand = !Number.isNaN(fakeOnHandNum) && fakeOnHandNum >= 0;
  const hasSetFakeOnHand = row.fakeOnHandQty != null;
  const isCoffee = (row.isCoffee ?? 0) > 0;
  const isTea = (row.isTea ?? 0) > 0;
  const isLemon = (row.isLemon ?? 0) > 0;
  const updatingCoffee = coffeeOrTeaUpdating === `${row.itemCode}-coffee`;
  const updatingTea = coffeeOrTeaUpdating === `${row.itemCode}-tea`;
  const updatingLemon = coffeeOrTeaUpdating === `${row.itemCode}-lemon`;
  return (
    <tr className="border-t border-slate-200 text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700/30">
      <td className="px-4 py-2 font-medium">{row.itemCode}</td>
      <td className="px-4 py-2">{row.itemName}</td>
      <td className="px-4 py-2">{row.unit ?? ""}</td>
      <td className="px-4 py-2 text-right">{formatNum(row.onHandQty)}</td>
      <td className="px-4 py-2 text-left">
        <div className="flex items-center justify-start gap-0.5">
          <input
            type="number"
            min={0}
            step={1}
            value={editFakeOnHand}
            onChange={(e) => setEditFakeOnHand(e.target.value)}
            onBlur={() => {
              if (isValidFakeOnHand) onSetFakeOnHand(row.itemCode, fakeOnHandNum);
            }}
            onKeyDown={(e) => {
              if (e.key === "Enter" && isValidFakeOnHand) onSetFakeOnHand(row.itemCode, fakeOnHandNum);
            }}
            className="w-24 rounded border border-slate-300 bg-white px-2 py-1 text-left text-slate-900 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
          />
          {hasSetFakeOnHand && (
            <button
              type="button"
              disabled={fakeOnHandClearing}
              onClick={() => onClearFakeOnHand(row.itemCode)}
              title="清除設定排期庫存"
              className="rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 disabled:opacity-50 dark:hover:bg-slate-600 dark:hover:text-slate-200"
            >
              {fakeOnHandClearing ? <CircularProgress size={14} sx={{ display: "block" }} /> : <Clear sx={{ fontSize: 18 }} />}
            </button>
          )}
        </div>
      </td>
      <td className="px-4 py-2 text-right">{formatNum(row.avgQtyLastMonth)}</td>
      <td className="px-4 py-2 text-left">
        <div className="flex items-center justify-start gap-0.5">
          <input
            type="number"
            min={0}
            step={1}
            value={editQty}
            onChange={(e) => setEditQty(e.target.value)}
            onBlur={() => {
              if (isValid) onSave(row.itemCode, numVal);
            }}
            onKeyDown={(e) => {
              if (e.key === "Enter" && isValid) onSave(row.itemCode, numVal);
            }}
            className="w-24 rounded border border-slate-300 bg-white px-2 py-1 text-left text-slate-900 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-100"
          />
          {hasSetQty && (
            <button
              type="button"
              disabled={clearing}
              onClick={() => onClear(row.itemCode)}
              title="清除設定排期每天出貨量"
              className="rounded p-0.5 text-slate-400 hover:bg-slate-200 hover:text-slate-600 disabled:opacity-50 dark:hover:bg-slate-600 dark:hover:text-slate-200"
            >
              {clearing ? <CircularProgress size={14} sx={{ display: "block" }} /> : <Clear sx={{ fontSize: 18 }} />}
            </button>
          )}
        </div>
      </td>
      <td className="px-4 py-2 text-center">
        <label className="inline-flex cursor-pointer items-center gap-1">
          <input
            type="checkbox"
            checked={isCoffee}
            disabled={updatingCoffee}
            onChange={(e) => onSetCoffeeOrTea(row.itemCode, "coffee", e.target.checked)}
            className="h-4 w-4 rounded border-slate-300 text-blue-500 focus:ring-blue-500"
          />
          {updatingCoffee && <CircularProgress size={14} sx={{ display: "block" }} />}
        </label>
      </td>
      <td className="px-4 py-2 text-center">
        <label className="inline-flex cursor-pointer items-center gap-1">
          <input
            type="checkbox"
            checked={isTea}
            disabled={updatingTea}
            onChange={(e) => onSetCoffeeOrTea(row.itemCode, "tea", e.target.checked)}
            className="h-4 w-4 rounded border-slate-300 text-blue-500 focus:ring-blue-500"
          />
          {updatingTea && <CircularProgress size={14} sx={{ display: "block" }} />}
        </label>
      </td>
      <td className="px-4 py-2 text-center">
        <label className="inline-flex cursor-pointer items-center gap-1">
          <input
            type="checkbox"
            checked={isLemon}
            disabled={updatingLemon}
            onChange={(e) => onSetCoffeeOrTea(row.itemCode, "lemon", e.target.checked)}
            className="h-4 w-4 rounded border-slate-300 text-blue-500 focus:ring-blue-500"
          />
          {updatingLemon && <CircularProgress size={14} sx={{ display: "block" }} />}
        </label>
      </td>
    </tr>
  );
}
