"use client";

import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  alpha,
  Avatar,
  Box,
  Typography,
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableRow,
  Paper,
  TextField,
  Alert,
  CircularProgress,
  Stack,
  Button,
  Chip,
  ToggleButton,
  ToggleButtonGroup,
  FormControl,
  FormControlLabel,
  InputLabel,
  MenuItem,
  Select,
  Switch,
  Tooltip,
} from "@mui/material";
import Link from "next/link";
import dayjs from "dayjs";
import AccountTree from "@mui/icons-material/AccountTree";
import CheckCircleOutline from "@mui/icons-material/CheckCircleOutline";
import FilterAltOutlined from "@mui/icons-material/FilterAltOutlined";
import HourglassEmpty from "@mui/icons-material/HourglassEmpty";
import PlayCircleOutline from "@mui/icons-material/PlayCircleOutline";
import Schedule from "@mui/icons-material/Schedule";
import TableChart from "@mui/icons-material/TableChart";
import { useTheme, type Theme } from "@mui/material/styles";
import { fetchProcessBoard, type ProcessBoardRow } from "@/app/api/chart/client";
import SafeApexCharts from "@/components/charts/SafeApexCharts";
import { CHART_BOARD_REFRESH_INTERVAL_SEC_OPTIONS } from "@/app/(main)/chart/chartBoardRefreshPrefs";
import { useChartBoardRefreshPrefs } from "@/app/(main)/chart/useChartBoardRefreshPrefs";

const BOARD_STATUS_ZH: Record<string, string> = {
  pending: "未開工",
  in_progress: "進行中",
  completed: "已完工",
};

const BOARD_STATUS_ORDER = ["in_progress", "pending", "completed"] as const;

/** Max processes shown in the horizontal bar chart (typical BOM has ~20–30 steps). */
const PROCESS_BAR_MAX = 35;

/**
 * HSL (degrees, 0–100, 0–100) → #rrggbb for chart-safe saturation/lightness.
 */
