"use client";

import React, { useEffect, useState, useMemo, useCallback, useRef } from "react";
import {
  Loader2, Play, Pause, CheckCircle2, AlertCircle, Timer, Package,
  ChevronLeft, ChevronRight, Check, X, CircleDot, ClipboardList,
  Plus, Trash2, Save, FileCheck, Construction,
} from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import { Badge } from "@/components/ui/badge";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { dataApi } from "@/lib/api/data";
import { apiClient } from "@/lib/api/client";
import { usePopEvent } from "@/hooks/pop/usePopEvent";
import { useAuth } from "@/hooks/useAuth";
import type { PopWorkDetailConfig, ResultSectionConfig, ResultSectionType } from "../types";
import type { TimelineProcessStep } from "../types";

// ========================================
// 타입
// ========================================

type RowData = Record<string, unknown>;

interface WorkResultRow {
  id: string;
  work_order_process_id: string;
  source_work_item_id: string;
  source_detail_id: string;
  work_phase: string;
  item_title: string;
  item_sort_order: string;
  detail_type: string;
  detail_label: string | null;
  detail_content: string;
  detail_sort_order: string;
  spec_value: string | null;
  lower_limit: string | null;
  upper_limit: string | null;
  input_type: string | null;
  result_value: string | null;
  status: string;
  is_passed: string | null;
  is_required: string | null;
  recorded_by: string | null;
  recorded_at: string | null;
  started_at: string | null;
  group_started_at: string | null;
  group_paused_at: string | null;
  group_total_paused_time: string | null;
  group_completed_at: string | null;
  inspection_method: string | null;
  unit: string | null;
}

interface GroupTimerState {
  startedAt: string | null;
  pausedAt: string | null;
  totalPausedTime: number;
  completedAt: string | null;
}

interface WorkGroup {
  phase: string;
  title: string;
  itemId: string;
  sortOrder: number;
  total: number;
  completed: number;
  stepStatus: "pending" | "active" | "completed";
  timer: GroupTimerState;
}

type WorkPhase = "PRE" | "IN" | "POST";
const PHASE_ORDER: Record<string, number> = { PRE: 1, IN: 2, POST: 3 };

interface ProcessTimerData {
  started_at: string | null;
  paused_at: string | null;
  total_paused_time: string | null;
  completed_at: string | null;
  completed_by: string | null;
  actual_work_time: string | null;
  status: string;
  good_qty: string | null;
  defect_qty: string | null;
  total_production_qty: string | null;
  defect_detail: string | null;
  result_note: string | null;
  result_status: string | null;
  input_qty: string | null;
}

interface DefectDetailEntry {
  defect_code: string;
  defect_name: string;
  qty: string;
  disposition: string;
}

interface DefectTypeOption {
  id: string;
  defect_code: string;
  defect_name: string;
  defect_type: string;
  severity: string;
}

const DEFAULT_INFO_FIELDS = [
  { label: "작업지시", column: "wo_no" },
  { label: "품목", column: "item_name" },
  { label: "공정", column: "__process_name" },
  { label: "지시수량", column: "qty" },
];

const DEFAULT_CFG: PopWorkDetailConfig = {
  showTimer: true,
  showQuantityInput: false,
  displayMode: "list",
  phaseLabels: { PRE: "작업 전", IN: "작업 중", POST: "작업 후" },
  infoBar: { enabled: true, fields: [] },
  stepControl: { requireStartBeforeInput: false, autoAdvance: true },
  navigation: { showPrevNext: true, showCompleteButton: true },
  resultSections: [
    { id: "total-qty", type: "total-qty", enabled: true, showCondition: { type: "always" } },
    { id: "good-defect", type: "good-defect", enabled: true, showCondition: { type: "always" } },
    { id: "defect-types", type: "defect-types", enabled: true, showCondition: { type: "always" } },
    { id: "note", type: "note", enabled: true, showCondition: { type: "always" } },
  ],
};

// ========================================
// Props
// ========================================

interface PopWorkDetailComponentProps {
  config?: PopWorkDetailConfig;
  screenId?: string;
  componentId?: string;
  currentRowSpan?: number;
  currentColSpan?: number;
  parentRow?: RowData;
}

// ========================================
// 메인 컴포넌트
// ========================================