function hslToHex(h: number, s: number, l: number): string {
  const sh = ((h % 360) + 360) % 360;
  const ss = Math.min(100, Math.max(0, s)) / 100;
  const sl = Math.min(100, Math.max(0, l)) / 100;
  const c = (1 - Math.abs(2 * sl - 1)) * ss;
  const x = c * (1 - Math.abs(((sh / 60) % 2) - 1));
  const m = sl - c / 2;
  let rp = 0;
  let gp = 0;
  let bp = 0;
  if (sh < 60) {
    rp = c;
    gp = x;
  } else if (sh < 120) {
    rp = x;
    gp = c;
  } else if (sh < 180) {
    gp = c;
    bp = x;
  } else if (sh < 240) {
    gp = x;
    bp = c;
  } else if (sh < 300) {
    rp = x;
    bp = c;
  } else {
    rp = c;
    bp = x;
  }
  const r = Math.round((rp + m) * 255);
  const g = Math.round((gp + m) * 255);
  const b = Math.round((bp + m) * 255);
  return `#${[r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
}

/**
 * Golden-angle hue spacing + staggered S/L so 20–40 adjacent bars rarely look like duplicates.
 */
function generateProcessBarColors(count: number): string[] {
  const n = Math.max(0, count);
  const GOLDEN = 137.508;
  const out: string[] = [];
  for (let i = 0; i < n; i++) {
    const h = (i * GOLDEN) % 360;
    const s = 56 + (i % 5) * 5.5;
    const l = 40 + (i % 7) * 2.4;
    out.push(hslToHex(h, s, l));
  }
  return out;
}

/** Stable color for a process row that is not in the current top-N bar chart. */
function fallbackProcessColor(processId: number): string {
  const h = (Math.abs(processId) * 137.508) % 360;
  return hslToHex(h, 64, 46);
}

function norm(s: string | undefined): string {
  return (s ?? "").trim().toLowerCase();
}

function formatCodeNameLine(code: string, name: string): string {
  const c = (code ?? "").trim();
  const n = (name ?? "").trim();
  if (!c && !n) return "—";
  if (!c) return n;
  if (!n) return c;
  if (c.toLowerCase() === n.toLowerCase()) return n;
  if (n.toLowerCase().startsWith(`${c.toLowerCase()} `) || n.toLowerCase().startsWith(`${c.toLowerCase()}　`)) return n;
  if (n.length > c.length && n.toLowerCase().startsWith(c.toLowerCase())) {
    const after = n.slice(c.length, c.length + 1);
    if (/[\s\-–—:：·.|／/]/.test(after)) return n;
  }
  return `${c} ${n}`;
}

function boardStatusAccent(
  theme: Theme,
  status: string,
): { stripe: string; tint: string } {
  const k = norm(status);
  if (k === "completed") {
    return {
      stripe: theme.palette.success.main,
      tint: alpha(theme.palette.success.main, 0.06),
    };
  }
  if (k === "in_progress") {
    return {
      stripe: theme.palette.primary.main,
      tint: alpha(theme.palette.primary.main, 0.07),
    };
  }
  return {
    stripe: theme.palette.grey[400],
    tint: alpha(theme.palette.grey[500], 0.06),
  };
}

function processAccentColor(processId: number, barColorMap: Map<number, string>): string {
  return barColorMap.get(processId) ?? fallbackProcessColor(processId);
}

function BoardStatusVisual({ status }: { status: string }) {
  const k = norm(status);
  const label = BOARD_STATUS_ZH[k] ?? status;
  if (k === "completed") {
    return (
      <Chip
        size="small"
        icon={<CheckCircleOutline />}
        label={label}
        color="success"
        variant="filled"
        sx={{ fontWeight: 600 }}
      />
    );
  }
  if (k === "in_progress") {
    return (
      <Chip
        size="small"
        icon={<PlayCircleOutline />}
        label={label}
        color="primary"
        variant="filled"
        sx={{ fontWeight: 600 }}
      />
    );
  }
  return (
    <Chip
      size="small"
      icon={<HourglassEmpty />}
      label={label}
      color="default"
      variant="outlined"
      sx={{ fontWeight: 600, borderWidth: 2 }}
    />
  );
}

function timeCell(text: string) {
  const empty = !text || text === "—";
  return (
    <Stack direction="row" spacing={0.75} alignItems="center" sx={{ minWidth: 0 }}>
      <Schedule
        fontSize="small"
        sx={{ color: empty ? "action.disabled" : "primary.light", flexShrink: 0, opacity: empty ? 0.5 : 1 }}
      />
      <Typography
        variant="body2"
        component="span"
        sx={{
          color: empty ? "text.disabled" : "text.primary",
          fontFamily: empty ? "inherit" : "ui-monospace, monospace",
          fontSize: empty ? "0.8rem" : "0.8125rem",
          whiteSpace: "nowrap",
        }}
      >
        {empty ? "—" : text}
      </Typography>
    </Stack>
  );
}

function formatQtyBoard(n: number): string {
  if (!Number.isFinite(n) || n === 0) return "0";
  const t = Number(n);
  return Math.abs(t - Math.round(t)) < 1e-6 ? String(Math.round(t)) : t.toFixed(2);
}

/** API: decimal minutes (Σ seconds ÷ 60). Shows seconds when under 1 min. */
function formatDurationMins(mins: number): string {
  if (!Number.isFinite(mins) || mins <= 0) return "—";
  const secs = Math.round(mins * 60);
  if (secs < 60) return `${secs} 秒`;
  if (mins < 10) return `${mins.toFixed(1)} 分`;
  return `${Math.round(mins)} 分`;
}

function formatAssumeEndMmDdHhMm(processStart: string, planProcessingMins: number): string {
  if (!processStart?.trim() || !planProcessingMins) return "—";
  const d = dayjs(processStart.replace(" ", "T"));
  if (!d.isValid()) return "—";
  return d.add(planProcessingMins, "minute").format("MM-DD HH:mm");
}

function ProcessBoardJoSummary({ r }: { r: ProcessBoardRow }) {
  const mat =
    [r.itemCode, r.itemName].filter((x) => (x ?? "").trim()).join("-") || "—";
  const assumeEnd = formatAssumeEndMmDdHhMm(r.productProcessStart, r.planProcessingMinsTotal);
  const startMmDd = r.productProcessStart ? dayjs(r.productProcessStart.replace(" ", "T")).format("MM-DD HH:mm") : "—";
  return (
    <Stack
      direction={{ xs: "column", sm: "row" }}
      spacing={2}
      alignItems="flex-start"
      sx={{ width: "100%", maxWidth: "100%" }}
    >
      <Stack spacing={0.45} sx={{ flex: "1 1 58%", minWidth: { xs: 0, sm: 280 } }}>
        <Typography variant="body2" fontWeight={600} sx={{ lineHeight: 1.35 }}>
          {mat}
        </Typography>
        <Typography variant="body2">
          <Box component="span" color="text.secondary">
            工單類型{" "}
          </Box>
          {r.jobTypeName?.trim() || "—"}
        </Typography>
        <Typography variant="body2">
          <Box component="span" color="text.secondary">
            數量{" "}
          </Box>
          <strong>{formatQtyBoard(r.reqQty)}</strong>
          {r.outputQtyUom ? `（${r.outputQtyUom}）` : ""}
        </Typography>
        <Typography variant="body2">
          <Box component="span" color="text.secondary">
            生產日期{" "}
          </Box>
          {r.productionDate || "—"}
        </Typography>
        <Typography variant="body2">
          <Box component="span" color="text.secondary">
            實際耗時（全單線段加總）{" "}
          </Box>
          {formatDurationMins(r.actualLineMinsTotal)}
        </Typography>
      </Stack>
      <Stack
        spacing={0.45}
        sx={{
          flex: "1 1 42%",
          minWidth: 0,
          pl: { sm: 2 },
          borderLeft: { sm: 1 },
          borderTop: { xs: 1, sm: 0 },
          borderColor: "divider",
          pt: { xs: 1.5, sm: 0 },
        }}
      >
        <Typography variant="body2">
          <Box component="span" color="text.secondary">
            預計生產{" "}
          </Box>
          {r.planProcessingMinsTotal > 0 ? `${Math.round(r.planProcessingMinsTotal)} 分` : "—"}
          {r.planSetupChangeoverMinsTotal > 0
            ? ` · 備轉 ${Math.round(r.planSetupChangeoverMinsTotal)} 分`
            : ""}
        </Typography>
        <Typography variant="body2" sx={{ fontFamily: "ui-monospace, monospace", fontSize: "0.8rem" }}>
          <Box component="span" color="text.secondary" sx={{ fontFamily: "inherit" }}>
            開始{" "}
          </Box>
          {startMmDd}
        </Typography>
        <Typography variant="body2" sx={{ fontFamily: "ui-monospace, monospace", fontSize: "0.8rem" }}>
          <Box component="span" color="text.secondary" sx={{ fontFamily: "inherit" }}>
            預計完成{" "}
          </Box>
          {assumeEnd}
        </Typography>
      </Stack>
    </Stack>
  );
}

function StepPlanActualMins({ plan, actual }: { plan: number; actual: number }) {
  return (
    <Stack spacing={0.35}>
      <Typography variant="body2">
        預計 <strong>{plan > 0 ? Math.round(plan) : "—"}</strong> 分
      </Typography>
      <Typography variant="body2" color="text.secondary">
        實際 <strong>{formatDurationMins(actual)}</strong>
      </Typography>
    </Stack>
  );
}

/** 頂部橫向捲軸與下方表格同步，避免使用者只捲到底部才發現可左右捲。 */
function HorizontalScrollSync({ minWidth, children }: { minWidth: number; children: React.ReactNode }) {
  const topRef = useRef<HTMLDivElement>(null);
  const bottomRef = useRef<HTMLDivElement>(null);
  const lock = useRef(false);

  const syncTopFromBottom = () => {
    const top = topRef.current;
    const bottom = bottomRef.current;
    if (!top || !bottom || lock.current) return;
    if (top.scrollLeft === bottom.scrollLeft) return;
    lock.current = true;
    top.scrollLeft = bottom.scrollLeft;
    requestAnimationFrame(() => {
      lock.current = false;
    });
  };
  const syncBottomFromTop = () => {
    const top = topRef.current;
    const bottom = bottomRef.current;
    if (!top || !bottom || lock.current) return;
    if (top.scrollLeft === bottom.scrollLeft) return;
    lock.current = true;
    bottom.scrollLeft = top.scrollLeft;
    requestAnimationFrame(() => {
      lock.current = false;
    });
  };

  return (
    <Box>
      <Box
        ref={topRef}
        onScroll={syncBottomFromTop}
        sx={{
          overflowX: "auto",
          overflowY: "hidden",
          height: 18,
          mb: 0.75,
          borderRadius: 1,
          border: 1,
          borderColor: "divider",
          bgcolor: (t) => alpha(t.palette.primary.main, 0.06),
          "&::-webkit-scrollbar": { height: 10 },
        }}
        aria-hidden
      >
        <Box sx={{ width: minWidth, height: 1 }} />
      </Box>
      <Box
        ref={bottomRef}
        onScroll={syncTopFromBottom}
        sx={{
          overflowX: "auto",
          WebkitOverflowScrolling: "touch",
          borderRadius: 1,
          "&::-webkit-scrollbar": { height: 10 },
        }}
      >
        {children}
      </Box>
    </Box>
  );
}

/** 生產流程摘要欄加寬（原 300 + 併入之名稱+描述欄 128+220）後，表格最小總寬。 */
const PROCESS_BOARD_TABLE_MIN_WIDTH = 1861;

/** 與工單工藝流程欄位一致：長文換行、空值顯示 — */
function processFlowTextCell(text: string, maxWidth: number) {
  const t = (text ?? "").trim();
  const empty = !t;
  return (
    <Typography
      variant="body2"
      fontWeight={500}
      sx={{
        maxWidth,
        wordBreak: "break-word",
        whiteSpace: "normal",
        lineHeight: 1.5,
        color: empty ? "text.disabled" : "text.primary",
      }}
    >
      {empty ? "—" : t}
    </Typography>
  );
}

/** 名稱 + 描述併單欄（欄標題已為「名稱／描述」，不重複小標）；描述在窄欄內強制換行多列顯示 */
function nameDescriptionCell(name: string, description: string, maxWidth: number) {
  const d = (description ?? "").trim();
  return (
    <Stack spacing={0.35} sx={{ minWidth: 0, width: "100%", maxWidth }}>
      {processFlowTextCell(name, maxWidth)}
      <Typography
        variant="body2"
        fontWeight={500}
        component="div"
        sx={{
          width: "100%",
          maxWidth: "100%",
          wordBreak: "break-word",
          overflowWrap: "anywhere",
          whiteSpace: "normal",
          lineHeight: 1.55,
          color: d ? "text.secondary" : "text.disabled",
        }}
      >
        {d || "—"}
      </Typography>
    </Stack>
  );
}

type GroupMode = "status" | "process";

function groupSectionStripe(theme: Theme, mode: GroupMode, groupKey: string): string {
  if (mode === "status") {
    const k = norm(groupKey);
    if (k === "in_progress") return theme.palette.primary.main;
    if (k === "pending") return theme.palette.grey[500];
    if (k === "completed") return theme.palette.success.main;
  }
  return theme.palette.secondary.main;
}

type ProcessGroupBlock = { key: string; title: string; rows: ProcessBoardRow[] };

export default function ProcessBoardPage() {
  const theme = useTheme();
  const [targetDate, setTargetDate] = useState(() => dayjs().format("YYYY-MM-DD"));
  const [allIncompleteOpen, setAllIncompleteOpen] = useState(false);
  const [rows, setRows] = useState<ProcessBoardRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [lastUpdated, setLastUpdated] = useState("");
  const [groupMode, setGroupMode] = useState<GroupMode>("status");
  const [filterBoardStatus, setFilterBoardStatus] = useState<string | null>(null);
  const [filterProcessId, setFilterProcessId] = useState<number | null>(null);
  const { autoRefreshOn, setAutoRefreshOn, refreshIntervalSec, setRefreshIntervalSec } =
    useChartBoardRefreshPrefs("process");

  const load = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const data = allIncompleteOpen
        ? await fetchProcessBoard(undefined, { incompleteOnly: true })
        : await fetchProcessBoard(targetDate);
      setRows(data);
      setLastUpdated(dayjs().format("YYYY-MM-DD HH:mm:ss"));
    } catch (e) {
      setError(e instanceof Error ? e.message : "Request failed");
    } finally {
      setLoading(false);
    }
  }, [allIncompleteOpen, targetDate]);

  useEffect(() => {
    void load();
  }, [load]);

  useEffect(() => {
    if (!autoRefreshOn || refreshIntervalSec < 10) return;
    const t = setInterval(() => void load(), refreshIntervalSec * 1000);
    return () => clearInterval(t);
  }, [load, autoRefreshOn, refreshIntervalSec]);

  useEffect(() => {
    setFilterBoardStatus(null);
    setFilterProcessId(null);
  }, [targetDate, allIncompleteOpen]);

  useEffect(() => {
    const onKey = (ev: KeyboardEvent) => {
      const t = ev.target as HTMLElement | null;
      if (
        t &&
        (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable)
      ) {
        return;
      }
      if (ev.ctrlKey || ev.metaKey || ev.altKey) return;
      if (ev.key === "t" || ev.key === "T") {
        ev.preventDefault();
        setTargetDate(dayjs().format("YYYY-MM-DD"));
      }
      if (ev.key === "y" || ev.key === "Y") {
        ev.preventDefault();
        setTargetDate(dayjs().subtract(1, "day").format("YYYY-MM-DD"));
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const displayRows = useMemo(() => {
    return rows.filter((r) => {
      if (filterBoardStatus && norm(r.boardStatus) !== norm(filterBoardStatus)) return false;
      if (filterProcessId != null && r.processId !== filterProcessId) return false;
      return true;
    });
  }, [rows, filterBoardStatus, filterProcessId]);

  const statusDonut = useMemo(() => {
    const map = new Map<string, number>();
    rows.forEach((r) => {
      const k = norm(r.boardStatus) || "pending";
      map.set(k, (map.get(k) ?? 0) + 1);
    });
    const order = ["in_progress", "pending", "completed"];
    const keys = order.filter((k) => (map.get(k) ?? 0) > 0);
    const extra = Array.from(map.keys()).filter((k) => !order.includes(k));
    extra.sort();
    const allKeys = [...keys, ...extra];
    return {
      keys: allKeys,
      labels: allKeys.map((k) => BOARD_STATUS_ZH[k] ?? k),
      series: allKeys.map((k) => map.get(k) ?? 0),
    };
  }, [rows]);

  const processBar = useMemo(() => {
    const m = new Map<number, { processId: number; label: string; count: number }>();
    rows.forEach((r) => {
      const id = r.processId;
      if (!id) return;
      const cur = m.get(id) ?? {
        processId: id,
        label: formatCodeNameLine(r.processCode, r.processName),
        count: 0,
      };
      cur.count += 1;
      m.set(id, cur);
    });
    const list = Array.from(m.values())
      .sort((a, b) => b.count - a.count)
      .slice(0, PROCESS_BAR_MAX);
    return {
      processIds: list.map((x) => x.processId),
      categories: list.map((x) => (x.label.length > 28 ? `${x.label.slice(0, 26)}…` : x.label)),
      data: list.map((x) => x.count),
    };
  }, [rows]);

  const processBarPalette = useMemo(
    () => generateProcessBarColors(processBar.processIds.length),
    [processBar.processIds],
  );

  const processBarColorById = useMemo(() => {
    const m = new Map<number, string>();
    processBar.processIds.forEach((id, i) => {
      const c = processBarPalette[i];
      if (c) m.set(id, c);
    });
    return m;
  }, [processBar.processIds, processBarPalette]);

  const donutClickRef = useRef<(i: number) => void>(() => {});
  donutClickRef.current = (index: number) => {
    const key = statusDonut.keys[index];
    if (key == null) return;
    setFilterProcessId(null);
    setFilterBoardStatus((prev) => (prev === key ? null : key));
  };

  const barClickRef = useRef<(i: number) => void>(() => {});
  barClickRef.current = (index: number) => {
    const id = processBar.processIds[index];
    if (id == null) return;
    setFilterBoardStatus(null);
    setFilterProcessId((prev) => (prev === id ? null : id));
  };

  const donutColors = useMemo(() => {
    const fallback = [
      theme.palette.primary.main,
      theme.palette.warning.main,
      theme.palette.success.main,
      theme.palette.secondary.main,
      theme.palette.error.main,
    ];
    return statusDonut.keys.map((k, i) => {
      if (k === "in_progress") return theme.palette.primary.main;
      if (k === "pending") return theme.palette.grey[500];
      if (k === "completed") return theme.palette.success.main;
      return fallback[(i + 3) % fallback.length];
    });
  }, [statusDonut.keys, theme]);

  const donutOptions = useMemo(
    () => ({
      chart: {
        type: "donut" as const,
        toolbar: { show: false },
        events: {
          dataPointSelection: (_e: unknown, _ctx: unknown, cfg: { dataPointIndex?: number }) => {
            const i = cfg?.dataPointIndex;
            if (typeof i !== "number" || i < 0) return;
            donutClickRef.current(i);
          },
          legendClick: (_chartContext: unknown, seriesIndex: number) => {
            if (typeof seriesIndex !== "number" || seriesIndex < 0) return false;
            donutClickRef.current(seriesIndex);
            return false;
          },
        },
      },
      labels: statusDonut.labels,
      colors: donutColors,
      legend: { position: "bottom" as const, onItemClick: { toggleDataSeries: false } },
      plotOptions: { pie: { donut: { size: "62%" } } },
      dataLabels: { enabled: true },
    }),
    [donutColors, statusDonut.labels],
  );

  const barColors = useMemo(() => processBarPalette.slice(0, processBar.data.length), [processBar.data.length, processBarPalette]);

  const barOptions = useMemo(
    () => ({
      chart: {
        type: "bar" as const,
        toolbar: { show: false },
        events: {
          dataPointSelection: (_e: unknown, _ctx: unknown, cfg: { dataPointIndex?: number }) => {
            const i = cfg?.dataPointIndex;
            if (typeof i !== "number" || i < 0) return;
            barClickRef.current(i);
          },
        },
      },
      colors: barColors,
      plotOptions: {
        bar: {
          borderRadius: 6,
          horizontal: true,
          barHeight: "72%",
          distributed: true,
        },
      },
      dataLabels: { enabled: true },
      xaxis: { categories: processBar.categories },
      yaxis: { labels: { maxWidth: 220 } },
      legend: { show: false },
    }),
    [barColors, processBar.categories],
  );

  const filterProcessLabel = useMemo(() => {
    if (filterProcessId == null) return "";
    const hit = rows.find((r) => r.processId === filterProcessId);
    return hit ? formatCodeNameLine(hit.processCode, hit.processName) : `工序 #${filterProcessId}`;
  }, [filterProcessId, rows]);

  const grouped = useMemo((): ProcessGroupBlock[] => {
    if (groupMode === "status") {
      const buckets: Record<string, ProcessBoardRow[]> = {};
      BOARD_STATUS_ORDER.forEach((k) => {
        buckets[k] = [];
      });
      displayRows.forEach((r) => {
        const k = norm(r.boardStatus) || "pending";
        if (!buckets[k]) buckets[k] = [];
        buckets[k].push(r);
      });
      return BOARD_STATUS_ORDER.filter((k) => (buckets[k]?.length ?? 0) > 0).map((k) => ({
        key: k,
        title: BOARD_STATUS_ZH[k] ?? k,
        rows: buckets[k] ?? [],
      }));
    }
    const byProc = new Map<number, { title: string; rows: ProcessBoardRow[] }>();
    displayRows.forEach((r) => {
      const id = r.processId;
      const title = formatCodeNameLine(r.processCode, r.processName);
      const cur = byProc.get(id) ?? { title, rows: [] };
      cur.rows.push(r);
      byProc.set(id, cur);
    });
    return Array.from(byProc.entries())
      .sort((a, b) => a[1].title.localeCompare(b[1].title, "zh-Hant"))
      .map(([id, g]) => ({
        key: `proc-${id}`,
        title: g.title,
        rows: g.rows.sort((a: ProcessBoardRow, b: ProcessBoardRow) => a.seqNo - b.seqNo),
      }));
  }, [displayRows, groupMode]);

  const isTargetToday = targetDate === dayjs().format("YYYY-MM-DD");
  const targetWeekdayZh = ["日", "一", "二", "三", "四", "五", "六"][dayjs(targetDate).day()] ?? "";

  return (
    <Box
      sx={{
        width: "100%",
        maxWidth: "100%",
        mx: "auto",
        p: { xs: 0.5, sm: 1 },
        boxSizing: "border-box",
      }}
    >
      <Typography variant="h5" sx={{ mb: 1, fontWeight: 600, display: "flex", alignItems: "center", gap: 1 }}>
        <AccountTree color="primary" /> 工序即時看板
      </Typography>
      <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
        每列為一筆工單工序（<strong>job_order_process</strong>）。狀態與工單「工藝流程」一致：優先依產線明細（<strong>productprocessline</strong>）狀態與起訖時間；若無對應明細則沿用工序上的開工／完工時間。
        {autoRefreshOn
          ? ` 已開啟自動重新整理（每 ${refreshIntervalSec} 秒）`
          : " 預設不自動更新，可開啟「自動重新整理」並選擇間隔。"}
        {" 設定自動儲存在本機（登入時依帳號；未登入則為此分頁工作階段）。"}
        {" · 最後更新 "}
        {lastUpdated || "—"}
        <strong> 快捷鍵</strong>（不在輸入框內時）：
        <Box
          component="kbd"
          sx={{ px: 0.75, py: 0.125, borderRadius: 0.5, border: 1, borderColor: "divider", fontSize: "0.75rem" }}
        >
          T
        </Box>{" "}
        今日、
        <Box
          component="kbd"
          sx={{ px: 0.75, py: 0.125, borderRadius: 0.5, border: 1, borderColor: "divider", fontSize: "0.75rem" }}
        >
          Y
        </Box>{" "}
        昨日（僅更新工單計劃開始日）。
      </Typography>

      {error && (
        <Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
          {error}
        </Alert>
      )}

      <Stack
        direction={{ xs: "column", lg: "row" }}
        spacing={2}
        alignItems={{ xs: "stretch", lg: "stretch" }}
        justifyContent={{ lg: "space-between" }}
        sx={{ mb: 2 }}
      >
        <Paper
          variant="outlined"
          sx={{
            p: 1.5,
            flex: { lg: "1 1 0" },
            minWidth: { xs: "100%", lg: 280 },
            borderColor: "divider",
            bgcolor: alpha(theme.palette.primary.main, 0.03),
          }}
        >
          <Typography
            variant="overline"
            color="text.secondary"
            sx={{ display: "block", mb: 1, lineHeight: 1.4, letterSpacing: 0.6 }}
          >
            查詢與列表
          </Typography>
          <Stack spacing={1.25}>
            <Typography variant="body2" color="text.secondary" sx={{ fontWeight: 600 }}>
              工單計劃開始日 {targetDate}（週{targetWeekdayZh}）
              {!isTargetToday && <Chip size="small" label="非今日" sx={{ ml: 1 }} variant="outlined" />}
            </Typography>
            <Stack direction="row" flexWrap="wrap" useFlexGap alignItems="center" gap={1.25}>
              <Tooltip title="快捷鍵 T">
                <span>
                  <Button
                    size="small"
                    variant={isTargetToday && !allIncompleteOpen ? "contained" : "outlined"}
                    onClick={() => setTargetDate(dayjs().format("YYYY-MM-DD"))}
                    disabled={allIncompleteOpen}
                  >
                    今日
                  </Button>
                </span>
              </Tooltip>
              <Tooltip title="快捷鍵 Y">
                <span>
                  <Button
                    size="small"
                    variant="outlined"
                    onClick={() => setTargetDate(dayjs().subtract(1, "day").format("YYYY-MM-DD"))}
                    disabled={allIncompleteOpen}
                  >
                    昨日
                  </Button>
                </span>
              </Tooltip>
              <TextField
                size="small"
                label="工單計劃開始日"
                type="date"
                value={targetDate}
                onChange={(e) => setTargetDate(e.target.value)}
                disabled={allIncompleteOpen}
                InputLabelProps={{ shrink: true }}
                sx={{ minWidth: 178 }}
              />
              <Button variant="outlined" size="small" onClick={() => void load()} disabled={loading}>
                重新整理
              </Button>
              <Button
                variant={allIncompleteOpen ? "contained" : "outlined"}
                color={allIncompleteOpen ? "warning" : "primary"}
                size="small"
                onClick={() => setAllIncompleteOpen((v) => !v)}
              >
                全部未完成工單
              </Button>
            </Stack>
            <ToggleButtonGroup
              size="small"
              value={groupMode}
              exclusive
              onChange={(_, v) => v && setGroupMode(v)}
              aria-label="group mode"
              sx={{ alignSelf: "flex-start" }}
            >
              <ToggleButton value="status">依工序狀態</ToggleButton>
              <ToggleButton value="process">依工序</ToggleButton>
            </ToggleButtonGroup>
          </Stack>
        </Paper>

        <Paper
          variant="outlined"
          sx={{
            p: 1.5,
            flex: { lg: "0 0 auto" },
            width: { xs: "100%", lg: "auto" },
            minWidth: { lg: 200 },
            borderColor: "divider",
            bgcolor: alpha(theme.palette.grey[500], 0.06),
          }}
        >
          <Typography
            variant="overline"
            color="text.secondary"
            sx={{ display: "block", mb: 1, lineHeight: 1.4, letterSpacing: 0.6 }}
          >
            其他看板
          </Typography>
          <Stack direction="column" spacing={1} sx={{ maxWidth: 220 }}>
            <Button component={Link} href="/chart/joborder/board" size="small" variant="outlined" fullWidth>
              工單即時看板
            </Button>
            <Button component={Link} href="/chart/equipment/board" size="small" variant="outlined" fullWidth>
              設備使用看板
            </Button>
            <Button component={Link} href="/chart/joborder" size="small" variant="outlined" fullWidth>
              工單圖表
            </Button>
          </Stack>
        </Paper>

        <Paper
          variant="outlined"
          sx={{
            p: 1.5,
            flex: { lg: "0 0 auto" },
            width: { xs: "100%", lg: "auto" },
            minWidth: { xs: "100%", lg: 300 },
            borderColor: "divider",
            bgcolor: alpha(theme.palette.info.main, 0.04),
          }}
        >
          <Typography
            variant="overline"
            color="text.secondary"
            sx={{ display: "block", mb: 1, lineHeight: 1.4, letterSpacing: 0.6 }}
          >
            自動重新整理
          </Typography>
          <Stack direction="row" flexWrap="wrap" useFlexGap alignItems="center" gap={1.25}>
            <FormControlLabel
              control={
                <Switch
                  size="small"
                  checked={autoRefreshOn}
                  onChange={(_, v) => setAutoRefreshOn(v)}
                  inputProps={{ "aria-label": "自動重新整理" }}
                />
              }
              label="開啟"
              sx={{ ml: 0, mr: 0 }}
            />
            <FormControl size="small" sx={{ minWidth: 124 }} disabled={!autoRefreshOn}>
              <InputLabel id="proc-board-refresh-interval-label">間隔（秒）</InputLabel>
              <Select
                labelId="proc-board-refresh-interval-label"
                label="間隔（秒）"
                value={refreshIntervalSec}
                onChange={(e) => setRefreshIntervalSec(Number(e.target.value))}
              >
                {CHART_BOARD_REFRESH_INTERVAL_SEC_OPTIONS.map((sec) => (
                  <MenuItem key={sec} value={sec}>
                    {sec} 秒
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </Stack>
        </Paper>
      </Stack>

      {(filterBoardStatus || filterProcessId != null) && (
        <Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }} flexWrap="wrap" useFlexGap>
          <Typography variant="body2" color="text.secondary">
            篩選中：
          </Typography>
          {filterBoardStatus ? (
            <Chip
              label={BOARD_STATUS_ZH[norm(filterBoardStatus)] ?? filterBoardStatus}
              onDelete={() => setFilterBoardStatus(null)}
              size="small"
            />
          ) : null}
          {filterProcessId != null ? (
            <Chip label={filterProcessLabel || `工序 #${filterProcessId}`} onDelete={() => setFilterProcessId(null)} size="small" />
          ) : null}
          <Button size="small" onClick={() => { setFilterBoardStatus(null); setFilterProcessId(null); }}>
            清除篩選
          </Button>
        </Stack>
      )}

      {!loading && (
        <Stack direction="row" spacing={2} useFlexGap flexWrap="wrap" sx={{ mb: 2 }}>
          <Paper
            variant="outlined"
            sx={{
              px: 2,
              py: 1.5,
              minWidth: 140,
              borderLeft: 4,
              borderLeftColor: "primary.main",
              bgcolor: alpha(theme.palette.primary.main, 0.04),
            }}
          >
            <Stack direction="row" spacing={1.25} alignItems="center">
              <Avatar sx={{ bgcolor: alpha(theme.palette.primary.main, 0.15), color: "primary.main", width: 36, height: 36 }}>
                <TableChart fontSize="small" />
              </Avatar>
              <Box>
                <Typography variant="caption" color="text.secondary" display="block">
                  工序列總數
                </Typography>
                <Typography variant="h6" color="primary.main">
                  {rows.length}
                </Typography>
              </Box>
            </Stack>
          </Paper>
          <Paper
            variant="outlined"
            sx={{
              px: 2,
              py: 1.5,
              minWidth: 140,
              borderLeft: 4,
              borderLeftColor: filterBoardStatus || filterProcessId != null ? "warning.main" : "success.main",
              bgcolor: alpha(
                filterBoardStatus || filterProcessId != null ? theme.palette.warning.main : theme.palette.success.main,
                0.06,
              ),
            }}
          >
            <Stack direction="row" spacing={1.25} alignItems="center">
              <Avatar
                sx={{
                  bgcolor: alpha(
                    filterBoardStatus || filterProcessId != null ? theme.palette.warning.main : theme.palette.success.main,
                    0.2,
                  ),
                  color: filterBoardStatus || filterProcessId != null ? "warning.dark" : "success.dark",
                  width: 36,
                  height: 36,
                }}
              >
                <FilterAltOutlined fontSize="small" />
              </Avatar>
              <Box>
                <Typography variant="caption" color="text.secondary" display="block">
                  篩選後
                </Typography>
                <Typography
                  variant="h6"
                  color={filterBoardStatus || filterProcessId != null ? "warning.dark" : "success.dark"}
                >
                  {displayRows.length}
                </Typography>
              </Box>
            </Stack>
          </Paper>
        </Stack>
      )}

      {rows.length > 0 && (
        <>
          <Alert severity="info" icon={<FilterAltOutlined fontSize="inherit" />} sx={{ mb: 2 }}>
            <Typography variant="body2" component="div">
              圓環圖與長條圖是<strong>查詢篩選條件</strong>（不是另一份獨立統計）：點某一扇區或長條後，下方表格只顯示符合該條件的工序列，與上方「篩選後」筆數一致。再點同一項可取消篩選；亦可用「清除篩選」。
            </Typography>
          </Alert>
          <Stack direction={{ xs: "column", md: "row" }} spacing={2} sx={{ mb: 3 }} alignItems="stretch">
          <Paper variant="outlined" sx={{ p: 2, flex: 1, minWidth: 0 }}>
            <Typography variant="subtitle2" gutterBottom>
              依工序狀態（點扇區 → 篩選下方列表）
            </Typography>
            <Typography variant="caption" color="text.secondary" display="block" sx={{ mb: 1 }}>
              與「篩選後」聯動：選「進行中」僅列出進行中的工序列。
            </Typography>
            {statusDonut.series.some((n) => n > 0) ? (
              <SafeApexCharts
                type="donut"
                series={statusDonut.series}
                options={donutOptions}
                height={280}
                chartRevision={`${statusDonut.keys.join(",")}-${statusDonut.series.join(",")}`}
              />
            ) : (
              <Typography variant="body2" color="text.secondary">
                無資料
              </Typography>
            )}
          </Paper>
          <Paper variant="outlined" sx={{ p: 2, flex: 1, minWidth: 0 }}>
            <Typography variant="subtitle2" gutterBottom>
              依工序名稱 · 筆數（點長條 → 篩選下方列表）
            </Typography>
            <Typography variant="caption" color="text.secondary" display="block" sx={{ mb: 1 }}>
              每色代表不同工序；點長條後表格僅保留該工序。
            </Typography>
            {processBar.data.length > 0 ? (
              <SafeApexCharts
                type="bar"
                series={[{ name: "筆數", data: processBar.data }]}
                options={barOptions}
                height={Math.max(280, processBar.data.length * 28)}
                chartRevision={`${processBar.processIds.join(",")}-${processBar.data.join(",")}`}
              />
            ) : (
              <Typography variant="body2" color="text.secondary">
                無資料
              </Typography>
            )}
          </Paper>
        </Stack>
        </>
      )}

      {loading && rows.length === 0 ? (
        <Box sx={{ display: "flex", justifyContent: "center", py: 6 }}>
          <CircularProgress />
        </Box>
      ) : rows.length === 0 ? (
        <Paper variant="outlined" sx={{ p: 4, textAlign: "center" }}>
          <Typography color="text.secondary">此條件下沒有工序資料。</Typography>
        </Paper>
      ) : (
        <Stack spacing={3}>
          {grouped.map((g) => {
            const sectionStripe = groupSectionStripe(theme, groupMode, g.key);
            return (
              <Paper
                key={g.key}
                variant="outlined"
                sx={{
                  overflowX: "auto",
                  overflowY: "visible",
                  borderRadius: 2,
                  borderColor: alpha(sectionStripe, 0.45),
                }}
              >
                <Box
                  sx={{
                    px: 2,
                    py: 1.25,
                    borderBottom: 1,
                    borderColor: "divider",
                    background: `linear-gradient(90deg, ${alpha(sectionStripe, 0.14)} 0%, ${alpha(sectionStripe, 0.04)} 48%, ${alpha(theme.palette.background.paper, 1)} 100%)`,
                    borderLeft: 5,
                    borderLeftColor: sectionStripe,
                  }}
                >
                  <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
                    <Avatar
                      sx={{
                        width: 32,
                        height: 32,
                        fontSize: "0.8rem",
                        bgcolor: alpha(sectionStripe, 0.25),
                        color: sectionStripe,
                        border: `1px solid ${alpha(sectionStripe, 0.4)}`,
                      }}
                    >
                      {g.rows.length}
                    </Avatar>
                    <Typography variant="subtitle1" fontWeight={700} sx={{ color: "text.primary" }}>
                      {g.title}
                      <Typography component="span" variant="body2" color="text.secondary" fontWeight={500} sx={{ ml: 0.75 }}>
                        · {g.rows.length} 筆
                      </Typography>
                    </Typography>
                  </Stack>
                </Box>
                <Typography variant="caption" color="text.secondary" sx={{ display: "block", px: 2, pb: 1, pt: 0.5 }}>
                  表格較寬時可<strong>左右捲動</strong>；上方<strong>同步捲軸</strong>與表格對齊，無需捲到頁面底部。<strong>點工單號</strong>於新分頁開啟編輯；序顯示於單號下方。
                </Typography>
                <HorizontalScrollSync minWidth={PROCESS_BOARD_TABLE_MIN_WIDTH}>
                  <Table
                    size="small"
                    stickyHeader
                    sx={{
                      minWidth: PROCESS_BOARD_TABLE_MIN_WIDTH,
                      tableLayout: "auto",
                      "& .MuiTableCell-root": { py: 1.25, verticalAlign: "top" },
                    }}
                  >
                    <TableHead>
                      <TableRow sx={{ bgcolor: alpha(theme.palette.primary.main, 0.06) }}>
                        <TableCell sx={{ fontWeight: 700 }}>工序狀態</TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 140 }}>
                          工單
                          <Typography variant="caption" color="text.secondary" display="block" sx={{ fontWeight: 500 }}>
                            點選開啟 · 序在下
                          </Typography>
                        </TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 648 }}>
                          生產流程摘要
                        </TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 112 }}>
                          本工序時間
                          <Typography variant="caption" color="text.secondary" display="block" sx={{ fontWeight: 500 }}>
                            預計／實際
                          </Typography>
                        </TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 139 }}>
                          名稱／描述
                        </TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 240 }}>
                          設備
                          <Typography component="span" variant="caption" color="text.secondary" display="block" sx={{ fontWeight: 500 }}>
                            類型-名稱-編號
                          </Typography>
                        </TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 200 }}>員工</TableCell>
                        <TableCell sx={{ fontWeight: 700, minWidth: 200 }}>
                          開工／完工
                          <Typography variant="caption" color="text.secondary" display="block" sx={{ fontWeight: 500 }}>
                            本工序
                          </Typography>
                        </TableCell>
                      </TableRow>
                    </TableHead>
                    <TableBody>
                      {g.rows.map((r) => {
                        const rowVis = boardStatusAccent(theme, r.boardStatus);
                        const procColor = processAccentColor(r.processId, processBarColorById);
                        return (
                          <TableRow
                            key={r.jopId}
                            hover
                            sx={{
                              borderLeft: 4,
                              borderLeftColor: rowVis.stripe,
                              bgcolor: rowVis.tint,
                              transition: "background-color 0.15s ease",
                              "&:hover": {
                                bgcolor: alpha(rowVis.stripe, 0.12),
                              },
                            }}
                          >
                            <TableCell>
                              <BoardStatusVisual status={r.boardStatus} />
                            </TableCell>
                            <TableCell sx={{ minWidth: 140 }}>
                              <Tooltip title="點擊工單號於新分頁開啟編輯" placement="top" enterDelay={400}>
                                <Link
                                  href={`/jo/edit?id=${r.jobOrderId}`}
                                  target="_blank"
                                  rel="noopener noreferrer"
                                  style={{ textDecoration: "none", color: "inherit" }}
                                >
                                  <Stack alignItems="flex-start" spacing={0.5} sx={{ cursor: "pointer" }}>
                                    <Chip
                                      label={r.jobOrderCode || "—"}
                                      size="small"
                                      sx={{
                                        fontWeight: 700,
                                        fontFamily: "ui-monospace, monospace",
                                        bgcolor: alpha(theme.palette.primary.main, 0.1),
                                        borderColor: alpha(theme.palette.primary.main, 0.35),
                                        borderWidth: 1,
                                        borderStyle: "solid",
                                        "&:hover": {
                                          bgcolor: alpha(theme.palette.primary.main, 0.18),
                                          borderColor: alpha(theme.palette.primary.main, 0.55),
                                        },
                                      }}
                                      variant="outlined"
                                    />
                                    <Stack direction="row" alignItems="center" spacing={0.75}>
                                      <Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
                                        序
                                      </Typography>
                                      <Avatar
                                        sx={{
                                          width: 28,
                                          height: 28,
                                          fontSize: "0.72rem",
                                          fontWeight: 700,
                                          bgcolor: procColor,
                                          color: "#fff",
                                          boxShadow: `0 0 0 2px ${alpha(procColor, 0.25)}`,
                                        }}
                                      >
                                        {r.seqNo}
                                      </Avatar>
                                    </Stack>
                                  </Stack>
                                </Link>
                              </Tooltip>
                            </TableCell>
                            <TableCell sx={{ minWidth: 648 }}>
                              <ProcessBoardJoSummary r={r} />
                            </TableCell>
                            <TableCell>
                              <StepPlanActualMins plan={r.stepPlanMins} actual={r.stepActualMins} />
                            </TableCell>
                            <TableCell sx={{ minWidth: 139, maxWidth: 139 }}>
                              {nameDescriptionCell(r.lineStepName, r.lineDescription, 128)}
                            </TableCell>
                            <TableCell>{processFlowTextCell(r.lineEquipmentLabel, 280)}</TableCell>
                            <TableCell sx={{ minWidth: 200 }}>{processFlowTextCell(r.lineOperatorInfo, 260)}</TableCell>
                            <TableCell sx={{ minWidth: 200 }}>
                              <Stack spacing={0.75}>
                                <Box>
                                  <Typography variant="caption" color="text.secondary" display="block" sx={{ lineHeight: 1.2 }}>
                                    開工
                                  </Typography>
                                  {timeCell(r.startTime)}
                                </Box>
                                <Box>
                                  <Typography variant="caption" color="text.secondary" display="block" sx={{ lineHeight: 1.2 }}>
                                    完工
                                  </Typography>
                                  {timeCell(r.endTime)}
                                </Box>
                              </Stack>
                            </TableCell>
                          </TableRow>
                        );
                      })}
                    </TableBody>
                  </Table>
                </HorizontalScrollSync>
              </Paper>
            );
          })}
        </Stack>
      )}
    </Box>
  );
}