export function PopWorkDetailComponent({
  config,
  screenId,
  parentRow: parentRowProp,
}: PopWorkDetailComponentProps) {
  const { getSharedData, publish } = usePopEvent(screenId || "default");
  const { user } = useAuth();

  const cfg: PopWorkDetailConfig = {
    ...DEFAULT_CFG,
    ...config,
    displayMode: config?.displayMode ?? DEFAULT_CFG.displayMode,
    infoBar: { ...DEFAULT_CFG.infoBar, ...config?.infoBar },
    stepControl: { ...DEFAULT_CFG.stepControl, ...config?.stepControl },
    navigation: { ...DEFAULT_CFG.navigation, ...config?.navigation },
    phaseLabels: { ...DEFAULT_CFG.phaseLabels, ...config?.phaseLabels },
  };

  const parentRow = parentRowProp ?? getSharedData<RowData>("parentRow");
  const processFlow = parentRow?.__processFlow__ as TimelineProcessStep[] | undefined;
  const currentProcess = processFlow?.find((p) => p.isCurrent);
  const workOrderProcessId = parentRow?.__splitProcessId
    ? String(parentRow.__splitProcessId)
    : parentRow?.__process_id
      ? String(parentRow.__process_id)
      : currentProcess?.processId
        ? String(currentProcess.processId)
        : undefined;
  const processName = currentProcess?.processName ?? "공정 상세";

  // ========================================
  // 상태
  // ========================================

  const [allResults, setAllResults] = useState<WorkResultRow[]>([]);
  const [processData, setProcessData] = useState<ProcessTimerData | null>(null);
  const [loading, setLoading] = useState(true);
  const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null);
  const [tick, setTick] = useState(Date.now());
  const [savingIds, setSavingIds] = useState<Set<string>>(new Set());
  const [activeStepIds, setActiveStepIds] = useState<Set<string>>(new Set());

  const [goodQty, setGoodQty] = useState("");
  const [defectQty, setDefectQty] = useState("");
  const [currentItemIdx, setCurrentItemIdx] = useState(0);
  const [showQuantityPanel, setShowQuantityPanel] = useState(false);

  // 실적 입력 탭 상태
  const [resultTabActive, setResultTabActive] = useState(false);
  const hasResultSections = !!(cfg.resultSections && cfg.resultSections.some((s) => s.enabled));

  // 불량 유형 목록 (부모에서 1회 로드, ResultPanel에 전달)
  const [cachedDefectTypes, setCachedDefectTypes] = useState<DefectTypeOption[]>([]);

  const contentRef = useRef<HTMLDivElement>(null);

  // ========================================
  // 데이터 로드
  // ========================================

  const fetchData = useCallback(async () => {
    if (!workOrderProcessId) {
      setLoading(false);
      return;
    }
    try {
      setLoading(true);
      const [resultRes, processRes] = await Promise.all([
        dataApi.getTableData("process_work_result", {
          size: 500,
          filters: { work_order_process_id: workOrderProcessId },
        }),
        dataApi.getTableData("work_order_process", {
          size: 1,
          filters: { id: workOrderProcessId },
        }),
      ]);
      setAllResults((resultRes.data ?? []) as unknown as WorkResultRow[]);
      const proc = (processRes.data?.[0] ?? null) as ProcessTimerData | null;
      setProcessData(proc);
      if (proc) {
        setGoodQty(proc.good_qty ?? "");
        setDefectQty(proc.defect_qty ?? "");
      }
    } catch {
      toast.error("데이터를 불러오는데 실패했습니다.");
    } finally {
      setLoading(false);
    }
  }, [workOrderProcessId]);

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

  useEffect(() => {
    const loadDefectTypes = async () => {
      try {
        const res = await apiClient.get("/pop/production/defect-types");
        if (res.data?.success) {
          setCachedDefectTypes(res.data.data || []);
        }
      } catch { /* 실패 시 빈 배열 유지 */ }
    };
    loadDefectTypes();
  }, []);

  // ========================================
  // 좌측 사이드바 - 작업항목 그룹핑
  // ========================================

  const groups = useMemo<WorkGroup[]>(() => {
    const map = new Map<string, WorkGroup>();
    for (const row of allResults) {
      const key = row.source_work_item_id;
      if (!map.has(key)) {
        map.set(key, {
          phase: row.work_phase,
          title: row.item_title,
          itemId: key,
          sortOrder: parseInt(row.item_sort_order || "0", 10),
          total: 0,
          completed: 0,
          stepStatus: "pending",
          timer: {
            startedAt: row.group_started_at ?? null,
            pausedAt: row.group_paused_at ?? null,
            totalPausedTime: parseInt(row.group_total_paused_time || "0", 10),
            completedAt: row.group_completed_at ?? null,
          },
        });
      }
      const g = map.get(key)!;
      g.total++;
      if (row.status === "completed") g.completed++;
    }
    const arr = Array.from(map.values()).sort(
      (a, b) =>
        (PHASE_ORDER[a.phase] ?? 9) - (PHASE_ORDER[b.phase] ?? 9) ||
        a.sortOrder - b.sortOrder
    );
    for (const g of arr) {
      if (g.completed >= g.total && g.total > 0) {
        g.stepStatus = "completed";
      } else if (activeStepIds.has(g.itemId) || g.timer.startedAt) {
        g.stepStatus = "active";
      }
    }
    return arr;
  }, [allResults, activeStepIds]);

  const groupsByPhase = useMemo(() => {
    const result: Record<string, WorkGroup[]> = {};
    for (const g of groups) {
      if (!result[g.phase]) result[g.phase] = [];
      result[g.phase].push(g);
    }
    return result;
  }, [groups]);

  useEffect(() => {
    if (groups.length > 0 && !selectedGroupId) {
      setSelectedGroupId(groups[0].itemId);
    } else if (groups.length === 0 && hasResultSections && !resultTabActive) {
      setResultTabActive(true);
    }
  }, [groups, selectedGroupId, hasResultSections, resultTabActive]);

  // 현재 선택 인덱스
  const selectedIndex = useMemo(
    () => groups.findIndex((g) => g.itemId === selectedGroupId),
    [groups, selectedGroupId]
  );

  // ========================================
  // 네비게이션
  // ========================================

  const navigateStep = useCallback(
    (delta: number) => {
      const nextIdx = selectedIndex + delta;
      if (nextIdx >= 0 && nextIdx < groups.length) {
        setSelectedGroupId(groups[nextIdx].itemId);
        contentRef.current?.scrollTo({ top: 0, behavior: "smooth" });
      }
    },
    [selectedIndex, groups]
  );

  // ========================================
  // 우측 체크리스트
  // ========================================

  const currentItems = useMemo(
    () =>
      allResults
        .filter((r) => r.source_work_item_id === selectedGroupId)
        .sort(
          (a, b) =>
            parseInt(a.detail_sort_order || "0", 10) -
            parseInt(b.detail_sort_order || "0", 10)
        ),
    [allResults, selectedGroupId]
  );

  // 스텝 모드: 전체 항목을 flat 리스트로 정렬
  const flatItems = useMemo(() => {
    const sorted = [...allResults].sort(
      (a, b) =>
        (PHASE_ORDER[a.work_phase] ?? 9) - (PHASE_ORDER[b.work_phase] ?? 9) ||
        parseInt(a.item_sort_order || "0", 10) - parseInt(b.item_sort_order || "0", 10) ||
        parseInt(a.detail_sort_order || "0", 10) - parseInt(b.detail_sort_order || "0", 10)
    );
    return sorted;
  }, [allResults]);

  // 스텝 모드: 모든 체크리스트 항목 완료 여부
  const allItemsCompleted = useMemo(
    () => flatItems.length > 0 && flatItems.every((r) => r.status === "completed"),
    [flatItems]
  );

  // 그룹 선택 변경 시 스텝 인덱스 리셋
  useEffect(() => {
    setCurrentItemIdx(0);
  }, [selectedGroupId]);

  // 스텝 모드 자동 다음 이동
  const stepAutoAdvance = useCallback(() => {
    if (cfg.displayMode !== "step") return;
    const nextPendingIdx = currentItems.findIndex(
      (r, i) => i > currentItemIdx && r.status !== "completed"
    );
    if (nextPendingIdx >= 0) {
      setCurrentItemIdx(nextPendingIdx);
    } else if (currentItems.every((r) => r.status === "completed")) {
      // 현재 그룹 완료 → 다음 그룹
      const nextGroupIdx = groups.findIndex(
        (g, i) => i > selectedIndex && g.stepStatus !== "completed"
      );
      if (nextGroupIdx >= 0) {
        setSelectedGroupId(groups[nextGroupIdx].itemId);
        toast.success(`${groups[selectedIndex]?.title} 완료`);
      } else {
        setShowQuantityPanel(true);
      }
    }
  }, [cfg.displayMode, currentItems, currentItemIdx, groups, selectedIndex, selectedGroupId]);

  const saveResultValue = useCallback(
    async (
      rowId: string,
      resultValue: string,
      isPassed: string | null,
      newStatus: string
    ) => {
      setSavingIds((prev) => new Set(prev).add(rowId));
      try {
        const existingRow = allResults.find((r) => r.id === rowId);
        const isFirstTouch = existingRow && !existingRow.started_at;
        const now = new Date().toISOString();

        const mkTask = (col: string, val: string) => ({
          type: "data-update" as const,
          targetTable: "process_work_result",
          targetColumn: col,
          operationType: "assign" as const,
          valueSource: "fixed" as const,
          fixedValue: val,
          lookupMode: "manual" as const,
          manualItemField: "id",
          manualPkColumn: "id",
        });

        const tasks = [
          mkTask("result_value", resultValue),
          mkTask("status", newStatus),
          ...(isPassed !== null ? [mkTask("is_passed", isPassed)] : []),
          mkTask("recorded_by", user?.userId ?? ""),
          mkTask("recorded_at", now),
          ...(isFirstTouch ? [mkTask("started_at", now)] : []),
        ];

        await apiClient.post("/pop/execute-action", {
          tasks,
          data: { items: [{ id: rowId }], fieldValues: {} },
        });

        setAllResults((prev) =>
          prev.map((r) =>
            r.id === rowId
              ? {
                  ...r,
                  result_value: resultValue,
                  status: newStatus,
                  is_passed: isPassed,
                  recorded_by: user?.userId ?? null,
                  recorded_at: now,
                  started_at: r.started_at ?? now,
                }
              : r
          )
        );

        if (cfg.stepControl.autoAdvance && newStatus === "completed") {
          setTimeout(() => {
            if (cfg.displayMode === "step") {
              stepAutoAdvance();
            } else {
              checkAutoAdvance();
            }
          }, 300);
        }
      } catch {
        toast.error("저장에 실패했습니다.");
      } finally {
        setSavingIds((prev) => {
          const next = new Set(prev);
          next.delete(rowId);
          return next;
        });
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [user?.userId, cfg.stepControl.autoAdvance, allResults]
  );

  const checkAutoAdvance = useCallback(() => {
    if (!selectedGroupId) return;
    const groupItems = allResults.filter(
      (r) => r.source_work_item_id === selectedGroupId
    );
    const requiredItems = groupItems.filter((r) => r.is_required === "Y");
    const allRequiredDone = requiredItems.length > 0 && requiredItems.every((r) => r.status === "completed");
    const allDone = groupItems.every((r) => r.status === "completed");

    if (allRequiredDone || allDone) {
      const idx = groups.findIndex((g) => g.itemId === selectedGroupId);
      if (idx >= 0 && idx < groups.length - 1) {
        const nextGroup = groups[idx + 1];
        setSelectedGroupId(nextGroup.itemId);
        contentRef.current?.scrollTo({ top: 0, behavior: "smooth" });
        toast.success(`${groups[idx].title} 완료 → ${nextGroup.title}`);
      }
    }
  }, [selectedGroupId, allResults, groups]);

  // ========================================
  // 단계 시작/활성화
  // ========================================

  const isStepLocked = useMemo(() => {
    if (!cfg.stepControl.requireStartBeforeInput) return false;
    if (!selectedGroupId) return true;
    return !activeStepIds.has(selectedGroupId);
  }, [cfg.stepControl.requireStartBeforeInput, selectedGroupId, activeStepIds]);

  // ========================================
  // 프로세스 타이머 (전체 공정용)
  // ========================================

  useEffect(() => {
    if (!cfg.showTimer) return;
    const hasActiveGroupTimer = groups.some(
      (g) => g.timer.startedAt && !g.timer.completedAt
    );
    if (!hasActiveGroupTimer && !processData?.started_at) return;
    const id = setInterval(() => setTick(Date.now()), 1000);
    return () => clearInterval(id);
  }, [cfg.showTimer, processData?.started_at, groups]);

  // 프로세스 레벨 타이머는 그룹별 타이머로 대체됨

  // ========================================
  // 그룹별 타이머
  // ========================================

  const selectedGroupTimer = useMemo(() => {
    const g = groups.find((g) => g.itemId === selectedGroupId);
    return g?.timer ?? { startedAt: null, pausedAt: null, totalPausedTime: 0, completedAt: null };
  }, [groups, selectedGroupId]);

  // 그룹 타이머: 순수 작업시간 (일시정지 제외)
  const groupWorkMs = useMemo(() => {
    if (!selectedGroupTimer.startedAt) return 0;
    const end = selectedGroupTimer.completedAt
      ? new Date(selectedGroupTimer.completedAt).getTime()
      : tick;
    const start = new Date(selectedGroupTimer.startedAt).getTime();
    const totalMs = end - start;
    const pausedMs = selectedGroupTimer.totalPausedTime * 1000;
    const currentPauseMs = selectedGroupTimer.pausedAt
      ? (selectedGroupTimer.completedAt ? 0 : tick - new Date(selectedGroupTimer.pausedAt).getTime())
      : 0;
    return Math.max(0, totalMs - pausedMs - currentPauseMs);
  }, [selectedGroupTimer, tick]);

  // 그룹 타이머: 경과 시간 (일시정지 무시, 시작~끝)
  const groupElapsedMs = useMemo(() => {
    if (!selectedGroupTimer.startedAt) return 0;
    const end = selectedGroupTimer.completedAt
      ? new Date(selectedGroupTimer.completedAt).getTime()
      : tick;
    const start = new Date(selectedGroupTimer.startedAt).getTime();
    return Math.max(0, end - start);
  }, [selectedGroupTimer, tick]);

  const groupTimerFormatted = useMemo(() => formatMsToTime(groupWorkMs), [groupWorkMs]);
  const groupElapsedFormatted = useMemo(() => formatMsToTime(groupElapsedMs), [groupElapsedMs]);

  const isGroupStarted = !!selectedGroupTimer.startedAt;
  const isGroupPaused = !!selectedGroupTimer.pausedAt;
  const isGroupCompleted = !!selectedGroupTimer.completedAt;

  const handleGroupTimerAction = useCallback(
    async (action: "start" | "pause" | "resume" | "complete") => {
      if (!workOrderProcessId || !selectedGroupId) return;
      try {
        await apiClient.post("/pop/production/group-timer", {
          work_order_process_id: workOrderProcessId,
          source_work_item_id: selectedGroupId,
          action,
        });
        await fetchData();
      } catch {
        toast.error("그룹 타이머 제어에 실패했습니다.");
      }
    },
    [workOrderProcessId, selectedGroupId, fetchData]
  );

  const handleTimerAction = useCallback(
    async (action: "start" | "pause" | "resume" | "complete") => {
      if (!workOrderProcessId) return;
      try {
        const body: Record<string, unknown> = {
          work_order_process_id: workOrderProcessId,
          action,
        };
        if (action === "complete") {
          body.good_qty = goodQty || "0";
          body.defect_qty = defectQty || "0";
        }
        await apiClient.post("/pop/production/timer", body);
        const res = await dataApi.getTableData("work_order_process", {
          size: 1,
          filters: { id: workOrderProcessId },
        });
        const proc = (res.data?.[0] ?? null) as ProcessTimerData | null;
        if (proc) {
          setProcessData(proc);
          if (action === "complete") {
            toast.success("공정이 완료되었습니다.");
            publish("process_completed", { workOrderProcessId, goodQty, defectQty });
          }
        }
      } catch {
        toast.error("타이머 제어에 실패했습니다.");
      }
    },
    [workOrderProcessId, goodQty, defectQty, publish]
  );

  // ========================================
  // 수량 등록 + 완료
  // ========================================

  const handleQuantityRegister = useCallback(async () => {
    if (!workOrderProcessId) return;
    try {
      const mkWopTask = (col: string, val: string) => ({
        type: "data-update" as const,
        targetTable: "work_order_process",
        targetColumn: col,
        operationType: "assign" as const,
        valueSource: "fixed" as const,
        fixedValue: val,
        lookupMode: "manual" as const,
        manualItemField: "id",
        manualPkColumn: "id",
      });
      await apiClient.post("/pop/execute-action", {
        tasks: [
          mkWopTask("good_qty", goodQty || "0"),
          mkWopTask("defect_qty", defectQty || "0"),
        ],
        data: { items: [{ id: workOrderProcessId }], fieldValues: {} },
      });
      toast.success("수량이 등록되었습니다.");
    } catch {
      toast.error("수량 등록에 실패했습니다.");
    }
  }, [workOrderProcessId, goodQty, defectQty]);

  const handleProcessComplete = useCallback(async () => {
    await handleTimerAction("complete");
  }, [handleTimerAction]);

  // ========================================
  // 안전 장치
  // ========================================

  if (!parentRow) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <AlertCircle className="mr-2 h-4 w-4" />
        카드를 선택해주세요
      </div>
    );
  }

  if (!workOrderProcessId) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <AlertCircle className="mr-2 h-4 w-4" />
        공정 정보를 찾을 수 없습니다
      </div>
    );
  }

  if (loading) {
    return (
      <div className="flex h-full items-center justify-center">
        <Loader2 className="h-6 w-6 animate-spin text-primary" />
      </div>
    );
  }

  const isProcessCompleted = processData?.status === "completed";

  if (allResults.length === 0 && !hasResultSections) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <AlertCircle className="mr-2 h-4 w-4" />
        작업기준이 등록되지 않았습니다
      </div>
    );
  }
  const selectedGroup = groups.find((g) => g.itemId === selectedGroupId);

  // ========================================
  // 렌더링
  // ========================================

  return (
    <div className="flex h-full flex-col">
      {/* 작업지시 정보 바 */}
      {cfg.infoBar.enabled && (
        <InfoBar
          fields={cfg.infoBar.fields.length > 0 ? cfg.infoBar.fields : DEFAULT_INFO_FIELDS}
          parentRow={parentRow}
          processName={processName}
        />
      )}

      {/* 전체 공정 진행 요약은 제거 - 타이머는 그룹 헤더로 이동 */}

      {/* 본문: 좌측 사이드바 + 우측 콘텐츠 */}
      <div className="flex flex-1 overflow-hidden">
        {/* 좌측 사이드바 */}
        <div className="w-52 shrink-0 overflow-y-auto border-r bg-muted/30">
          {(["PRE", "IN", "POST"] as WorkPhase[]).map((phase) => {
            const phaseGroups = groupsByPhase[phase];
            if (!phaseGroups || phaseGroups.length === 0) return null;
            return (
              <div key={phase}>
                <div className="px-4 pb-1 pt-3 text-xs font-semibold uppercase text-muted-foreground">
                  {cfg.phaseLabels[phase] ?? phase}
                </div>
                {phaseGroups.map((g) => (
                  <button
                    key={g.itemId}
                    className={cn(
                      "flex w-full items-center gap-2.5 px-4 py-3 text-left transition-colors",
                      selectedGroupId === g.itemId
                        ? "bg-primary/10 text-primary border-l-2 border-primary"
                        : "hover:bg-muted/60"
                    )}
                    onClick={() => {
                      setSelectedGroupId(g.itemId);
                      setResultTabActive(false);
                      contentRef.current?.scrollTo({ top: 0, behavior: "smooth" });
                    }}
                  >
                    <StepStatusIcon status={g.stepStatus} />
                    <div className="flex-1 min-w-0">
                      <span className="block truncate text-sm font-medium leading-tight">
                        {g.title}
                      </span>
                      <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
                        <span>{g.completed}/{g.total}</span>
                        {g.timer.startedAt && !g.timer.completedAt && (
                          <span className="text-primary">진행중</span>
                        )}
                        {g.timer.completedAt && (
                          <span className="text-green-600">완료</span>
                        )}
                      </div>
                    </div>
                  </button>
                ))}
              </div>
            );
          })}

          {/* 실적 입력 탭 (resultSections가 설정된 경우만) */}
          {cfg.resultSections && cfg.resultSections.some((s) => s.enabled) && (
            <>
              <div className="mx-3 my-2 border-t" />
              <button
                className={cn(
                  "flex w-full items-center gap-2.5 px-4 py-3 text-left transition-colors",
                  resultTabActive
                    ? "bg-primary/10 text-primary border-l-2 border-primary"
                    : "hover:bg-muted/60"
                )}
                onClick={() => {
                  setResultTabActive(true);
                  setSelectedGroupId(null);
                  contentRef.current?.scrollTo({ top: 0, behavior: "smooth" });
                }}
              >
                <ClipboardList className="h-4 w-4 shrink-0" />
                <div className="flex-1 min-w-0">
                  <span className="block truncate text-sm font-medium leading-tight">
                    실적 입력
                  </span>
                  <span className="text-xs text-muted-foreground">
                    {processData?.result_status === "confirmed" ? "확정됨" : "미확정"}
                  </span>
                </div>
              </button>
            </>
          )}
        </div>

        {/* 우측 콘텐츠 */}
        <div className="flex flex-1 flex-col overflow-hidden">
          {/* 실적 입력 패널 (hidden으로 상태 유지, unmount 방지) */}
          {hasResultSections && (
            <div className={cn("flex flex-1 flex-col overflow-hidden", !resultTabActive && "hidden")}>
              <ResultPanel
                workOrderProcessId={workOrderProcessId!}
                processData={processData}
                sections={cfg.resultSections ?? []}
                isProcessCompleted={isProcessCompleted}
                defectTypes={cachedDefectTypes}
                onSaved={(updated) => {
                  setProcessData((prev) => prev ? { ...prev, ...updated } : prev);
                  publish("process_completed", { workOrderProcessId, status: updated?.status });
                }}
              />
            </div>
          )}

          {/* 체크리스트 영역 */}
          <div className={cn("flex flex-1 flex-col overflow-hidden", resultTabActive && "hidden")}>
          {cfg.displayMode === "step" ? (
            /* ======== 스텝 모드 ======== */
            <>
              {showQuantityPanel || allItemsCompleted ? (
                /* 수량 등록 + 공정 완료 화면 */
                <div ref={contentRef} className="flex flex-1 flex-col items-center justify-center gap-5 overflow-y-auto p-6">
                  <CheckCircle2 className="h-12 w-12 text-green-600" />
                  <p className="text-base font-medium">모든 작업 항목이 완료되었습니다</p>

                  {cfg.showQuantityInput && !isProcessCompleted && !hasResultSections && (
                    <div className="w-full max-w-sm space-y-4 rounded-lg border p-5">
                      <p className="text-sm font-semibold">실적 수량 등록</p>
                      <div className="space-y-3">
                        <div className="flex items-center gap-3">
                          <span className="w-12 text-sm text-muted-foreground">양품</span>
                          <Input type="number" className="h-11 text-base" value={goodQty} onChange={(e) => setGoodQty(e.target.value)} placeholder="0" />
                        </div>
                        <div className="flex items-center gap-3">
                          <span className="w-12 text-sm text-muted-foreground">불량</span>
                          <Input type="number" className="h-11 text-base" value={defectQty} onChange={(e) => setDefectQty(e.target.value)} placeholder="0" />
                        </div>
                        {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0) > 0 && (
                          <p className="text-right text-sm text-muted-foreground">
                            합계: {(parseInt(goodQty, 10) || 0) + (parseInt(defectQty, 10) || 0)}
                          </p>
                        )}
                      </div>
                    </div>
                  )}

                  {!isProcessCompleted && cfg.navigation.showCompleteButton && (
                    <Button
                      className="h-12 gap-2 px-8 text-base"
                      onClick={handleProcessComplete}
                    >
                      <CheckCircle2 className="h-5 w-5" />공정 완료
                    </Button>
                  )}

                  {isProcessCompleted && (
                    <Badge variant="outline" className="text-base text-green-600 px-3 py-1">
                      공정이 완료되었습니다
                    </Badge>
                  )}
                </div>
              ) : (
                /* 단계별 항목 표시 */
                <>
                  {/* 그룹 헤더 + 타이머 + 진행률 */}
                  {selectedGroup && (
                    <>
                      <GroupTimerHeader
                        group={selectedGroup}
                        cfg={cfg}
                        isProcessCompleted={isProcessCompleted}
                        isGroupStarted={isGroupStarted}
                        isGroupPaused={isGroupPaused}
                        isGroupCompleted={isGroupCompleted}
                        groupTimerFormatted={groupTimerFormatted}
                        groupElapsedFormatted={groupElapsedFormatted}
                        onTimerAction={handleGroupTimerAction}
                      />
                      <div className="px-4 pb-2">
                        <div className="flex items-center justify-between text-xs text-muted-foreground">
                          <span>{currentItemIdx + 1} / {currentItems.length}</span>
                        </div>
                        <div className="mt-1 h-2 w-full overflow-hidden rounded-full bg-muted">
                          <div
                            className="h-full bg-primary transition-all duration-300"
                            style={{
                              width: `${currentItems.length > 0 && selectedGroup.total > 0 ? ((selectedGroup.completed / selectedGroup.total) * 100) : 0}%`,
                            }}
                          />
                        </div>
                      </div>
                    </>
                  )}

                  {/* 현재 항목 1개 표시 */}
                  <div ref={contentRef} className="flex flex-1 flex-col overflow-y-auto p-5">
                    {currentItems[currentItemIdx] && (
                      <div className="mx-auto w-full max-w-lg space-y-3">
                        {currentItems[currentItemIdx].started_at && (
                          <div className="flex items-center justify-end gap-1.5 text-xs text-muted-foreground">
                            <Timer className="h-3.5 w-3.5" />
                            {currentItems[currentItemIdx].recorded_at
                              ? formatDuration(currentItems[currentItemIdx].started_at!, currentItems[currentItemIdx].recorded_at!)
                              : "진행 중..."}
                          </div>
                        )}
                        <ChecklistItem
                          item={currentItems[currentItemIdx]}
                          saving={savingIds.has(currentItems[currentItemIdx].id)}
                          disabled={isProcessCompleted || isStepLocked}
                          onSave={saveResultValue}
                        />
                      </div>
                    )}
                  </div>

                  {/* 스텝 네비게이션 */}
                  <div className="flex items-center justify-between border-t px-4 py-3">
                    <Button
                      size="sm"
                      variant="outline"
                      className="h-11 gap-1.5 px-5 text-sm"
                      disabled={currentItemIdx <= 0 && selectedIndex <= 0}
                      onClick={() => {
                        if (currentItemIdx > 0) {
                          setCurrentItemIdx(currentItemIdx - 1);
                        } else if (selectedIndex > 0) {
                          const prevGroup = groups[selectedIndex - 1];
                          setSelectedGroupId(prevGroup.itemId);
                          const prevItems = allResults.filter(
                            (r) => r.source_work_item_id === prevGroup.itemId
                          );
                          setCurrentItemIdx(Math.max(0, prevItems.length - 1));
                        }
                      }}
                    >
                      <ChevronLeft className="h-4 w-4" />이전
                    </Button>

                    <span className="text-xs text-muted-foreground">
                      {selectedGroup?.title} ({currentItemIdx + 1}/{currentItems.length})
                    </span>

                    <Button
                      size="sm"
                      variant={currentItemIdx >= currentItems.length - 1 && selectedIndex >= groups.length - 1 ? "default" : "outline"}
                      className="h-11 gap-1.5 px-5 text-sm"
                      onClick={() => {
                        if (currentItemIdx < currentItems.length - 1) {
                          setCurrentItemIdx(currentItemIdx + 1);
                        } else if (selectedIndex < groups.length - 1) {
                          navigateStep(1);
                        } else {
                          setShowQuantityPanel(true);
                        }
                      }}
                    >
                      {currentItemIdx >= currentItems.length - 1 && selectedIndex >= groups.length - 1 ? (
                        <>수량 등록<ChevronRight className="h-4 w-4" /></>
                      ) : (
                        <>다음<ChevronRight className="h-4 w-4" /></>
                      )}
                    </Button>
                  </div>
                </>
              )}
            </>
          ) : (
            /* ======== 리스트 모드 (기존) ======== */
            <>
              {/* 그룹 헤더 + 타이머 */}
              {selectedGroup && (
                <GroupTimerHeader
                  group={selectedGroup}
                  cfg={cfg}
                  isProcessCompleted={isProcessCompleted}
                  isGroupStarted={isGroupStarted}
                  isGroupPaused={isGroupPaused}
                  isGroupCompleted={isGroupCompleted}
                  groupTimerFormatted={groupTimerFormatted}
                  groupElapsedFormatted={groupElapsedFormatted}
                  onTimerAction={handleGroupTimerAction}
                />
              )}

              {/* 체크리스트 콘텐츠 */}
              <div ref={contentRef} className="flex-1 overflow-y-auto p-4">
                {selectedGroupId && (
                  <div className="space-y-2.5">
                    {currentItems.map((item) => (
                      <ChecklistItem
                        key={item.id}
                        item={item}
                        saving={savingIds.has(item.id)}
                        disabled={isProcessCompleted || isStepLocked}
                        onSave={saveResultValue}
                      />
                    ))}
                  </div>
                )}
              </div>

              {/* 하단 네비게이션 + 수량/완료 */}
              <div className="border-t">
                {cfg.showQuantityInput && !hasResultSections && (
                  <div className="flex items-center gap-3 px-4 py-2.5">
                    <Package className="h-4.5 w-4.5 shrink-0 text-muted-foreground" />
                    <div className="flex items-center gap-2">
                      <span className="text-sm text-muted-foreground">양품</span>
                      <Input type="number" className="h-10 w-24 text-sm" value={goodQty} onChange={(e) => setGoodQty(e.target.value)} disabled={isProcessCompleted} />
                    </div>
                    <div className="flex items-center gap-2">
                      <span className="text-sm text-muted-foreground">불량</span>
                      <Input type="number" className="h-10 w-24 text-sm" value={defectQty} onChange={(e) => setDefectQty(e.target.value)} disabled={isProcessCompleted} />
                    </div>
                    <Button size="sm" variant="outline" className="h-10 px-4 text-sm" onClick={handleQuantityRegister} disabled={isProcessCompleted}>
                      등록
                    </Button>
                  </div>
                )}

                {cfg.navigation.showPrevNext && (
                  <div className="flex items-center justify-between px-4 py-2.5">
                    <Button
                      size="sm"
                      variant="outline"
                      className="h-11 gap-1.5 px-5 text-sm"
                      disabled={selectedIndex <= 0}
                      onClick={() => navigateStep(-1)}
                    >
                      <ChevronLeft className="h-4 w-4" />이전
                    </Button>

                    <span className="text-xs text-muted-foreground">
                      {selectedIndex + 1} / {groups.length}
                    </span>

                    {selectedIndex < groups.length - 1 ? (
                      <Button
                        size="sm"
                        variant="outline"
                        className="h-11 gap-1.5 px-5 text-sm"
                        onClick={() => navigateStep(1)}
                      >
                        다음<ChevronRight className="h-4 w-4" />
                      </Button>
                    ) : cfg.navigation.showCompleteButton && !isProcessCompleted ? (
                      <Button
                        size="sm"
                        variant="default"
                        className="h-11 gap-1.5 px-5 text-sm"
                        onClick={handleProcessComplete}
                      >
                        <CheckCircle2 className="h-4 w-4" />공정 완료
                      </Button>
                    ) : (
                      <div className="w-16" />
                    )}
                  </div>
                )}
              </div>
            </>
          )}
          </div>
        </div>
      </div>
    </div>
  );
}

// ========================================
// 실적 입력 패널 (분할 실적 누적 방식)
// ========================================

const DISPOSITION_OPTIONS = [
  { value: "scrap", label: "폐기" },
  { value: "rework", label: "재작업" },
  { value: "accept", label: "특채" },
];

interface BatchHistoryItem {
  seq: number;
  batch_qty: number;
  batch_good: number;
  batch_defect: number;
  accumulated_total: number;
  changed_at: string;
  changed_by: string | null;
}

const IMPLEMENTED_SECTIONS = new Set<ResultSectionType>(["total-qty", "good-defect", "defect-types", "note"]);

const SECTION_LABELS: Record<ResultSectionType, string> = {
  "total-qty": "생산수량",
  "good-defect": "양품/불량",
  "defect-types": "불량 유형 상세",
  "note": "비고",
  "box-packing": "박스 포장",
  "label-print": "라벨 출력",
  "photo": "사진",
  "document": "문서",
  "material-input": "자재 투입",
  "barcode-scan": "바코드 스캔",
  "plc-data": "PLC 데이터",
};

interface ResultPanelProps {
  workOrderProcessId: string;
  processData: ProcessTimerData | null;
  sections: ResultSectionConfig[];
  isProcessCompleted: boolean;
  defectTypes: DefectTypeOption[];
  onSaved: (updated: Partial<ProcessTimerData>) => void;
}

function ResultPanel({
  workOrderProcessId,
  processData,
  sections,
  isProcessCompleted,
  defectTypes,
  onSaved,
}: ResultPanelProps) {
  // 이번 차수 입력값 (누적치가 아닌 이번에 생산한 수량)
  const [batchQty, setBatchQty] = useState("");
  const [batchDefect, setBatchDefect] = useState("");
  const [resultNote, setResultNote] = useState("");
  const [defectEntries, setDefectEntries] = useState<DefectDetailEntry[]>([]);
  const [saving, setSaving] = useState(false);
  const [confirming, setConfirming] = useState(false);
  const [history, setHistory] = useState<BatchHistoryItem[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [availableInfo, setAvailableInfo] = useState<{
    prevGoodQty: number;
    availableQty: number;
    instructionQty: number;
  } | null>(null);

  const isConfirmed = processData?.result_status === "confirmed";

  const inputQty = parseInt(processData?.input_qty ?? "0", 10) || 0;
  const accumulatedTotal = parseInt(processData?.total_production_qty ?? "0", 10) || 0;
  const accumulatedGood = parseInt(processData?.good_qty ?? "0", 10) || 0;
  const accumulatedDefect = parseInt(processData?.defect_qty ?? "0", 10) || 0;
  const remainingQty = Math.max(0, inputQty - accumulatedTotal);

  const batchGood = useMemo(() => {
    const production = parseInt(batchQty, 10) || 0;
    const defect = parseInt(batchDefect, 10) || 0;
    return Math.max(0, production - defect);
  }, [batchQty, batchDefect]);

  const totalDefectFromEntries = useMemo(
    () => defectEntries.reduce((sum, e) => sum + (parseInt(e.qty, 10) || 0), 0),
    [defectEntries]
  );

  useEffect(() => {
    if (totalDefectFromEntries > 0) {
      setBatchDefect(String(totalDefectFromEntries));
    }
  }, [totalDefectFromEntries]);

  const enabledSections = sections.filter((s) => s.enabled);

  // 접수가능량 로드
  const loadAvailableQty = useCallback(async () => {
    try {
      const res = await apiClient.get("/pop/production/available-qty", {
        params: { work_order_process_id: workOrderProcessId },
      });
      if (res.data?.success) {
        setAvailableInfo(res.data.data);
      }
    } catch { /* ignore */ }
  }, [workOrderProcessId]);

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

  // 이력 로드
  const loadHistory = useCallback(async () => {
    setHistoryLoading(true);
    try {
      const res = await apiClient.get("/pop/production/result-history", {
        params: { work_order_process_id: workOrderProcessId },
      });
      if (res.data?.success) {
        setHistory(res.data.data || []);
      }
    } catch { /* 실패 시 빈 배열 유지 */ }
    finally { setHistoryLoading(false); }
  }, [workOrderProcessId]);

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

  const addDefectEntry = () => {
    setDefectEntries((prev) => [
      ...prev,
      { defect_code: "", defect_name: "", qty: "", disposition: "scrap" },
    ]);
  };

  const removeDefectEntry = (idx: number) => {
    setDefectEntries((prev) => prev.filter((_, i) => i !== idx));
  };

  const updateDefectEntry = (idx: number, field: keyof DefectDetailEntry, value: string) => {
    setDefectEntries((prev) =>
      prev.map((e, i) => {
        if (i !== idx) return e;
        const updated = { ...e, [field]: value };
        if (field === "defect_code") {
          const found = defectTypes.find((dt) => dt.defect_code === value);
          if (found) updated.defect_name = found.defect_name;
        }
        return updated;
      })
    );
  };

  const resetForm = () => {
    setBatchQty("");
    setBatchDefect("");
    setResultNote("");
    setDefectEntries([]);
  };

  const handleSubmitBatch = async () => {
    if (!batchQty || parseInt(batchQty, 10) <= 0) {
      toast.error("생산수량을 입력해주세요.");
      return;
    }
    const batchNum = parseInt(batchQty, 10);
    if (inputQty > 0 && (accumulatedTotal + batchNum) > inputQty) {
      toast.error(`생산수량(${batchNum})이 잔여량(${remainingQty})을 초과합니다.`);
      return;
    }
    setSaving(true);
    try {
      const res = await apiClient.post("/pop/production/save-result", {
        work_order_process_id: workOrderProcessId,
        production_qty: batchQty,
        good_qty: String(batchGood),
        defect_qty: batchDefect || "0",
        defect_detail: defectEntries.length > 0 ? defectEntries : null,
        result_note: resultNote || null,
      });
      if (res.data?.success) {
        const savedData = res.data.data;
        if (savedData?.status === "completed") {
          toast.success("모든 수량이 완료되어 자동 확정되었습니다.");
        } else {
          toast.success(`${batchQty}개 실적이 등록되었습니다.`);
        }
        onSaved(savedData);
        resetForm();
        loadHistory();
        loadAvailableQty();
      } else {
        toast.error(res.data?.message || "실적 등록에 실패했습니다.");
      }
    } catch {
      toast.error("실적 등록 중 오류가 발생했습니다.");
    } finally {
      setSaving(false);
    }
  };

  const handleConfirm = async () => {
    if (accumulatedTotal <= 0) {
      toast.error("등록된 실적이 없습니다.");
      return;
    }
    setConfirming(true);
    try {
      const res = await apiClient.post("/pop/production/confirm-result", {
        work_order_process_id: workOrderProcessId,
      });
      if (res.data?.success) {
        toast.success("실적이 확정되었습니다.");
        onSaved({ ...res.data.data, result_status: "confirmed" });
      } else {
        toast.error(res.data?.message || "실적 확정에 실패했습니다.");
      }
    } catch {
      toast.error("실적 확정 중 오류가 발생했습니다.");
    } finally {
      setConfirming(false);
    }
  };

  return (
    <div className="flex flex-1 flex-col overflow-y-auto">
      <div className="space-y-5 p-4">
        {/* 확정 상태 배너 */}
        {isConfirmed && (
          <div className="flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-2.5">
            <FileCheck className="h-4 w-4 text-green-600" />
            <span className="text-sm font-medium text-green-700">실적이 확정되었습니다</span>
          </div>
        )}

        {/* 공정 현황: 접수량 / 작업완료 / 잔여 + 앞공정 완료량 */}
        <div className="rounded-lg border bg-muted/20 px-4 py-3">
          <div className="mb-2 text-xs font-semibold uppercase text-muted-foreground">공정 현황</div>
          <div className="flex items-center gap-5">
            <div className="text-center">
              <div className="text-lg font-bold">{inputQty}</div>
              <div className="text-xs text-muted-foreground">접수량</div>
            </div>
            <div className="text-center">
              <div className="text-lg font-bold text-blue-600">{accumulatedTotal}</div>
              <div className="text-xs text-muted-foreground">작업완료</div>
            </div>
            <div className="text-center">
              <div className={`text-lg font-bold ${remainingQty > 0 ? "text-amber-600" : "text-green-600"}`}>
                {remainingQty}
              </div>
              <div className="text-xs text-muted-foreground">잔여</div>
            </div>
            {availableInfo && availableInfo.availableQty > 0 && (
              <div className="text-center">
                <div className="text-lg font-bold text-violet-600">{availableInfo.availableQty}</div>
                <div className="text-xs text-muted-foreground">추가접수가능</div>
              </div>
            )}
          </div>
          {inputQty > 0 && (
            <div className="mt-2 h-2 w-full overflow-hidden rounded-full bg-muted">
              <div
                className="h-full bg-blue-500 transition-all duration-300"
                style={{ width: `${Math.min(100, (accumulatedTotal / inputQty) * 100)}%` }}
              />
            </div>
          )}
          {availableInfo && (
            <div className="mt-2 flex gap-3 text-xs text-muted-foreground">
              <span>앞공정 완료: {availableInfo.prevGoodQty}</span>
              <span>지시수량: {availableInfo.instructionQty}</span>
            </div>
          )}
        </div>

        {/* 누적 실적 현황 */}
        <div className="rounded-lg border bg-muted/20 px-4 py-3">
          <div className="mb-2 text-xs font-semibold uppercase text-muted-foreground">누적 실적</div>
          <div className="flex items-center gap-5">
            <div className="text-center">
              <div className="text-lg font-bold">{accumulatedTotal}</div>
              <div className="text-xs text-muted-foreground">총생산</div>
            </div>
            <div className="text-center">
              <div className="text-lg font-bold text-green-600">{accumulatedGood}</div>
              <div className="text-xs text-muted-foreground">양품</div>
            </div>
            <div className="text-center">
              <div className="text-lg font-bold text-red-600">{accumulatedDefect}</div>
              <div className="text-xs text-muted-foreground">불량</div>
            </div>
            <div className="text-center">
              <div className="text-lg font-bold">{history.length}</div>
              <div className="text-xs text-muted-foreground">차수</div>
            </div>
          </div>
          {accumulatedTotal > 0 && (
            <div className="mt-2 h-2 w-full overflow-hidden rounded-full bg-muted">
              <div
                className="h-full bg-green-500 transition-all duration-300"
                style={{ width: `${accumulatedTotal > 0 ? (accumulatedGood / accumulatedTotal) * 100 : 0}%` }}
              />
            </div>
          )}
        </div>

        {/* 이번 차수 실적 입력 */}
        {!isConfirmed && (
          <div className="space-y-4">
            <div className="text-sm font-semibold">이번 차수 실적</div>

            {/* 생산수량 */}
            {enabledSections.some((s) => s.type === "total-qty") && (
              <div className="space-y-2">
                <label className="text-sm font-medium">생산수량</label>
                <div className="flex items-center gap-3">
                  <Input
                    type="number"
                    className="h-11 w-40 text-base"
                    value={batchQty}
                    onChange={(e) => setBatchQty(e.target.value)}
                    placeholder="0"
                  />
                  <span className="text-sm text-muted-foreground">EA</span>
                </div>
              </div>
            )}

            {/* 양품/불량 */}
            {enabledSections.some((s) => s.type === "good-defect") && (
              <div className="space-y-2">
                <label className="text-sm font-medium">양품 / 불량</label>
                <div className="flex items-center gap-4">
                  <div className="flex items-center gap-2">
                    <span className="text-sm text-muted-foreground">양품</span>
                    <Input
                      type="number"
                      className="h-11 w-28 bg-muted/50 text-base"
                      value={batchGood > 0 ? String(batchGood) : ""}
                      readOnly
                      placeholder="자동"
                    />
                  </div>
                  <div className="flex items-center gap-2">
                    <span className="text-sm text-muted-foreground">불량</span>
                    <Input
                      type="number"
                      className="h-11 w-28 text-base"
                      value={batchDefect}
                      onChange={(e) => setBatchDefect(e.target.value)}
                      placeholder="0"
                    />
                  </div>
                </div>
                {(parseInt(batchQty, 10) || 0) > 0 && (
                  <p className="text-xs text-muted-foreground">
                    양품 {batchGood} = 생산 {batchQty} - 불량 {batchDefect || 0}
                  </p>
                )}
              </div>
            )}

            {/* 불량 유형 상세 */}
            {enabledSections.some((s) => s.type === "defect-types") && (
              <div className="space-y-3">
                <div className="flex items-center justify-between">
                  <label className="text-sm font-medium">불량 유형 상세</label>
                  <Button size="sm" variant="outline" className="h-8 gap-1 text-xs" onClick={addDefectEntry}>
                    <Plus className="h-3.5 w-3.5" />추가
                  </Button>
                </div>
                {defectEntries.length === 0 ? (
                  <p className="text-sm text-muted-foreground">등록된 불량 유형이 없습니다.</p>
                ) : (
                  <div className="space-y-2">
                    {defectEntries.map((entry, idx) => (
                      <div key={idx} className="flex items-center gap-2 rounded-lg border p-2.5">
                        <select
                          className="h-10 rounded-md border px-2 text-sm"
                          value={entry.defect_code}
                          onChange={(e) => updateDefectEntry(idx, "defect_code", e.target.value)}
                        >
                          <option value="">불량유형 선택</option>
                          {defectTypes.map((dt) => (
                            <option key={dt.defect_code} value={dt.defect_code}>
                              {dt.defect_name} ({dt.defect_code})
                            </option>
                          ))}
                        </select>
                        <Input
                          type="number"
                          className="h-10 w-20 text-sm"
                          value={entry.qty}
                          onChange={(e) => updateDefectEntry(idx, "qty", e.target.value)}
                          placeholder="수량"
                        />
                        <select
                          className="h-10 rounded-md border px-2 text-sm"
                          value={entry.disposition}
                          onChange={(e) => updateDefectEntry(idx, "disposition", e.target.value)}
                        >
                          {DISPOSITION_OPTIONS.map((opt) => (
                            <option key={opt.value} value={opt.value}>{opt.label}</option>
                          ))}
                        </select>
                        <Button size="icon" variant="ghost" className="h-8 w-8 shrink-0" onClick={() => removeDefectEntry(idx)}>
                          <Trash2 className="h-3.5 w-3.5 text-destructive" />
                        </Button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            )}

            {/* 비고 */}
            {enabledSections.some((s) => s.type === "note") && (
              <div className="space-y-2">
                <label className="text-sm font-medium">비고</label>
                <Textarea
                  className="min-h-[60px] text-sm"
                  value={resultNote}
                  onChange={(e) => setResultNote(e.target.value)}
                  placeholder="작업 내용, 특이사항 등"
                />
              </div>
            )}

            {/* 미구현 섹션 플레이스홀더 (순서 보존) */}
            {enabledSections
              .filter((s) => !IMPLEMENTED_SECTIONS.has(s.type))
              .map((s) => (
                <div key={s.id} className="flex items-center gap-3 rounded-lg border border-dashed p-4">
                  <Construction className="h-5 w-5 shrink-0 text-muted-foreground" />
                  <div>
                    <p className="text-sm font-medium">{SECTION_LABELS[s.type] ?? s.type}</p>
                    <p className="text-xs text-muted-foreground">준비 중인 기능입니다</p>
                  </div>
                </div>
              ))}

            {/* 등록 버튼 */}
            <div className="flex items-center gap-3">
              <Button
                className="h-11 gap-2 px-6 text-sm"
                onClick={handleSubmitBatch}
                disabled={saving || !batchQty}
              >
                {saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Plus className="h-4 w-4" />}
                실적 등록
              </Button>
            </div>
          </div>
        )}

        {/* 이전 실적 이력 */}
        <div className="space-y-2">
          <div className="text-sm font-semibold">등록 이력</div>
          {historyLoading ? (
            <div className="flex items-center gap-2 py-4 text-sm text-muted-foreground">
              <Loader2 className="h-4 w-4 animate-spin" />불러오는 중...
            </div>
          ) : history.length === 0 ? (
            <p className="py-4 text-sm text-muted-foreground">등록된 실적이 없습니다.</p>
          ) : (
            <div className="space-y-1.5">
              {[...history].reverse().map((h) => (
                <div key={h.seq} className="flex items-center justify-between rounded-lg border px-3 py-2">
                  <div className="flex items-center gap-3">
                    <Badge variant="secondary" className="text-xs px-2">#{h.seq}</Badge>
                    <span className="text-sm font-medium">+{h.batch_qty}</span>
                    <span className="text-xs text-green-600">양품 +{h.batch_good}</span>
                    {h.batch_defect > 0 && (
                      <span className="text-xs text-red-600">불량 +{h.batch_defect}</span>
                    )}
                  </div>
                  <div className="flex items-center gap-2 text-xs text-muted-foreground">
                    <span>누적 {h.accumulated_total}</span>
                    <span>{new Date(h.changed_at).toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit" })}</span>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>

      {/* 실적 확정 버튼 제거됨 - 자동 완료로 대체 (2026-03-17 결정) */}
    </div>
  );
}

// ========================================
// 유틸리티
// ========================================

function formatSeconds(totalSec: number): string {
  const h = String(Math.floor(totalSec / 3600)).padStart(2, "0");
  const m = String(Math.floor((totalSec % 3600) / 60)).padStart(2, "0");
  const s = String(totalSec % 60).padStart(2, "0");
  return `${h}:${m}:${s}`;
}

function formatMsToTime(ms: number): string {
  return formatSeconds(Math.floor(ms / 1000));
}

function formatDuration(startISO: string, endISO: string): string {
  const diffMs = new Date(endISO).getTime() - new Date(startISO).getTime();
  if (diffMs <= 0) return "0초";
  const totalSec = Math.floor(diffMs / 1000);
  if (totalSec < 60) return `${totalSec}초`;
  const min = Math.floor(totalSec / 60);
  const sec = totalSec % 60;
  return sec > 0 ? `${min}분 ${sec}초` : `${min}분`;
}

// ========================================
// 작업지시 정보 바
// ========================================

interface InfoBarProps {
  fields: Array<{ label: string; column: string }>;
  parentRow: RowData;
  processName: string;
}

function InfoBar({ fields, parentRow, processName }: InfoBarProps) {
  return (
    <div className="flex flex-wrap items-center gap-x-5 gap-y-1.5 border-b bg-muted/30 px-4 py-2.5">
      {fields.map((f) => {
        const val = f.column === "__process_name"
          ? processName
          : parentRow[f.column];
        return (
          <div key={f.column} className="flex items-center gap-1.5">
            <span className="text-xs text-muted-foreground">{f.label}</span>
            <span className="text-sm font-medium">{val != null ? String(val) : "-"}</span>
          </div>
        );
      })}
    </div>
  );
}

// ========================================
// 그룹별 타이머 헤더
// ========================================

interface GroupTimerHeaderProps {
  group: WorkGroup;
  cfg: PopWorkDetailConfig;
  isProcessCompleted: boolean;
  isGroupStarted: boolean;
  isGroupPaused: boolean;
  isGroupCompleted: boolean;
  groupTimerFormatted: string;
  groupElapsedFormatted: string;
  onTimerAction: (action: "start" | "pause" | "resume" | "complete") => void;
}

function GroupTimerHeader({
  group,
  cfg,
  isProcessCompleted,
  isGroupStarted,
  isGroupPaused,
  isGroupCompleted,
  groupTimerFormatted,
  groupElapsedFormatted,
  onTimerAction,
}: GroupTimerHeaderProps) {
  return (
    <div className="border-b">
      {/* 그룹 제목 + 진행 카운트 */}
      <div className="flex items-center justify-between px-4 pt-3 pb-1.5">
        <div className="flex items-center gap-2.5">
          <span className="text-sm font-semibold">{group.title}</span>
          <Badge variant="secondary" className="text-xs px-2 py-0.5">
            {group.completed}/{group.total}
          </Badge>
        </div>
        {isGroupCompleted && (
          <Badge variant="outline" className="text-xs text-green-600 px-2 py-0.5">완료</Badge>
        )}
      </div>

      {/* 그룹 타이머 */}
      {cfg.showTimer && (
        <div className="flex items-center justify-between bg-muted/20 px-4 py-2.5">
          <div className="flex items-center gap-4">
            <div className="flex items-center gap-2">
              <Timer className="h-4 w-4 text-muted-foreground" />
              <span className="font-mono text-sm font-medium tabular-nums">{groupTimerFormatted}</span>
              <span className="text-xs text-muted-foreground">작업</span>
            </div>
            <div className="flex items-center gap-2 text-muted-foreground">
              <span className="font-mono text-xs tabular-nums">{groupElapsedFormatted}</span>
              <span className="text-xs">경과</span>
            </div>
          </div>
          {!isProcessCompleted && !isGroupCompleted && (
            <div className="flex items-center gap-1.5">
              {!isGroupStarted && (
                <Button size="sm" variant="outline" className="h-10 px-4 text-sm" onClick={() => onTimerAction("start")}>
                  <Play className="mr-1.5 h-4 w-4" />시작
                </Button>
              )}
              {isGroupStarted && !isGroupPaused && (
                <>
                  <Button size="sm" variant="outline" className="h-10 px-3 text-sm" onClick={() => onTimerAction("pause")}>
                    <Pause className="mr-1.5 h-4 w-4" />정지
                  </Button>
                  <Button size="sm" variant="default" className="h-10 px-3 text-sm" onClick={() => onTimerAction("complete")}>
                    <CheckCircle2 className="mr-1.5 h-4 w-4" />완료
                  </Button>
                </>
              )}
              {isGroupStarted && isGroupPaused && (
                <>
                  <Button size="sm" variant="outline" className="h-10 px-3 text-sm" onClick={() => onTimerAction("resume")}>
                    <Play className="mr-1.5 h-4 w-4" />재개
                  </Button>
                  <Button size="sm" variant="default" className="h-10 px-3 text-sm" onClick={() => onTimerAction("complete")}>
                    <CheckCircle2 className="mr-1.5 h-4 w-4" />완료
                  </Button>
                </>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ========================================
// 단계 상태 아이콘
// ========================================

function StepStatusIcon({ status }: { status: "pending" | "active" | "completed" }) {
  switch (status) {
    case "completed":
      return <CheckCircle2 className="h-4.5 w-4.5 shrink-0 text-green-600" />;
    case "active":
      return <CircleDot className="h-4.5 w-4.5 shrink-0 text-primary" />;
    default:
      return <div className="h-4.5 w-4.5 shrink-0 rounded-full border border-muted-foreground/40" />;
  }
}

// ========================================
// 체크리스트 개별 항목 (라우터)
// ========================================

interface ChecklistItemProps {
  item: WorkResultRow;
  saving: boolean;
  disabled: boolean;
  onSave: (rowId: string, resultValue: string, isPassed: string | null, newStatus: string) => void;
}

function ChecklistItem({ item, saving, disabled, onSave }: ChecklistItemProps) {
  const isDisabled = disabled || saving;
  const dt = item.detail_type ?? "";

  // "inspect_numeric" 등 레거시 형식 → "inspect"로 정규화, 접미사를 input_type 폴백으로 사용
  if (dt.startsWith("inspect")) {
    const normalized = { ...item, detail_type: "inspect" } as WorkResultRow;
    if (!normalized.input_type && dt.includes("_")) {
      const suffix = dt.split("_").slice(1).join("_");
      const typeMap: Record<string, string> = { numeric: "numeric_range", ox: "ox", text: "text", select: "select" };
      normalized.input_type = typeMap[suffix] ?? suffix;
    }
    return <InspectRouter item={normalized} disabled={isDisabled} saving={saving} onSave={onSave} />;
  }

  switch (dt) {
    case "check":
      return <CheckItem item={item} disabled={isDisabled} saving={saving} onSave={onSave} />;
    case "input":
      return <InputItem item={item} disabled={isDisabled} saving={saving} onSave={onSave} />;
    case "procedure":
      return <ProcedureItem item={item} disabled={isDisabled} saving={saving} onSave={onSave} />;
    case "material":
      return <MaterialItem item={item} disabled={isDisabled} saving={saving} onSave={onSave} />;
    case "result":
      return <ResultInputItem item={item} disabled={isDisabled} saving={saving} onSave={onSave} />;
    case "info":
      return (
        <div className="rounded-lg border bg-muted/30 px-4 py-3 text-sm">
          {item.detail_label || item.detail_content}
        </div>
      );
    default:
      return (
        <div className="rounded-lg border p-3 text-sm text-muted-foreground">
          알 수 없는 유형: {item.detail_type}
        </div>
      );
  }
}

// ========================================
// check: 체크박스
// ========================================

function CheckItem({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const checked = item.result_value === "Y";
  return (
    <div className={cn("flex items-center gap-3 rounded-lg border px-4 py-3", item.status === "completed" && "border-green-200 bg-green-50")}>
      <Checkbox
        checked={checked}
        disabled={disabled}
        className="h-5 w-5"
        onCheckedChange={(v) => {
          const val = v ? "Y" : "N";
          onSave(item.id, val, v ? "Y" : "N", v ? "completed" : "pending");
        }}
      />
      <span className="flex-1 text-sm">{item.detail_label || item.detail_content}</span>
      {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      {saving && <Loader2 className="h-4 w-4 animate-spin" />}
      {item.status === "completed" && !saving && <Badge variant="outline" className="text-xs text-green-600">완료</Badge>}
    </div>
  );
}

// ========================================
// inspect: 라우터 (input_type으로 분기)
// ========================================

function InspectRouter(props: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const inputType = props.item.input_type ?? "numeric_range";
  switch (inputType) {
    case "ox":
      return <InspectOX {...props} />;
    case "select":
      return <InspectSelect {...props} />;
    case "text":
      return <InspectText {...props} />;
    default:
      return <InspectNumeric {...props} />;
  }
}

// ===== inspect: 수치(범위) =====

function InspectNumeric({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const [inputVal, setInputVal] = useState(item.result_value ?? "");
  const lower = parseFloat(item.lower_limit ?? "");
  const upper = parseFloat(item.upper_limit ?? "");
  const hasRange = !isNaN(lower) && !isNaN(upper);

  const handleBlur = () => {
    if (!inputVal || disabled) return;
    const numVal = parseFloat(inputVal);
    let passed: string | null = null;
    if (hasRange) {
      passed = numVal >= lower && numVal <= upper ? "Y" : "N";
    }
    onSave(item.id, inputVal, passed, "completed");
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", item.is_passed === "Y" && "border-green-200 bg-green-50", item.is_passed === "N" && "border-red-200 bg-red-50")}>
      <div className="mb-2 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="text-sm font-medium">{item.detail_label || item.detail_content}</span>
          {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
        </div>
        {hasRange && (
          <span className="text-xs text-muted-foreground">
            {item.lower_limit} ~ {item.upper_limit} {item.unit || ""}
            {item.spec_value ? ` (표준: ${item.spec_value})` : ""}
          </span>
        )}
      </div>
      {item.inspection_method && (
        <div className="mb-2 text-xs text-muted-foreground">{item.inspection_method}</div>
      )}
      <div className="flex items-center gap-2.5">
        <Input type="number" className="h-10 w-36 text-sm" value={inputVal} onChange={(e) => setInputVal(e.target.value)} onBlur={handleBlur} disabled={disabled} placeholder="측정값 입력" />
        {item.unit && <span className="text-xs text-muted-foreground">{item.unit}</span>}
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
        {item.is_passed === "Y" && !saving && <Badge variant="outline" className="text-xs text-green-600">합격</Badge>}
        {item.is_passed === "N" && !saving && <Badge variant="outline" className="text-xs text-red-600">불합격</Badge>}
      </div>
    </div>
  );
}

// ===== inspect: O/X =====

function InspectOX({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const currentValue = item.result_value ?? "";
  const criteriaOK = item.spec_value ?? "OK";

  const handleSelect = (value: string) => {
    if (disabled) return;
    const passed = value === criteriaOK ? "Y" : "N";
    onSave(item.id, value, passed, "completed");
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", item.is_passed === "Y" && "border-green-200 bg-green-50", item.is_passed === "N" && "border-red-200 bg-red-50")}>
      <div className="mb-2 flex items-center gap-2">
        <span className="text-sm font-medium">{item.detail_label || item.detail_content}</span>
        {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      </div>
      <div className="flex items-center gap-2.5">
        <Button
          size="sm"
          variant={currentValue === "OK" ? "default" : "outline"}
          className={cn("h-11 gap-1.5 px-6 text-sm font-medium", currentValue === "OK" && "bg-green-600 hover:bg-green-700")}
          disabled={disabled}
          onClick={() => handleSelect("OK")}
        >
          <Check className="h-4 w-4" />OK
        </Button>
        <Button
          size="sm"
          variant={currentValue === "NG" ? "default" : "outline"}
          className={cn("h-11 gap-1.5 px-6 text-sm font-medium", currentValue === "NG" && "bg-red-600 hover:bg-red-700")}
          disabled={disabled}
          onClick={() => handleSelect("NG")}
        >
          <X className="h-4 w-4" />NG
        </Button>
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
        {item.is_passed === "Y" && !saving && <Badge variant="outline" className="text-xs text-green-600">합격</Badge>}
        {item.is_passed === "N" && !saving && <Badge variant="outline" className="text-xs text-red-600">불합격</Badge>}
      </div>
    </div>
  );
}

// ===== inspect: 선택형 =====

function InspectSelect({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const currentValue = item.result_value ?? "";
  let options: string[] = [];
  let passValues: string[] = [];

  try {
    const parsed = JSON.parse(item.spec_value ?? "{}");
    options = Array.isArray(parsed.options) ? parsed.options : [];
    passValues = Array.isArray(parsed.passValues) ? parsed.passValues : [];
  } catch {
    options = (item.spec_value ?? "").split(",").filter(Boolean);
  }

  const handleSelect = (value: string) => {
    if (disabled) return;
    const passed = passValues.includes(value) ? "Y" : "N";
    onSave(item.id, value, passed, "completed");
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", item.is_passed === "Y" && "border-green-200 bg-green-50", item.is_passed === "N" && "border-red-200 bg-red-50")}>
      <div className="mb-2 flex items-center gap-2">
        <span className="text-sm font-medium">{item.detail_label || item.detail_content}</span>
        {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      </div>
      <div className="flex flex-wrap items-center gap-2">
        {options.map((opt) => (
          <Button
            key={opt}
            size="sm"
            variant={currentValue === opt ? "default" : "outline"}
            className="h-10 px-4 text-sm"
            disabled={disabled}
            onClick={() => handleSelect(opt)}
          >
            {opt}
          </Button>
        ))}
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
        {item.is_passed === "Y" && !saving && <Badge variant="outline" className="text-xs text-green-600">합격</Badge>}
        {item.is_passed === "N" && !saving && <Badge variant="outline" className="text-xs text-red-600">불합격</Badge>}
      </div>
    </div>
  );
}

// ===== inspect: 텍스트 입력 + 수동 판정 =====

function InspectText({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const [inputVal, setInputVal] = useState(item.result_value ?? "");
  const [judged, setJudged] = useState<string | null>(item.is_passed);

  const handleJudge = (passed: string) => {
    if (disabled) return;
    setJudged(passed);
    onSave(item.id, inputVal || "-", passed, "completed");
  };

  const handleBlur = () => {
    if (!inputVal || disabled) return;
    if (judged) {
      onSave(item.id, inputVal, judged, "completed");
    }
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", judged === "Y" && "border-green-200 bg-green-50", judged === "N" && "border-red-200 bg-red-50")}>
      <div className="mb-2 flex items-center gap-2">
        <span className="text-sm font-medium">{item.detail_label || item.detail_content}</span>
        {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      </div>
      <div className="flex items-center gap-2.5">
        <Input className="h-10 flex-1 text-sm" value={inputVal} onChange={(e) => setInputVal(e.target.value)} onBlur={handleBlur} disabled={disabled} placeholder="내용 입력" />
        <Button
          size="sm"
          variant={judged === "Y" ? "default" : "outline"}
          className={cn("h-10 px-4 text-sm", judged === "Y" && "bg-green-600 hover:bg-green-700")}
          disabled={disabled}
          onClick={() => handleJudge("Y")}
        >
          합격
        </Button>
        <Button
          size="sm"
          variant={judged === "N" ? "default" : "outline"}
          className={cn("h-10 px-4 text-sm", judged === "N" && "bg-red-600 hover:bg-red-700")}
          disabled={disabled}
          onClick={() => handleJudge("N")}
        >
          불합격
        </Button>
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
      </div>
    </div>
  );
}

// ========================================
// input: 자유 입력
// ========================================

function InputItem({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const [inputVal, setInputVal] = useState(item.result_value ?? "");
  const inputType = item.input_type === "number" ? "number" : "text";

  const handleBlur = () => {
    if (!inputVal || disabled) return;
    onSave(item.id, inputVal, null, "completed");
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", item.status === "completed" && "border-green-200 bg-green-50")}>
      <div className="mb-2 text-sm font-medium">{item.detail_label || item.detail_content}</div>
      <div className="flex items-center gap-2.5">
        <Input type={inputType} className="h-10 flex-1 text-sm" value={inputVal} onChange={(e) => setInputVal(e.target.value)} onBlur={handleBlur} disabled={disabled} placeholder="값 입력" />
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
      </div>
    </div>
  );
}

// ========================================
// procedure: 절차 확인
// ========================================

function ProcedureItem({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const checked = item.result_value === "Y";
  return (
    <div className={cn("rounded-lg border px-4 py-3", item.status === "completed" && "border-green-200 bg-green-50")}>
      <div className="mb-2 flex items-center gap-2.5">
        <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">
          {item.detail_sort_order || "?"}
        </div>
        <span className="flex-1 text-sm">{item.detail_content || item.detail_label}</span>
        {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      </div>
      <div className="flex items-center gap-2.5 pl-9">
        <Checkbox
          checked={checked}
          disabled={disabled}
          className="h-5 w-5"
          onCheckedChange={(v) => {
            onSave(item.id, v ? "Y" : "N", null, v ? "completed" : "pending");
          }}
        />
        <span className="text-sm text-muted-foreground">확인</span>
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
      </div>
    </div>
  );
}

// ========================================
// material: 자재/LOT 입력 (바코드 스캔 지원)
// ========================================

function MaterialItem({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  const [inputVal, setInputVal] = useState(item.result_value ?? "");
  const inputRef = useRef<HTMLInputElement>(null);

  const handleSubmit = () => {
    if (!inputVal || disabled) return;
    onSave(item.id, inputVal, null, "completed");
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === "Enter") {
      e.preventDefault();
      handleSubmit();
    }
  };

  return (
    <div className={cn("rounded-lg border px-4 py-3", item.status === "completed" && "border-green-200 bg-green-50")}>
      <div className="mb-2 flex items-center gap-2">
        <Package className="h-4 w-4 text-muted-foreground" />
        <span className="text-sm font-medium">{item.detail_label || item.detail_content}</span>
        {item.is_required === "Y" && <span className="text-xs font-semibold text-destructive">필수</span>}
      </div>
      <div className="flex items-center gap-2.5">
        <Input
          ref={inputRef}
          className="h-10 flex-1 text-sm"
          value={inputVal}
          onChange={(e) => setInputVal(e.target.value)}
          onKeyDown={handleKeyDown}
          disabled={disabled}
          placeholder="LOT/바코드 스캔 또는 입력"
        />
        <Button size="sm" variant="outline" className="h-10 px-4 text-sm" onClick={handleSubmit} disabled={disabled || !inputVal}>
          확인
        </Button>
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
        {item.status === "completed" && !saving && <Badge variant="outline" className="text-xs text-green-600">투입</Badge>}
      </div>
    </div>
  );
}

// ========================================
// result: 실적 입력 (양품/불량/불량유형)
// ========================================

function ResultInputItem({ item, disabled, saving, onSave }: { item: WorkResultRow; disabled: boolean; saving: boolean; onSave: ChecklistItemProps["onSave"] }) {
  let savedData: { good?: string; defect?: string; defectType?: string; note?: string } = {};
  try {
    savedData = JSON.parse(item.result_value ?? "{}");
  } catch {
    savedData = {};
  }

  const [good, setGood] = useState(savedData.good ?? "");
  const [defect, setDefect] = useState(savedData.defect ?? "");
  const [defectType, setDefectType] = useState(savedData.defectType ?? "");
  const [note, setNote] = useState(savedData.note ?? "");

  const handleSave = () => {
    if (disabled) return;
    const val = JSON.stringify({ good, defect, defectType, note });
    onSave(item.id, val, null, "completed");
  };

  const total = (parseInt(good, 10) || 0) + (parseInt(defect, 10) || 0);

  return (
    <div className={cn("rounded-lg border px-4 py-3 space-y-3", item.status === "completed" && "border-green-200 bg-green-50")}>
      <div className="text-sm font-medium">{item.detail_label || item.detail_content || "실적 입력"}</div>

      <div className="flex items-center gap-4">
        <div className="flex items-center gap-2">
          <span className="text-xs text-muted-foreground">양품</span>
          <Input type="number" className="h-10 w-24 text-sm" value={good} onChange={(e) => setGood(e.target.value)} disabled={disabled} placeholder="0" />
        </div>
        <div className="flex items-center gap-2">
          <span className="text-xs text-muted-foreground">불량</span>
          <Input type="number" className="h-10 w-24 text-sm" value={defect} onChange={(e) => setDefect(e.target.value)} disabled={disabled} placeholder="0" />
        </div>
        <span className="text-xs text-muted-foreground">합계: {total}</span>
      </div>

      {parseInt(defect, 10) > 0 && (
        <div className="flex items-center gap-2">
          <span className="text-xs text-muted-foreground">불량유형</span>
          <Input className="h-10 flex-1 text-sm" value={defectType} onChange={(e) => setDefectType(e.target.value)} disabled={disabled} placeholder="스크래치, 치수불량 등" />
        </div>
      )}

      <div className="flex items-center gap-2">
        <span className="text-xs text-muted-foreground">비고</span>
        <Input className="h-10 flex-1 text-sm" value={note} onChange={(e) => setNote(e.target.value)} disabled={disabled} placeholder="비고 (선택)" />
      </div>

      <div className="flex items-center gap-2.5">
        <Button size="sm" variant="outline" className="h-10 px-4 text-sm" onClick={handleSave} disabled={disabled}>
          실적 등록
        </Button>
        {saving && <Loader2 className="h-4 w-4 animate-spin" />}
        {item.status === "completed" && !saving && <Badge variant="outline" className="text-xs text-green-600">등록됨</Badge>}
      </div>
    </div>
  );
}
