"use client";

import { useRouter } from "next/navigation";
import React, {
	useCallback,
	useEffect,
	useMemo,
	useRef,
	useState,
} from "react";
import { usePopSettings } from "@/hooks/pop/usePopSettings";
import { apiClient } from "@/lib/api/client";
import { dataApi } from "@/lib/api/data";
import { ConfirmModal } from "../common/ConfirmModal";
import {
	type DefectEntry,
	type DefectType,
	DefectTypeModal,
} from "./DefectTypeModal";
import { ProcessTimer, type TimerStatus } from "./ProcessTimer";

/* ================================================================== */
/* Types                                                               */
/* ================================================================== */

interface ProcessData {
	id: string;
	wo_id: string;
	seq_no: string;
	process_code: string;
	process_name: string;
	status: string;
	plan_qty: string;
	input_qty: string;
	good_qty: string;
	defect_qty: string;
	concession_qty: string;
	total_production_qty: string;
	parent_process_id: string | null;
	result_status: string;
	result_note: string;
	started_at: string | null;
	paused_at: string | null;
	total_paused_time: string | null;
	completed_at: string | null;
	actual_work_time: string | null;
	accepted_at: string | null;
	accepted_by: string | null;
	defect_detail: string | null;
	target_warehouse_id: string | null;
	target_location_code: string | null;
	is_rework: string;
	routing_detail_id: string | null;
	batch_id?: string | null;
}

interface WorkInstructionInfo {
	work_instruction_no: string;
	item_name: string;
	item_code: string;
	qty: number;
}

interface ChecklistItem {
	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_content: string;
	detail_type: string;
	detail_label: string | null;
	detail_sort_order: string;
	is_required: string | null;
	result_value: string | null;
	is_passed: string | null;
	status: string;
	spec_value: string | null;
	inspection_code: string | null;
	inspection_method: string | null;
	unit: string | null;
	lower_limit: string | null;
	upper_limit: string | null;
	input_type: string | null;
	group_started_at: string | null;
	group_paused_at: string | null;
	group_total_paused_time: string | null;
	group_completed_at: string | null;
	recorded_by: string | null;
	recorded_at: string | null;
	started_at: string | null;
}

interface Warehouse {
	id: string;
	warehouse_code: string;
	warehouse_name: string;
}

interface WarehouseLocation {
	id: string;
	location_code: string;
	location_name: string;
}

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

type ActiveSection = "checklist" | "result" | "inventory" | "material";

const PHASE_ORDER: Record<string, number> = { PRE: 1, IN: 2, POST: 3 };
const PHASE_LABELS: Record<string, string> = {
	PRE: "작업 전",
	IN: "작업 중",
	POST: "작업 후",
};

/* ================================================================== */
/* ISA-101 Design Tokens                                               */
/* ================================================================== */

const DESIGN = {
	bg: {
		page: "#F5F5F5",
		card: "#FFFFFF",
		header: "#1a1a2e",
		infoBar: "#1a1a2e",
	},
	sidebar: { width: 280 },
	timer: { fontSize: 48 },
	button: { height: 60, touchMin: 48 },
	input: { height: 52 },
	footer: { height: 64 },
} as const;

/* ================================================================== */
/* Numpad Modal                                                        */
/* ================================================================== */

const KEYS = [
	{ label: "7", action: "7" },
	{ label: "8", action: "8" },
	{ label: "9", action: "9" },
	{ label: "\u2190", action: "backspace" },
	{ label: "4", action: "4" },
	{ label: "5", action: "5" },
	{ label: "6", action: "6" },
	{ label: "C", action: "clear" },
	{ label: "1", action: "1" },
	{ label: "2", action: "2" },
	{ label: "3", action: "3" },
	{ label: "MAX", action: "max" },
];

function SimpleNumpadModal({
	open,
	onClose,
	onConfirm,
	maxQty,
	title,
	subtitle,
}: {
	open: boolean;
	onClose: () => void;
	onConfirm: (qty: number) => void;
	maxQty: number;
	title: string;
	subtitle: string;
}) {
	const [qty, setQty] = useState("0");

	useEffect(() => {
		if (open) setQty("0");
	}, [open]);

	const qtyNum = parseInt(qty, 10) || 0;

	const handleKey = useCallback(
		(key: string) => {
			setQty((prev) => {
				switch (key) {
					case "backspace":
						return prev.length <= 1 ? "0" : prev.slice(0, -1);
					case "clear":
						return "0";
					case "max":
						return String(maxQty);
					default: {
						const next = prev === "0" ? key : prev + key;
						const num = parseInt(next, 10);
						if (isNaN(num)) return prev;
						return next;
					}
				}
			});
		},
		[maxQty],
	);

	if (!open) return null;

	return (
		<div className="fixed inset-0 z-50 flex items-center justify-center">
			<div className="absolute inset-0 bg-black/40" onClick={onClose} />
			<div className="relative bg-white w-[90%] max-w-[360px] rounded-2xl shadow-2xl z-10 overflow-hidden">
				<div
					className="px-4 py-3"
					style={{
						background: "linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)",
					}}
				>
					<span className="text-white font-bold text-base">{title}</span>
					<p className="text-white/80 text-xs mt-0.5">{subtitle}</p>
				</div>
				<div className="p-4">
					<input
						type="text"
						readOnly
						value={qtyNum.toLocaleString()}
						className="w-full px-4 py-3 text-right text-3xl font-bold border-2 border-gray-200 rounded-xl bg-gray-50 text-gray-900 mb-3"
						style={{ fontVariantNumeric: "tabular-nums" }}
					/>
					<div className="grid grid-cols-4 gap-2.5">
						{KEYS.map((key) => (
							<button
								key={key.action}
								onClick={() => handleKey(key.action)}
								className={`h-14 rounded-xl text-lg font-semibold active:scale-95 transition-all ${
									key.action === "backspace" || key.action === "clear"
										? "bg-amber-100 text-amber-700 hover:bg-amber-200"
										: key.action === "max"
											? "bg-blue-100 text-blue-700 hover:bg-blue-200 text-sm"
											: "bg-gray-100 text-gray-900 hover:bg-gray-200"
								}`}
							>
								{key.label}
							</button>
						))}
						<button
							onClick={() => handleKey("0")}
							className="col-span-2 h-14 rounded-xl text-lg font-semibold bg-gray-100 text-gray-900 hover:bg-gray-200 active:scale-95 transition-all"
						>
							0
						</button>
						<button
							onClick={() => {
								if (qtyNum > 0) {
									onConfirm(Math.min(qtyNum, maxQty));
									onClose();
								}
							}}
							disabled={qtyNum <= 0}
							className="col-span-2 h-14 rounded-xl text-lg font-bold text-white active:scale-95 transition-all disabled:opacity-40"
							style={{
								background:
									qtyNum <= 0
										? "#9ca3af"
										: "linear-gradient(135deg, #10b981 0%, #059669 100%)",
							}}
						>
							확인
						</button>
					</div>
				</div>
			</div>
		</div>
	);
}

/* ================================================================== */
/* Checklist Group                                                     */
/* ================================================================== */

interface ChecklistGroup {
	phase: string;
	title: string;
	itemId: string;
	sortOrder: number;
	items: ChecklistItem[];
	completed: number;
	total: number;
	timerStarted: boolean;
	timerCompleted: boolean;
	timerPaused: boolean;
	timerState: {
		startedAt: string | null;
		pausedAt: string | null;
		totalPausedTime: number;
		completedAt: string | null;
	};
}

/* ================================================================== */
/* Helper: formatTime                                                  */
/* ================================================================== */

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

function calcGroupWorkSeconds(timer: ChecklistGroup["timerState"]): number {
	if (!timer.startedAt) return 0;
	const now = Date.now();
	const start = new Date(timer.startedAt).getTime();
	const end = timer.completedAt ? new Date(timer.completedAt).getTime() : now;
	let pausedMs = timer.totalPausedTime * 1000;
	if (timer.pausedAt && !timer.completedAt) {
		pausedMs += now - new Date(timer.pausedAt).getTime();
	}
	return Math.max(0, Math.floor((end - start - pausedMs) / 1000));
}

/* ================================================================== */
/* Main Component                                                      */
/* ================================================================== */

interface ProcessWorkProps {
	processId: string;
}

export function ProcessWork({ processId }: ProcessWorkProps) {
	const router = useRouter();
	const contentRef = useRef<HTMLDivElement>(null);

	/* ---- Core State ---- */
	const { settings: popSettings } = usePopSettings();
	const peSettings = popSettings.screens.processExecution;
	const [process, setProcess] = useState<ProcessData | null>(null);
	const [wiInfo, setWiInfo] = useState<WorkInstructionInfo | null>(null);
	const [checklist, setChecklist] = useState<ChecklistItem[]>([]);
	const [defectTypes, setDefectTypes] = useState<DefectType[]>([]);
	const [processList, setProcessList] = useState<
		Array<{ process_code: string; process_name: string }>
	>([]);
	const [loading, setLoading] = useState(true);
	const [saving, setSaving] = useState(false);

	/* ---- Navigation State ---- */
	const [activeSection, setActiveSection] =
		useState<ActiveSection>("checklist");
	const [selectedGroupId, setSelectedGroupId] = useState<string | null>(null);

	/* ---- Timer tick ---- */
	const [tick, setTick] = useState(Date.now());

	/* ---- Production Input ---- */
	const [productionQty, setProductionQty] = useState(0);
	const [defectEntries, setDefectEntries] = useState<DefectEntry[]>([]);
	const [resultNote, setResultNote] = useState("");

	/* ---- Modals ---- */
	const [prodQtyModal, setProdQtyModal] = useState(false);
	const [defectModal, setDefectModal] = useState(false);
	const [confirmModalState, setConfirmModalState] = useState<{
		open: boolean;
		message: string;
		title?: string;
		confirmText?: string;
		variant?: "primary" | "danger" | "success";
		onConfirm: () => void;
	}>({ open: false, message: "", onConfirm: () => {} });
	const askConfirm = (
		message: string,
		onConfirm: () => void,
		opts?: {
			title?: string;
			confirmText?: string;
			variant?: "primary" | "danger" | "success";
		},
	) => {
		setConfirmModalState({
			open: true,
			message,
			title: opts?.title,
			confirmText: opts?.confirmText,
			variant: opts?.variant,
			onConfirm: () => {
				setConfirmModalState((s) => ({ ...s, open: false }));
				onConfirm();
			},
		});
	};

	/* ---- Last Process / Warehouse ---- */
	const [isLastProcess, setIsLastProcess] = useState(false);
	const [warehouses, setWarehouses] = useState<Warehouse[]>([]);
	const [warehouseLocations, setWarehouseLocations] = useState<
		WarehouseLocation[]
	>([]);
	const [selectedWarehouse, setSelectedWarehouse] = useState<string>("");
	const [selectedLocation, setSelectedLocation] = useState<string>("");
	const [packageUnit, setPackageUnit] = useState<string>("");
	const [inboundDone, setInboundDone] = useState(false);

	/* ---- Batch Badge (단일/다중품목) ---- */
	const [batchBadge, setBatchBadge] = useState<{
		isMulti: boolean;
		index: number;
		total: number;
		itemType: string;
	} | null>(null);

	/* ---- Batch History ---- */
	const [history, setHistory] = useState<BatchHistoryItem[]>([]);
	const [historyLoading, setHistoryLoading] = useState(false);

	/* ================================================================ */
	/* Data Fetch                                                        */
	/* ================================================================ */

	const fetchProcess = useCallback(async () => {
		setLoading(true);
		try {
			// 1. Fetch process data
			const procRes = await dataApi.getTableData("work_order_process", {
				size: 1,
				filters: { id: processId },
			});
			const procData = (procRes.data?.[0] ?? null) as ProcessData | null;
			if (procData) {
				setProcess(procData);

				// 2. Fetch work instruction info
				if (procData.wo_id) {
					try {
						const wiRes = await dataApi.getTableData("work_instruction", {
							size: 1,
							filters: { id: procData.wo_id },
						});
						const wi = wiRes.data?.[0] as Record<string, unknown> | undefined;
						if (wi) {
							let itemName = String(wi.item_name || "");
							let itemCode = String(wi.item_code || wi.item_number || "");

							// item_name이 비어있고 item_id가 있으면 item_info에서 조회
							if (!itemName && wi.item_id) {
								try {
									const itemRes = await apiClient.get(
										`/data/item_info/${wi.item_id}`,
									);
									const item = itemRes.data?.data;
									if (item) {
										itemName = String(item.item_name || "");
										itemCode = String(item.item_number || item.item_code || "");
									}
								} catch {
									/* non-critical */
								}
							}

							// batch_id가 있으면 해당 품목의 이름을 조회 (다중 품목 지원)
							let batchItemType = "";
							if (procData.batch_id) {
								try {
									const batchItemRes = await dataApi.getTableData("item_info", {
										size: 1,
										filters: { item_number: procData.batch_id },
									});
									const batchItem = batchItemRes.data?.[0] as Record<string, unknown> | undefined;
									if (batchItem) {
										itemName = String(batchItem.item_name || procData.batch_id);
										itemCode = String(batchItem.item_number || procData.batch_id);
										batchItemType = String(batchItem.type || "");
									} else {
										itemName = procData.batch_id;
										itemCode = procData.batch_id;
									}
								} catch {
									itemName = procData.batch_id;
									itemCode = procData.batch_id;
								}
							}
							// item_type이 없으면 WI의 item_number로 조회
							if (!batchItemType && wi.item_number) {
								try {
									const wiItemRes = await dataApi.getTableData("item_info", {
										size: 1,
										filters: { item_number: String(wi.item_number) },
									});
									const wiItem = wiItemRes.data?.[0] as Record<string, unknown> | undefined;
									if (wiItem) {
										batchItemType = String(wiItem.type || "");
									}
								} catch {
									/* non-critical */
								}
							}
							// batchItemType을 임시 저장 (step 6에서 사용)
							(procData as unknown as Record<string, unknown>)._itemType = batchItemType;

							setWiInfo({
								work_instruction_no: String(wi.work_instruction_no || ""),
								item_name: itemName,
								item_code: itemCode,
								qty: parseInt(String(wi.qty), 10) || 0,
							});
						}
					} catch {
						// Non-critical
					}
				}
			}

			// 3. Fetch checklist (process_work_result)
			const checkRes = await dataApi.getTableData("process_work_result", {
				size: 500,
				filters: { work_order_process_id: processId },
			});
			setChecklist((checkRes.data ?? []) as ChecklistItem[]);

			// 4. Defect types
			try {
				const dtRes = await apiClient.get("/pop/production/defect-types");
				setDefectTypes(dtRes.data?.data || []);
			} catch {
				setDefectTypes([]);
			}

			// 5. Is last process
			try {
				const lpRes = await apiClient.get(
					`/pop/production/is-last-process/${processId}`,
				);
				const lpData = lpRes.data?.data;
				setIsLastProcess(lpData?.isLast || false);
				if (lpData?.targetWarehouseId) {
					setSelectedWarehouse(lpData.targetWarehouseId);
					setSelectedLocation(lpData.targetLocationCode || "");
					setInboundDone(true);
				}
			} catch {
				setIsLastProcess(false);
			}

			// 6. 같은 작업지시의 공정 목록 (재작업 공정 지정용)
			if (procData?.wo_id) {
				try {
					const plRes = await dataApi.getTableData("work_order_process", {
						size: 100,
						filters: { wo_id: procData.wo_id },
					});
					const allSiblings = (plRes.data ?? []) as ProcessData[];
					const masters = allSiblings
						.filter((p) => !p.parent_process_id)
						.sort((a, b) => parseInt(a.seq_no, 10) - parseInt(b.seq_no, 10));
					// 중복 제거
					const seen = new Set<string>();
					setProcessList(
						masters.map((p) => ({
							process_code: p.process_code,
							process_name: p.process_name,
						})).filter((m) => {
							if (seen.has(m.process_code)) return false;
							seen.add(m.process_code);
							return true;
						}),
					);
					// 다중품목 판단: 마스터 공정의 DISTINCT batch_id
					const uniqueBatches: string[] = [];
					for (const p of masters) {
						const bid = p.batch_id || "";
						if (bid && !uniqueBatches.includes(bid)) {
							uniqueBatches.push(bid);
						}
					}
					const currentBid = procData?.batch_id || "";
					const isMultiBatch = uniqueBatches.length > 1;
					const bIdx = currentBid ? uniqueBatches.indexOf(currentBid) + 1 : 1;
					const fetchedItemType = String((procData as unknown as Record<string, unknown>)?._itemType || "");
					setBatchBadge({
						isMulti: isMultiBatch,
						index: Math.max(bIdx, 1),
						total: Math.max(uniqueBatches.length, 1),
						itemType: fetchedItemType,
					});
				} catch {
					setProcessList([]);
				}
			}

			// 7. Warehouses
			try {
				const whRes = await apiClient.get("/pop/production/warehouses");
				setWarehouses(whRes.data?.data || []);
			} catch {
				setWarehouses([]);
			}
		} catch (error) {
			console.error("[ProcessWork] fetch error:", error);
		} finally {
			setLoading(false);
		}
	}, [processId]);

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

	/* ---- Batch History ---- */
	const loadHistory = useCallback(async () => {
		setHistoryLoading(true);
		try {
			const res = await apiClient.get("/pop/production/result-history", {
				params: { work_order_process_id: processId },
			});
			if (res.data?.success) {
				setHistory(res.data.data || []);
			}
		} catch {
			/* ignore */
		} finally {
			setHistoryLoading(false);
		}
	}, [processId]);

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

	/* ---- Timer tick for group timers ---- */
	useEffect(() => {
		const id = setInterval(() => setTick(Date.now()), 1000);
		return () => clearInterval(id);
	}, []);

	/* ================================================================ */
	/* Checklist Groups                                                  */
	/* ================================================================ */

	const groups = useMemo<ChecklistGroup[]>(() => {
		const map = new Map<string, ChecklistGroup>();
		for (const item of checklist) {
			const key = item.source_work_item_id;
			if (!map.has(key)) {
				map.set(key, {
					phase: item.work_phase,
					title: item.item_title,
					itemId: key,
					sortOrder: parseInt(item.item_sort_order || "0", 10),
					items: [],
					completed: 0,
					total: 0,
					timerStarted: !!item.group_started_at,
					timerCompleted: !!item.group_completed_at,
					timerPaused: !!item.group_paused_at,
					timerState: {
						startedAt: item.group_started_at ?? null,
						pausedAt: item.group_paused_at ?? null,
						totalPausedTime: parseInt(item.group_total_paused_time || "0", 10),
						completedAt: item.group_completed_at ?? null,
					},
				});
			}
			const g = map.get(key)!;
			g.items.push(item);
			g.total++;
			if (item.status === "recorded" || item.status === "completed")
				g.completed++;
			// Update timer state (any row may have timer data)
			if (item.group_started_at) {
				g.timerStarted = true;
				g.timerState.startedAt = item.group_started_at;
			}
			if (item.group_completed_at) {
				g.timerCompleted = true;
				g.timerState.completedAt = item.group_completed_at;
			}
			if (item.group_paused_at) {
				g.timerPaused = true;
				g.timerState.pausedAt = item.group_paused_at;
			}
			if (item.group_total_paused_time) {
				g.timerState.totalPausedTime =
					parseInt(item.group_total_paused_time, 10) || 0;
			}
		}

		return Array.from(map.values()).sort(
			(a, b) =>
				(PHASE_ORDER[a.phase] ?? 9) - (PHASE_ORDER[b.phase] ?? 9) ||
				a.sortOrder - b.sortOrder,
		);
	}, [checklist]);

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

	const availablePhases = useMemo(() => {
		const phases: string[] = [];
		if (groupsByPhase["PRE"]?.length) phases.push("PRE");
		if (groupsByPhase["IN"]?.length) phases.push("IN");
		if (groupsByPhase["POST"]?.length) phases.push("POST");
		return phases;
	}, [groupsByPhase]);

	// Auto-select first group
	useEffect(() => {
		if (groups.length > 0 && !selectedGroupId) {
			setSelectedGroupId(groups[0].itemId);
		}
	}, [groups, selectedGroupId]);

	const selectedGroup = useMemo(
		() => groups.find((g) => g.itemId === selectedGroupId) || null,
		[groups, selectedGroupId],
	);

	const currentItems = useMemo(
		() =>
			selectedGroup?.items.sort(
				(a, b) =>
					parseInt(a.detail_sort_order || "0", 10) -
					parseInt(b.detail_sort_order || "0", 10),
			) || [],
		[selectedGroup],
	);

	/* ================================================================ */
	/* Timer Handlers                                                    */
	/* ================================================================ */

	const timerStatus: TimerStatus = (() => {
		if (!process) return "idle";
		if (process.status === "completed") return "completed";
		if (process.paused_at) return "paused";
		if (process.started_at) return "running";
		return "idle";
	})();

	const handleTimerAction = async (
		action: "start" | "pause" | "resume" | "complete",
	) => {
		// 낙관적 업데이트: UI 즉시 반영
		setProcess((prev) => {
			if (!prev) return prev;
			const now = new Date().toISOString();
			if (action === "start")
				return {
					...prev,
					status: "in_progress",
					started_at: now,
					paused_at: null,
				};
			if (action === "pause") return { ...prev, paused_at: now };
			if (action === "resume") {
				const pausedSec = prev.paused_at
					? Math.floor((Date.now() - new Date(prev.paused_at).getTime()) / 1000)
					: 0;
				return {
					...prev,
					paused_at: null,
					total_paused_time: String(
						(parseInt(prev.total_paused_time || "0", 10) || 0) + pausedSec,
					),
				};
			}
			if (action === "complete")
				return {
					...prev,
					status: "completed",
					completed_at: now,
					paused_at: null,
				};
			return prev;
		});

		// API 백그라운드 호출
		try {
			const body: Record<string, unknown> = {
				work_order_process_id: processId,
				action,
			};
			if (action === "complete") {
				body.good_qty = process?.good_qty || "0";
				body.defect_qty = process?.defect_qty || "0";
			}
			await apiClient.post("/pop/production/timer", body);
			// 서버 데이터로 동기화 (조용히)
			fetchProcess();
		} catch (error: unknown) {
			const err = error as { response?: { data?: { message?: string } } };
			alert(err.response?.data?.message || "타이머 오류");
			fetchProcess(); // 실패 시 서버 상태로 복원
		}
	};

	/* ---- Group Timer (낙관적 업데이트) ---- */
	const handleGroupTimerAction = (
		action: "start" | "pause" | "resume" | "complete",
		groupItemId: string,
	) => {
		// 로컬 체크리스트 상태 즉시 업데이트
		const now = new Date().toISOString();
		setChecklist((prev) =>
			prev.map((item) => {
				if (item.source_work_item_id !== groupItemId) return item;
				if (action === "start")
					return { ...item, group_started_at: now, group_paused_at: null };
				if (action === "pause") return { ...item, group_paused_at: now };
				if (action === "resume") {
					const pausedSec = item.group_paused_at
						? Math.floor(
								(Date.now() - new Date(item.group_paused_at).getTime()) / 1000,
							)
						: 0;
					const prev_total =
						parseInt(item.group_total_paused_time || "0", 10) || 0;
					return {
						...item,
						group_paused_at: null,
						group_total_paused_time: String(prev_total + pausedSec),
					};
				}
				if (action === "complete")
					return { ...item, group_completed_at: now, group_paused_at: null };
				return item;
			}),
		);

		// API 백그라운드 (결과 무시, 실패 시만 동기화)
		apiClient
			.post("/pop/production/group-timer", {
				work_order_process_id: processId,
				source_work_item_id: groupItemId,
				action,
			})
			.catch(() => {
				fetchProcess();
			});
	};

	/* ---- Group timer display ---- */
	const selectedGroupWorkSec = useMemo(() => {
		if (!selectedGroup) return 0;
		return calcGroupWorkSeconds(selectedGroup.timerState);
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [selectedGroup, tick]);

	/* ================================================================ */
	/* Checklist Save                                                    */
	/* ================================================================ */

	const handleChecklistSave = async (
		itemId: string,
		resultValue: string,
		isPassed: string | null,
	) => {
		try {
			const now = new Date().toISOString();
			await apiClient.post("/pop/execute-action", {
				tasks: [
					{
						type: "data-update",
						targetTable: "process_work_result",
						targetColumn: "result_value",
						operationType: "assign",
						valueSource: "fixed",
						fixedValue: resultValue,
						lookupMode: "manual",
						manualItemField: "id",
						manualPkColumn: "id",
					},
					{
						type: "data-update",
						targetTable: "process_work_result",
						targetColumn: "status",
						operationType: "assign",
						valueSource: "fixed",
						fixedValue: "recorded",
						lookupMode: "manual",
						manualItemField: "id",
						manualPkColumn: "id",
					},
					...(isPassed !== null
						? [
								{
									type: "data-update",
									targetTable: "process_work_result",
									targetColumn: "is_passed",
									operationType: "assign",
									valueSource: "fixed",
									fixedValue: isPassed,
									lookupMode: "manual",
									manualItemField: "id",
									manualPkColumn: "id",
								},
							]
						: []),
					{
						type: "data-update",
						targetTable: "process_work_result",
						targetColumn: "recorded_at",
						operationType: "assign",
						valueSource: "fixed",
						fixedValue: now,
						lookupMode: "manual",
						manualItemField: "id",
						manualPkColumn: "id",
					},
				],
				data: { items: [{ id: itemId }], fieldValues: {} },
			});
			// Update local state
			setChecklist((prev) =>
				prev.map((c) =>
					c.id === itemId
						? {
								...c,
								result_value: resultValue,
								is_passed: isPassed,
								status: "recorded",
								recorded_at: now,
							}
						: c,
				),
			);
		} catch {
			alert("체크리스트 저장 실패");
		}
	};

	/* ================================================================ */
	/* Save Result (batch-cumulative)                                    */
	/* ================================================================ */

	const handleSaveResult = async () => {
		if (productionQty <= 0) {
			alert("생산수량을 입력해주세요.");
			return;
		}
		setSaving(true);
		try {
			const totalDefect = defectEntries.reduce(
				(s, e) => s + parseInt(e.qty, 10),
				0,
			);
			const res = await apiClient.post("/pop/production/save-result", {
				work_order_process_id: processId,
				production_qty: String(productionQty),
				good_qty: String(productionQty - totalDefect),
				defect_qty: String(totalDefect),
				defect_detail: defectEntries.length > 0 ? defectEntries : undefined,
				result_note: resultNote || undefined,
			});
			if (res.data?.success) {
				const d = res.data.data;
				setProcess((prev) => {
					if (!prev) return prev;
					return { ...prev, ...d };
				});
				setProductionQty(0);
				setDefectEntries([]);
				setResultNote("");
				loadHistory();
				if (d?.status === "completed") {
					alert("모든 수량이 완료되어 자동 확정되었습니다.");
				} else {
					alert("실적이 저장되었습니다.");
				}
			} else {
				alert(res.data?.message || "저장 실패");
			}
		} catch (error: unknown) {
			const err = error as { response?: { data?: { message?: string } } };
			alert(err.response?.data?.message || "실적 저장 중 오류");
		} finally {
			setSaving(false);
		}
	};

	/* ---- Confirm Result ---- */
	const handleConfirmResult = () => {
		askConfirm(
			"실적을 확정하시겠습니까?\n확정 후에는 추가 등록이 불가합니다.",
			async () => {
				setSaving(true);
				try {
					const res = await apiClient.post("/pop/production/confirm-result", {
						work_order_process_id: processId,
					});
					if (res.data?.success) {
						await fetchProcess();
						alert("실적이 확정되었습니다.");
					} else {
						alert(res.data?.message || "확정 실패");
					}
				} catch (error: unknown) {
					const err = error as { response?: { data?: { message?: string } } };
					alert(err.response?.data?.message || "확정 중 오류");
				} finally {
					setSaving(false);
				}
			},
			{ title: "실적 확정", confirmText: "확정", variant: "success" },
		);
	};

	/* ================================================================ */
	/* Inventory Inbound                                                 */
	/* ================================================================ */

	const fetchLocations = useCallback(async (warehouseId: string) => {
		if (!warehouseId) {
			setWarehouseLocations([]);
			return;
		}
		try {
			const res = await apiClient.get(
				`/pop/production/warehouse-locations/${warehouseId}`,
			);
			setWarehouseLocations(res.data?.data || []);
		} catch {
			setWarehouseLocations([]);
		}
	}, []);

	const handleInbound = () => {
		if (!selectedWarehouse) {
			alert("창고를 선택해주세요.");
			return;
		}
		askConfirm(
			"생산입고를 진행하시겠습니까?",
			async () => {
				setSaving(true);
				try {
					const wh = warehouses.find((w) => w.id === selectedWarehouse);
					const warehouseCode = wh?.warehouse_code || selectedWarehouse;
					const res = await apiClient.post(
						"/pop/production/inventory-inbound",
						{
							work_order_process_id: processId,
							warehouse_code: warehouseCode,
							location_code: selectedLocation || undefined,
						},
					);
					if (res.data?.success) {
						setInboundDone(true);
						alert(`재고 입고 완료: ${res.data.data?.qty || 0}개`);
					} else {
						alert(res.data?.message || "입고 실패");
					}
				} catch (error: unknown) {
					const err = error as {
						response?: { data?: { message?: string }; status?: number };
					};
					const msg = err.response?.data?.message;
					if (err.response?.status === 409) {
						setInboundDone(true);
						alert(msg || "이미 입고 완료");
					} else {
						alert(msg || "입고 중 오류");
					}
				} finally {
					setSaving(false);
				}
			},
			{ title: "생산 입고", confirmText: "입고", variant: "primary" },
		);
	};

	/* ================================================================ */
	/* Computed Values                                                    */
	/* ================================================================ */

	const totalDefectQty = defectEntries.reduce(
		(s, e) => s + parseInt(e.qty, 10),
		0,
	);
	const goodQtyThisBatch = productionQty - totalDefectQty;
	const inputQty = parseInt(process?.input_qty || "0", 10);
	const totalProduced = parseInt(process?.total_production_qty || "0", 10);
	const accumulatedGood = parseInt(process?.good_qty || "0", 10);
	const accumulatedDefect = parseInt(process?.defect_qty || "0", 10);
	const remaining = Math.max(0, inputQty - totalProduced);
	const isCompleted = process?.status === "completed";
	const isConfirmed = process?.result_status === "confirmed";
	const hasChecklist = checklist.length > 0;

	/* ================================================================ */
	/* Loading / Error                                                   */
	/* ================================================================ */

	if (loading) {
		return (
			<div className="flex items-center justify-center py-20">
				<div className="flex flex-col items-center gap-3">
					<div className="w-8 h-8 border-3 border-blue-500 border-t-transparent rounded-full animate-spin" />
					<span className="text-sm text-gray-400">공정 정보 로딩중...</span>
				</div>
			</div>
		);
	}

	if (!process) {
		return (
			<div className="flex flex-col items-center justify-center py-20 gap-3">
				<p className="text-gray-400">공정을 찾을 수 없습니다</p>
				<button
					onClick={() => router.back()}
					className="px-4 py-2 rounded-xl bg-gray-100 text-gray-700 text-sm font-semibold active:scale-95 transition-all"
				>
					뒤로가기
				</button>
			</div>
		);
	}

	/* ================================================================ */
	/* Render                                                            */
	/* ================================================================ */

	return (
		<div
			className="flex flex-col h-full"
			style={{ backgroundColor: DESIGN.bg.page }}
		>
			{/* ============================================================ */}
			{/* Info Bar (Dark Header)                                        */}
			{/* ============================================================ */}
			<div
				className="shrink-0 px-4 sm:px-6 py-3"
				style={{ backgroundColor: DESIGN.bg.infoBar }}
			>
				<div className="flex items-center gap-3 sm:gap-6">
					<div className="flex items-center gap-4 sm:gap-8 flex-1 min-w-0 overflow-x-auto">
						{wiInfo && (
							<div className="flex items-center gap-1.5 shrink-0">
								<span className="text-white/40 text-sm">작업지시</span>
								<span className="text-white font-medium text-base">
									{wiInfo.work_instruction_no}
								</span>
							</div>
						)}
						{wiInfo && (
							<div className="flex items-center gap-1.5 shrink-0">
								<span className="text-white/40 text-sm">품목</span>
								<span className="text-white font-medium text-base">
									{wiInfo.item_name}
								</span>
							</div>
						)}
						{batchBadge && (
							<div className="flex items-center gap-1.5 shrink-0">
								{batchBadge.isMulti ? (
									<span className="bg-blue-100 text-blue-700 text-xs font-bold px-2 py-0.5 rounded-full">
										다중 {batchBadge.index}/{batchBadge.total}{batchBadge.itemType ? ` · ${batchBadge.itemType}` : ""}
									</span>
								) : (
									<span className="bg-gray-100 text-gray-600 text-xs font-bold px-2 py-0.5 rounded-full">
										단일{batchBadge.itemType ? ` · ${batchBadge.itemType}` : ""}
									</span>
								)}
							</div>
						)}
						<div className="flex items-center gap-1.5 shrink-0">
							<span className="text-white/40 text-sm">공정</span>
							<span className="text-white font-medium text-base">
								{process.seq_no ? `${process.seq_no}. ` : ""}
								{process.process_name || "공정"}
							</span>
						</div>
						<div className="flex items-center gap-1.5 shrink-0">
							<span className="text-white/40 text-sm">지시</span>
							<span className="text-white font-medium text-base">
								{parseInt(process.plan_qty || "0", 10).toLocaleString()}
							</span>
						</div>
						<div className="flex items-center gap-1.5 shrink-0">
							<span className="text-amber-400/80 text-sm">접수</span>
							<span className="text-amber-300 font-bold text-lg">
								{inputQty.toLocaleString()}
							</span>
						</div>
					</div>
					{/* Status badge */}
					<span
						className={`text-[13px] font-bold px-2.5 py-1 rounded-full shrink-0 ${
							isCompleted
								? "bg-green-500/20 text-green-300"
								: process.status === "in_progress"
									? "bg-blue-500/20 text-blue-300"
									: "bg-white/10 text-white/50"
						}`}
					>
						{isCompleted
							? "완료"
							: process.status === "in_progress"
								? "진행중"
								: process.status}
					</span>
					{process.is_rework === "Y" && (
						<span className="text-[13px] font-bold px-2 py-0.5 rounded-full bg-amber-500/20 text-amber-300 shrink-0">
							재작업
						</span>
					)}
				</div>
			</div>

			{/* ============================================================ */}
			{/* Main Layout: Sidebar(left) + Timer+Content(right)             */}
			{/* ============================================================ */}
			{(hasChecklist || !isConfirmed || (isLastProcess && !inboundDone)) && (
				<div className="flex flex-1 gap-0 min-h-0 overflow-hidden">
					{/* ========================================================= */}
					{/* Left Sidebar (always visible)                              */}
					{/* ========================================================= */}
					<div
						className="shrink-0 flex flex-col overflow-y-auto border-r border-gray-200 bg-gray-50"
						style={{
							width: `${DESIGN.sidebar.width}px`,
							scrollbarWidth: "thin",
						}}
					>
						<div className="py-3">
							{/* Checklist groups by phase */}
							{availablePhases.map((phase) => {
								const phaseGroups = groupsByPhase[phase] || [];
								const phaseDone = phaseGroups.reduce(
									(s, g) => s + g.completed,
									0,
								);
								const phaseTotal = phaseGroups.reduce((s, g) => s + g.total, 0);
								const allDone = phaseDone >= phaseTotal && phaseTotal > 0;

								return (
									<div key={phase} className="mb-4">
										<div className="flex items-center gap-2 px-3 mb-1.5">
											<div
												className={`w-4 h-4 rounded-full flex items-center justify-center ${
													allDone ? "bg-green-500" : "bg-gray-300"
												}`}
											>
												<svg
													className="w-2.5 h-2.5 text-white"
													fill="none"
													stroke="currentColor"
													strokeWidth={3}
													viewBox="0 0 24 24"
												>
													<path
														strokeLinecap="round"
														strokeLinejoin="round"
														d="M4.5 12.75l6 6 9-13.5"
													/>
												</svg>
											</div>
											<span className="text-base font-bold text-gray-400 uppercase tracking-wider">
												{PHASE_LABELS[phase] || phase}
											</span>
											<span
												className={`ml-auto text-[13px] font-semibold ${allDone ? "text-green-500" : "text-gray-300"}`}
											>
												{phaseDone}/{phaseTotal}
											</span>
										</div>
										{phaseGroups.map((g) => {
											const isSelected =
												selectedGroupId === g.itemId &&
												activeSection === "checklist";
											const isDone = g.completed >= g.total && g.total > 0;
											return (
												<button
													key={g.itemId}
													onClick={() => {
														setSelectedGroupId(g.itemId);
														setActiveSection("checklist");
														contentRef.current?.scrollTo({
															top: 0,
															behavior: "smooth",
														});
													}}
													className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${
														isSelected
															? "bg-blue-50 border-2 border-blue-400 shadow-sm"
															: isDone
																? "bg-green-50/50 border border-green-200"
																: "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm"
													}`}
													style={{ width: "calc(100% - 16px)" }}
												>
													<span
														className={`w-3 h-3 rounded-full shrink-0 ${
															isDone
																? "bg-green-500"
																: g.timerStarted
																	? "bg-blue-500 animate-pulse"
																	: isSelected
																		? "bg-blue-500"
																		: "bg-gray-300"
														}`}
													/>
													<span
														className={`text-sm flex-1 ${
															isSelected
																? "font-bold text-blue-800"
																: isDone
																	? "text-green-700 font-medium"
																	: "text-gray-700 font-medium"
														}`}
													>
														{g.title}
													</span>
													<span
														className={`text-xs font-bold px-2 py-0.5 rounded-full ${
															isDone
																? "bg-green-100 text-green-600"
																: isSelected
																	? "bg-blue-100 text-blue-600"
																	: "bg-gray-100 text-gray-400"
														}`}
													>
														{g.completed}/{g.total}
													</span>
												</button>
											);
										})}
									</div>
								);
							})}

							{/* Result section link */}
							{!isConfirmed && (
								<div className="mb-4">
									{/* 자재 투입 (설정으로 제어) */}
									{peSettings.materialInput && (
										<button
											onClick={() => {
												setActiveSection("material");
												contentRef.current?.scrollTo({
													top: 0,
													behavior: "smooth",
												});
											}}
											className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${
												activeSection === "material"
													? "bg-blue-50 border-2 border-blue-400 shadow-sm"
													: "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm"
											}`}
											style={{ width: "calc(100% - 16px)" }}
										>
											<span className="text-base">📦</span>
											<span
												className={`text-sm font-medium ${activeSection === "material" ? "font-bold text-blue-800" : "text-gray-700"}`}
											>
												자재 투입
											</span>
										</button>
									)}

									<button
										onClick={() => {
											setActiveSection("result");
											contentRef.current?.scrollTo({
												top: 0,
												behavior: "smooth",
											});
										}}
										className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${
											activeSection === "result"
												? "bg-blue-50 border-2 border-blue-400 shadow-sm"
												: "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm"
										}`}
										style={{ width: "calc(100% - 16px)" }}
									>
										<span className="text-base">📋</span>
										<span
											className={`text-sm font-medium ${activeSection === "result" ? "font-bold text-blue-800" : "text-gray-700"}`}
										>
											실적 입력
										</span>
									</button>
								</div>
							)}

							{/* Inventory section link */}
							{isLastProcess && (
								<div>
									<button
										onClick={() => {
											setActiveSection("inventory");
											contentRef.current?.scrollTo({
												top: 0,
												behavior: "smooth",
											});
										}}
										className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${
											activeSection === "inventory"
												? "bg-blue-50 border-2 border-blue-400 shadow-sm"
												: inboundDone
													? "bg-green-50 border border-green-300"
													: "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm"
										}`}
										style={{ width: "calc(100% - 16px)" }}
									>
										<span className="text-base">{inboundDone ? "✅" : "🏭"}</span>
										<span
											className={`text-sm font-medium ${
												activeSection === "inventory"
													? "font-bold text-blue-800"
													: inboundDone
														? "text-green-700 font-bold"
														: "text-gray-700"
											}`}
										>
											재고 입고{inboundDone ? " 완료" : ""}
										</span>
									</button>
								</div>
							)}
						</div>
					</div>

					{/* ========================================================= */}
					{/* Mobile Tabs (hidden — sidebar always visible)                */}
					{/* ========================================================= */}
					<div className="hidden shrink-0 flex overflow-x-auto bg-white border-b border-gray-200 px-2 gap-1">
						{availablePhases.map((phase) => {
							const phaseGroups = groupsByPhase[phase] || [];
							const phaseDone = phaseGroups.reduce(
								(s, g) => s + g.completed,
								0,
							);
							const phaseTotal = phaseGroups.reduce((s, g) => s + g.total, 0);
							const isActive =
								activeSection === "checklist" &&
								selectedGroup &&
								selectedGroup.phase === phase;
							return (
								<button
									key={phase}
									onClick={() => {
										const grps = groupsByPhase[phase];
										if (grps && grps.length > 0) {
											setSelectedGroupId(grps[0].itemId);
											setActiveSection("checklist");
										}
									}}
									className={`shrink-0 px-3 py-2 text-xs font-semibold rounded-t-lg transition-all ${
										isActive
											? "bg-blue-50 text-blue-700 border-b-2 border-blue-500"
											: "text-gray-500"
									}`}
								>
									{PHASE_LABELS[phase]} ({phaseDone}/{phaseTotal})
								</button>
							);
						})}
						{!isConfirmed && (
							<button
								onClick={() => setActiveSection("result")}
								className={`shrink-0 px-3 py-2 text-xs font-semibold rounded-t-lg transition-all ${
									activeSection === "result"
										? "bg-amber-50 text-amber-700 border-b-2 border-amber-500"
										: "text-gray-500"
								}`}
							>
								실적
							</button>
						)}
						{isLastProcess && (
							<button
								onClick={() => setActiveSection("inventory")}
								className={`shrink-0 px-3 py-2 text-xs font-semibold rounded-t-lg transition-all ${
									activeSection === "inventory"
										? "bg-green-50 text-green-700 border-b-2 border-green-500"
										: "text-gray-500"
								}`}
							>
								입고 {inboundDone && "✓"}
							</button>
						)}
					</div>

					{/* ========================================================= */}
					{/* Right Column: Timer + Content                              */}
					{/* ========================================================= */}
					<div className="flex-1 flex flex-col min-h-0 overflow-hidden">
						{/* Unified Timer Bar (group-level) */}
						<div
							className={`shrink-0 px-4 py-3 border-b bg-white ${
								selectedGroup?.timerPaused
									? "border-amber-200"
									: selectedGroup?.timerCompleted
										? "border-green-200"
										: "border-gray-100"
							}`}
						>
							{(() => {
								const g = selectedGroup;
								const groupElapsed = g?.timerState.startedAt
									? Math.max(
											0,
											Math.floor(
												((g.timerState.completedAt
													? new Date(g.timerState.completedAt).getTime()
													: Date.now()) -
													new Date(g.timerState.startedAt).getTime()) /
													1000,
											),
										)
									: 0;
								const groupWork = selectedGroupWorkSec;
								const isIdle = !g?.timerStarted;
								const isPaused = g?.timerPaused;
								const isDone = g?.timerCompleted;
								const isRunning = g?.timerStarted && !isPaused && !isDone;

								const btnClass =
									"h-12 min-w-[80px] rounded-xl text-base font-bold text-white active:scale-95 transition-all px-4";

								return (
									<div className="flex items-center gap-4">
										<span
											className="text-2xl font-black tracking-wider text-gray-900"
											style={{
												fontVariantNumeric: "tabular-nums",
												fontFamily: "monospace",
											}}
										>
											{formatTime(groupElapsed)}
										</span>
										<span
											className="text-lg font-semibold tracking-wider text-gray-300"
											style={{
												fontVariantNumeric: "tabular-nums",
												fontFamily: "monospace",
											}}
										>
											{formatTime(groupWork)}
										</span>
										<span
											className={`flex-1 text-base font-bold truncate ${
												isPaused
													? "text-amber-700"
													: isDone
														? "text-green-700"
														: "text-gray-900"
											}`}
										>
											{g?.title || "그룹 선택"}
											{isPaused && (
												<span className="text-xs text-amber-500 ml-2">
													일시정지
												</span>
											)}
											{isDone && (
												<span className="text-xs text-green-500 ml-2">
													✓ 완료
												</span>
											)}
										</span>
										<div className="flex gap-2 shrink-0">
											{isIdle && g && (
												<button
													onClick={() =>
														handleGroupTimerAction("start", g.itemId)
													}
													className={btnClass}
													style={{
														background:
															"linear-gradient(135deg,#3b82f6,#1d4ed8)",
													}}
												>
													▶ 시작
												</button>
											)}
											{isRunning && g && (
												<>
													<button
														onClick={() =>
															handleGroupTimerAction("pause", g.itemId)
														}
														className={btnClass}
														style={{
															background:
																"linear-gradient(135deg,#f59e0b,#d97706)",
														}}
													>
														⏸ 정지
													</button>
													<button
														onClick={() =>
															handleGroupTimerAction("complete", g.itemId)
														}
														className={btnClass}
														style={{
															background:
																"linear-gradient(135deg,#10b981,#059669)",
														}}
													>
														⏹ 종료
													</button>
												</>
											)}
											{isPaused && g && (
												<>
													<button
														onClick={() =>
															handleGroupTimerAction("resume", g.itemId)
														}
														className={btnClass}
														style={{
															background:
																"linear-gradient(135deg,#3b82f6,#1d4ed8)",
														}}
													>
														▶ 재개
													</button>
													<button
														onClick={() =>
															handleGroupTimerAction("complete", g.itemId)
														}
														className={btnClass}
														style={{
															background:
																"linear-gradient(135deg,#10b981,#059669)",
														}}
													>
														⏹ 종료
													</button>
												</>
											)}
										</div>
									</div>
								);
							})()}
						</div>

						{/* Content Area (scrollable) */}
						<div ref={contentRef} className="flex-1 overflow-y-auto p-4">
							{/* Checklist Content */}
							{activeSection === "checklist" && selectedGroup && (
								<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 sm:p-5">
									{/* Group header with timer */}
									<div className="flex items-center justify-between mb-4">
										<div>
											<p className="text-xs text-gray-400 font-medium">
												{PHASE_LABELS[selectedGroup.phase] ||
													selectedGroup.phase}
											</p>
											<p className="text-base font-bold text-gray-900">
												{selectedGroup.title}
											</p>
										</div>
										<span
											className={`text-xs font-semibold px-2.5 py-1 rounded-full ${
												selectedGroup.completed >= selectedGroup.total &&
												selectedGroup.total > 0
													? "bg-green-100 text-green-700"
													: selectedGroup.timerStarted
														? "bg-blue-100 text-blue-700"
														: "bg-gray-100 text-gray-500"
											}`}
										>
											{selectedGroup.completed}/{selectedGroup.total}
										</span>
									</div>

									{/* 그룹 타이머는 상단 통합 타이머로 이동 */}

									{/* Mobile group navigation (sidebar not visible) */}
									<div className="md:hidden flex overflow-x-auto gap-1.5 mb-4 pb-1">
										{(groupsByPhase[selectedGroup.phase] || []).map((g) => {
											const isSelected = g.itemId === selectedGroupId;
											const isDone = g.completed >= g.total && g.total > 0;
											return (
												<button
													key={g.itemId}
													onClick={() => setSelectedGroupId(g.itemId)}
													className={`shrink-0 px-3 py-1.5 rounded-lg text-xs font-medium border transition-all ${
														isSelected
															? "border-blue-400 bg-blue-50 text-blue-700"
															: isDone
																? "border-green-200 bg-green-50 text-green-600"
																: "border-gray-200 bg-white text-gray-600"
													}`}
												>
													{g.title} ({g.completed}/{g.total})
												</button>
											);
										})}
									</div>

									{/* Checklist items */}
									<div className="flex flex-col gap-2">
										{currentItems.map((item) => (
											<ChecklistRow
												key={item.id}
												item={item}
												disabled={isCompleted || false}
												onSave={handleChecklistSave}
												showPhoto={peSettings.groupPhotoEnabled}
											/>
										))}
										{currentItems.length === 0 && (
											<p className="text-sm text-gray-400 text-center py-6">
												체크리스트 항목이 없습니다
											</p>
										)}
									</div>
								</div>
							)}

							{/* ====== Material Input Content (설정) ====== */}
							{activeSection === "material" && peSettings.materialInput && (
								<MaterialInputSection processId={processId} />
							)}

							{/* ====== Result Content ====== */}
							{activeSection === "result" && !isConfirmed && (
								<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 sm:p-5">
									<p className="text-sm font-bold text-gray-900 mb-4 flex items-center gap-2">
										<svg
											className="w-4 h-4 text-blue-500"
											fill="none"
											stroke="currentColor"
											strokeWidth={2}
											viewBox="0 0 24 24"
										>
											<path
												strokeLinecap="round"
												strokeLinejoin="round"
												d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"
											/>
										</svg>
										이번 차수 실적 입력
										{remaining > 0 && (
											<span className="text-xs font-normal text-gray-400 ml-auto">
												잔여: {remaining.toLocaleString()}
											</span>
										)}
									</p>

									<div className="flex flex-col gap-3">
										{/* Production Qty */}
										<button
											onClick={() => setProdQtyModal(true)}
											className="flex items-center justify-between p-3.5 rounded-xl border-2 border-gray-200 bg-gray-50 hover:border-blue-300 active:scale-[0.98] transition-all"
										>
											<span className="text-sm text-gray-500">생산수량</span>
											<span
												className="text-xl font-bold text-gray-900"
												style={{ fontVariantNumeric: "tabular-nums" }}
											>
												{productionQty > 0
													? `${productionQty.toLocaleString()} EA`
													: "0 EA"}
											</span>
										</button>

										{/* Good Qty (자동 계산) */}
										<div className="flex items-center justify-between p-3.5 rounded-xl border-2 border-green-200 bg-green-50">
											<span className="text-sm text-green-600 font-medium">
												양품
											</span>
											<span
												className="text-xl font-bold text-green-700"
												style={{ fontVariantNumeric: "tabular-nums" }}
											>
												{goodQtyThisBatch > 0
													? `${goodQtyThisBatch.toLocaleString()} EA`
													: "0 EA"}
											</span>
										</div>

										{/* Defect */}
										<button
											onClick={() => setDefectModal(true)}
											className="flex items-center justify-between p-3.5 rounded-xl border-2 border-gray-200 bg-gray-50 hover:border-red-300 active:scale-[0.98] transition-all"
										>
											<span className="text-sm text-gray-500">불량</span>
											<span
												className={`text-xl font-bold ${totalDefectQty > 0 ? "text-red-600" : "text-gray-400"}`}
												style={{ fontVariantNumeric: "tabular-nums" }}
											>
												{totalDefectQty > 0
													? `${totalDefectQty.toLocaleString()} EA`
													: "0 EA"}
											</span>
										</button>

										{/* Defect entries summary */}
										{defectEntries.length > 0 && (
											<div className="flex flex-wrap gap-1.5">
												{defectEntries.map((de) => (
													<span
														key={de.defect_code}
														className="text-xs px-2 py-1 rounded-full bg-red-50 text-red-600 border border-red-200 font-medium"
													>
														{de.defect_name}: {de.qty}개 (
														{de.disposition === "scrap"
															? "폐기"
															: de.disposition === "rework"
																? "재작업"
																: "특채"}
														)
													</span>
												))}
											</div>
										)}

										{/* 누적 현황 */}
										{totalProduced > 0 && (
											<div className="text-xs text-gray-400 text-center">
												누적: {totalProduced}/{inputQty} (
												{Math.round((totalProduced / inputQty) * 100)}%)
											</div>
										)}

										{/* Note */}
										<textarea
											value={resultNote}
											onChange={(e) => setResultNote(e.target.value)}
											placeholder="비고 (선택)"
											className="w-full px-3 py-2.5 rounded-xl border-2 border-gray-200 text-sm text-gray-700 resize-none focus:outline-none focus:border-blue-300"
											rows={2}
										/>
									</div>

									{/* 사진 첨부 (설정으로 제어) */}
									{peSettings.photoUpload && (
										<div className="mt-3">
											<label className="flex items-center gap-2 px-4 py-3 rounded-xl bg-gray-50 border-2 border-dashed border-gray-200 text-gray-500 text-sm font-medium cursor-pointer hover:bg-gray-100 active:scale-[0.98] transition-all">
												<svg
													className="w-5 h-5"
													fill="none"
													stroke="currentColor"
													strokeWidth={2}
													viewBox="0 0 24 24"
												>
													<path
														strokeLinecap="round"
														strokeLinejoin="round"
														d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z"
													/>
													<path
														strokeLinecap="round"
														strokeLinejoin="round"
														d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0z"
													/>
												</svg>
												📷 사진 첨부 (선택)
												<input
													type="file"
													accept="image/*"
													capture="environment"
													className="hidden"
													onChange={async (e) => {
														const file = e.target.files?.[0];
														if (!file) return;
														const formData = new FormData();
														formData.append("file", file);
														formData.append(
															"targetTable",
															"work_order_process",
														);
														formData.append("targetObjid", processId);
														try {
															const res = await fetch("/api/files", {
																method: "POST",
																headers: {
																	Authorization: `Bearer ${localStorage.getItem("accessToken") || localStorage.getItem("token") || ""}`,
																},
																body: formData,
															});
															alert(res.ok ? "사진 첨부 완료" : "첨부 실패");
														} catch {
															alert("첨부 오류");
														}
														e.target.value = "";
													}}
												/>
											</label>
										</div>
									)}

									{/* Save + Confirm buttons */}
									<div className="flex flex-col gap-3 mt-4">
										<button
											onClick={handleSaveResult}
											disabled={productionQty <= 0 || saving}
											className="w-full h-12 rounded-xl text-base font-bold text-white active:scale-95 transition-all disabled:opacity-40"
											style={{
												background:
													productionQty <= 0 || saving
														? "#9ca3af"
														: "linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)",
											}}
										>
											{saving
												? "저장중..."
												: totalProduced + productionQty >= inputQty &&
														inputQty > 0
													? "작업 완료"
													: "분할 완료"}
										</button>

										{/* 공정 확정 버튼 제거 (CEO 결정 2026-04-09): 작업 완료/분할 완료로 충분 */}
									</div>

									{/* Batch History */}
									{history.length > 0 && (
										<div className="mt-6">
											<p className="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">
												등록 이력
											</p>
											<div className="overflow-x-auto">
												<table className="w-full text-sm">
													<thead>
														<tr className="text-xs text-gray-400">
															<th className="px-2 py-1.5 text-left font-medium">
																차수
															</th>
															<th className="px-2 py-1.5 text-right font-medium">
																생산
															</th>
															<th className="px-2 py-1.5 text-right font-medium">
																양품
															</th>
															<th className="px-2 py-1.5 text-right font-medium">
																불량
															</th>
															<th className="px-2 py-1.5 text-right font-medium">
																누적
															</th>
															<th className="px-2 py-1.5 text-right font-medium">
																시각
															</th>
														</tr>
													</thead>
													<tbody>
														{[...history].reverse().map((h, i) => (
															<tr
																key={h.seq}
																className="border-t border-gray-100"
															>
																<td className="px-2 py-2">
																	<span
																		className={`text-xs font-bold px-1.5 py-0.5 rounded ${i === 0 ? "bg-blue-100 text-blue-700" : "bg-gray-100 text-gray-600"}`}
																	>
																		#{h.seq}
																	</span>
																</td>
																<td className="px-2 py-2 text-right font-semibold text-gray-900">
																	+{h.batch_qty}
																</td>
																<td className="px-2 py-2 text-right text-green-600">
																	+{h.batch_good}
																</td>
																<td className="px-2 py-2 text-right text-red-600">
																	+{h.batch_defect}
																</td>
																<td className="px-2 py-2 text-right text-gray-500">
																	{h.accumulated_total}
																</td>
																<td className="px-2 py-2 text-right text-gray-400 text-xs">
																	{new Date(h.changed_at).toLocaleTimeString(
																		"ko-KR",
																		{ hour: "2-digit", minute: "2-digit" },
																	)}
																</td>
															</tr>
														))}
													</tbody>
												</table>
											</div>
										</div>
									)}
									{historyLoading && (
										<div className="flex items-center gap-2 py-4 text-sm text-gray-400">
											<div className="w-4 h-4 border-2 border-gray-300 border-t-transparent rounded-full animate-spin" />
											불러오는 중...
										</div>
									)}
								</div>
							)}

							{/* Confirmed badge */}
							{activeSection === "result" && isConfirmed && (
								<div className="bg-blue-50 border-2 border-blue-200 rounded-2xl p-6 text-center">
									<div className="flex items-center justify-center gap-2 text-blue-700 font-bold text-lg">
										<svg
											className="w-6 h-6"
											fill="none"
											stroke="currentColor"
											strokeWidth={2.5}
											viewBox="0 0 24 24"
										>
											<path
												strokeLinecap="round"
												strokeLinejoin="round"
												d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
											/>
										</svg>
										실적 확정 완료
									</div>
									<div className="flex items-center justify-center gap-4 mt-3 text-sm text-blue-600">
										<span>양품: {accumulatedGood.toLocaleString()}</span>
										<span>불량: {accumulatedDefect.toLocaleString()}</span>
										<span>총: {totalProduced.toLocaleString()}</span>
									</div>
								</div>
							)}

							{/* ====== Inventory Content ====== */}
							{activeSection === "inventory" && isLastProcess && (
								<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 sm:p-5">
									<p className="text-sm font-bold text-green-700 mb-4 flex items-center gap-2">
										<svg
											className="w-4 h-4"
											fill="none"
											stroke="currentColor"
											strokeWidth={2}
											viewBox="0 0 24 24"
										>
											<path
												strokeLinecap="round"
												strokeLinejoin="round"
												d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z"
											/>
										</svg>
										생산입고 (마지막 공정)
									</p>

									{/* Current production summary */}
									<div className="rounded-xl border border-gray-200 bg-gray-50 p-4 mb-4">
										<div className="flex items-center gap-6">
											<div className="flex flex-col items-center">
												<span
													className="text-2xl font-bold text-green-600"
													style={{ fontVariantNumeric: "tabular-nums" }}
												>
													{accumulatedGood}
												</span>
												<span className="text-xs text-green-500 font-medium mt-0.5">
													양품
												</span>
											</div>
											<div className="h-8 w-px bg-gray-200" />
											<div className="flex flex-col items-center">
												<span
													className="text-2xl font-bold text-red-600"
													style={{ fontVariantNumeric: "tabular-nums" }}
												>
													{accumulatedDefect}
												</span>
												<span className="text-xs text-red-500 font-medium mt-0.5">
													불량
												</span>
											</div>
											<div className="h-8 w-px bg-gray-200" />
											<div className="flex flex-col items-center">
												<span
													className="text-2xl font-bold text-gray-900"
													style={{ fontVariantNumeric: "tabular-nums" }}
												>
													{totalProduced}
												</span>
												<span className="text-xs text-gray-400 font-medium mt-0.5">
													총 생산
												</span>
											</div>
										</div>
									</div>

									{!inboundDone ? (
										accumulatedGood > 0 ? (
											<div className="flex flex-col gap-3 mb-4">
												{/* Warehouse selection */}
												<div>
													<label className="text-xs font-semibold text-gray-500 mb-1.5 block">
														창고 선택
													</label>
													<div className="grid grid-cols-2 gap-2">
														{warehouses.map((wh) => (
															<button
																key={wh.id}
																onClick={() => {
																	setSelectedWarehouse(wh.id);
																	setSelectedLocation("");
																	fetchLocations(wh.id);
																}}
																className={`p-3 rounded-xl text-sm font-medium border-2 active:scale-95 transition-all text-left ${
																	selectedWarehouse === wh.id
																		? "border-green-400 bg-green-50 text-green-700"
																		: "border-gray-200 bg-white text-gray-700 hover:border-green-300"
																}`}
															>
																{wh.warehouse_name}
															</button>
														))}
													</div>
												</div>

												{/* Location selection */}
												{warehouseLocations.length > 0 && (
													<div>
														<label className="text-xs font-semibold text-gray-500 mb-1.5 block">
															위치 선택
														</label>
														<div className="grid grid-cols-3 gap-2">
															{warehouseLocations.map((loc) => (
																<button
																	key={loc.id}
																	onClick={() =>
																		setSelectedLocation(loc.location_code)
																	}
																	className={`p-2.5 rounded-xl text-xs font-medium border-2 active:scale-95 transition-all ${
																		selectedLocation === loc.location_code
																			? "border-green-400 bg-green-50 text-green-700"
																			: "border-gray-200 bg-white text-gray-600 hover:border-green-300"
																	}`}
																>
																	{loc.location_name || loc.location_code}
																</button>
															))}
														</div>
													</div>
												)}

												{/* 포장 단위 (선택) */}
												<div>
													<label className="text-xs font-semibold text-gray-500 mb-1.5 block">
														포장 단위 (선택)
													</label>
													<div className="grid grid-cols-3 gap-2">
														{["낱개", "박스", "파렛트"].map((pkg) => (
															<button
																key={pkg}
																onClick={() =>
																	setPackageUnit((prev: string) =>
																		prev === pkg ? "" : pkg,
																	)
																}
																className={`p-2.5 rounded-xl text-sm font-medium border-2 active:scale-95 transition-all ${
																	packageUnit === pkg
																		? "border-blue-400 bg-blue-50 text-blue-700"
																		: "border-gray-200 bg-white text-gray-600"
																}`}
															>
																{pkg}
															</button>
														))}
													</div>
												</div>

												{/* 사진 첨부 (선택) */}
												<label className="flex items-center gap-2 px-4 py-3 rounded-xl bg-gray-50 border-2 border-dashed border-gray-200 text-gray-500 text-sm font-medium cursor-pointer hover:bg-gray-100">
													📷 입고 사진 첨부 (선택)
													<input
														type="file"
														accept="image/*"
														capture="environment"
														className="hidden"
														onChange={async (e) => {
															const file = e.target.files?.[0];
															if (!file) return;
															const formData = new FormData();
															formData.append("file", file);
															formData.append(
																"targetTable",
																"work_order_process",
															);
															formData.append("targetObjid", processId);
															try {
																const res = await fetch("/api/files", {
																	method: "POST",
																	headers: {
																		Authorization: `Bearer ${localStorage.getItem("accessToken") || localStorage.getItem("token") || ""}`,
																	},
																	body: formData,
																});
																alert(res.ok ? "사진 첨부 완료" : "첨부 실패");
															} catch {
																alert("첨부 오류");
															}
															e.target.value = "";
														}}
													/>
												</label>

												{/* Inbound button */}
												<button
													onClick={handleInbound}
													disabled={!selectedWarehouse || saving}
													className="w-full h-14 rounded-xl text-lg font-bold text-white active:scale-95 transition-all disabled:opacity-40"
													style={{
														background:
															!selectedWarehouse || saving
																? "#9ca3af"
																: "linear-gradient(135deg, #22c55e 0%, #15803d 100%)",
													}}
												>
													{saving
														? "처리중..."
														: `생산입고 (양품 ${accumulatedGood}개)`}
												</button>
											</div>
										) : (
											<div className="flex flex-col items-center gap-3 py-8 text-amber-600">
												<svg
													className="w-10 h-10"
													fill="none"
													stroke="currentColor"
													strokeWidth={1.5}
													viewBox="0 0 24 24"
												>
													<path
														strokeLinecap="round"
														strokeLinejoin="round"
														d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
													/>
												</svg>
												<p className="text-sm font-semibold">
													실적을 먼저 등록해주세요
												</p>
												<p className="text-xs text-amber-500">
													양품 수량이 0개입니다
												</p>
											</div>
										)
									) : (
										<div className="flex flex-col items-center gap-3 py-8 text-green-600">
											<svg
												className="w-12 h-12"
												fill="none"
												stroke="currentColor"
												strokeWidth={2}
												viewBox="0 0 24 24"
											>
												<path
													strokeLinecap="round"
													strokeLinejoin="round"
													d="M4.5 12.75l6 6 9-13.5"
												/>
											</svg>
											<p className="text-lg font-bold">재고 입고 완료</p>
											<p className="text-sm text-green-500">
												{accumulatedGood}개가 입고 처리되었습니다
											</p>
										</div>
									)}
								</div>
							)}
						</div>
					</div>
					{/* end Right Column */}
				</div>
			)}

			{/* ============================================================ */}
			{/* Footer Actions                                                */}
			{/* ============================================================ */}
			<div
				className="shrink-0 border-t border-gray-200 bg-white px-4 flex items-center justify-between gap-3"
				style={{ height: `${DESIGN.footer.height}px` }}
			>
				{isCompleted || isConfirmed ? (
					<div className="flex items-center justify-center w-full gap-2 text-green-600">
						<svg
							className="w-5 h-5"
							fill="none"
							stroke="currentColor"
							strokeWidth={2.5}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
							/>
						</svg>
						<span className="text-sm font-bold">
							{isConfirmed ? "실적 확정 완료" : "공정 완료"}
						</span>
					</div>
				) : null}
			</div>

			{/* ============================================================ */}
			{/* Modals                                                        */}
			{/* ============================================================ */}
			<SimpleNumpadModal
				open={prodQtyModal}
				onClose={() => setProdQtyModal(false)}
				onConfirm={(qty) => setProductionQty(qty)}
				maxQty={remaining > 0 ? remaining : 99999}
				title="생산수량 입력"
				subtitle={`잔여: ${remaining.toLocaleString()} EA`}
			/>

			<DefectTypeModal
				open={defectModal}
				onClose={() => setDefectModal(false)}
				onConfirm={setDefectEntries}
				defectTypes={defectTypes}
				maxQty={productionQty > 0 ? productionQty : 99999}
				initialEntries={defectEntries}
				processList={processList}
			/>

			<ConfirmModal
				open={confirmModalState.open}
				title={confirmModalState.title}
				message={confirmModalState.message}
				confirmText={confirmModalState.confirmText}
				variant={confirmModalState.variant}
				onConfirm={confirmModalState.onConfirm}
				onCancel={() => setConfirmModalState((s) => ({ ...s, open: false }))}
			/>
		</div>
	);
}

/* ================================================================== */
/* Checklist Row Component                                             */
/* ================================================================== */

function ChecklistRow({
	item,
	disabled,
	onSave,
	showPhoto = true,
}: {
	item: ChecklistItem;
	disabled: boolean;
	onSave: (id: string, value: string, isPassed: string | null) => void;
	showPhoto?: boolean;
}) {
	const [localValue, setLocalValue] = useState(item.result_value || "");
	const isRecorded = item.status === "recorded" || item.status === "completed";
	const isRequired = item.is_required === "Y";

	// Inspection type: check limits
	const detailType = item.detail_type || "";
	// 판단기준(judgment_criteria) 우선 → 폴백으로 detail_type 매핑
	const jc =
		(item as ChecklistItem & { judgment_criteria?: string })
			.judgment_criteria || "";
	const isInspection =
		jc === "CAT_JC_01" ||
		detailType === "inspection" ||
		detailType === "number" ||
		detailType === "equip_condition" ||
		detailType === "production_result" ||
		detailType.startsWith("inspect");
	const isCheckbox =
		jc === "CAT_JC_03" ||
		detailType === "checkbox" ||
		detailType === "check" ||
		detailType === "checklist" ||
		detailType === "procedure" ||
		detailType === "equip_inspection";
	const isPlc = item.input_type === "plc" || detailType === "plc_data";
	const hasLimits = !!(item.lower_limit || item.upper_limit);

	const handleSave = () => {
		if (!localValue.trim()) return;

		let isPassed: string | null = null;
		if (isInspection && hasLimits) {
			const numVal = parseFloat(localValue);
			const lower = item.lower_limit ? parseFloat(item.lower_limit) : -Infinity;
			const upper = item.upper_limit ? parseFloat(item.upper_limit) : Infinity;
			isPassed =
				!isNaN(numVal) && numVal >= lower && numVal <= upper ? "Y" : "N";

			if (isPassed === "N") {
				const rangeStr = `${item.lower_limit || ""}~${item.upper_limit || ""}`;
				alert(
					`⚠️ 기준 초과!\n\n입력값: ${localValue}\n허용 범위: ${rangeStr}\n\n불합격으로 기록됩니다.`,
				);
			}
		}

		onSave(item.id, localValue, isPassed);
	};

	// Build range text
	const rangeText = buildRangeText(item);

	return (
		<div
			className={`p-4 rounded-xl border-2 transition-all ${
				isRecorded
					? "border-green-200 bg-green-50/50"
					: isRequired
						? "border-amber-300 bg-amber-50/30"
						: "border-gray-200 bg-gray-50/50"
			}`}
		>
			<div className="flex items-center justify-between mb-2">
				<span className="text-base font-semibold text-gray-900 truncate flex-1">
					{item.detail_label || item.detail_content || item.item_title}
				</span>
				<div className="flex items-center gap-2 shrink-0 ml-2">
					{isRequired && !isRecorded && (
						<span className="text-xs font-bold text-red-500 px-2 py-0.5 bg-red-50 rounded-full">
							필수
						</span>
					)}
					{isRecorded && (
						<svg
							className="w-5 h-5 text-green-500"
							fill="none"
							stroke="currentColor"
							strokeWidth={2.5}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M4.5 12.75l6 6 9-13.5"
							/>
						</svg>
					)}
				</div>
			</div>

			{/* Range/spec info */}
			{rangeText && (
				<div className="flex items-center gap-2 mb-2 text-sm text-gray-400">
					📏 {rangeText}
				</div>
			)}

			{/* Input area */}
			{!disabled && !isRecorded && (
				<div className="flex items-center gap-2">
					{isPlc ? (
						<div className="w-full p-3 rounded-xl bg-blue-50 border border-blue-200">
							<div className="flex items-center justify-between">
								<span className="text-sm text-blue-600 font-medium">
									📡 PLC 자동 연동
								</span>
								<span className="text-xs text-blue-400">설정 필요</span>
							</div>
							<p className="text-xs text-blue-400 mt-1">
								POP 관리 설정에서 PLC 매핑을 설정하면 자동으로 값이 표시됩니다
							</p>
							<input
								type="number"
								value={localValue}
								onChange={(e) => setLocalValue(e.target.value)}
								onKeyDown={(e) => e.key === "Enter" && handleSave()}
								placeholder="수동 입력"
								className="mt-2 w-full px-3 py-2 rounded-lg border border-blue-200 text-sm"
							/>
							{localValue && (
								<button
									onClick={handleSave}
									className="mt-2 w-full py-2 rounded-lg text-sm font-bold text-white bg-blue-500"
								>
									저장
								</button>
							)}
						</div>
					) : isCheckbox ? (
						<div className="flex gap-3 w-full">
							<button
								onClick={() => onSave(item.id, "Y", "Y")}
								className="flex-1 py-3 rounded-xl text-base font-bold bg-green-100 text-green-700 border-2 border-green-300 hover:bg-green-200 active:scale-95 transition-all"
							>
								✓ 합격
							</button>
							<button
								onClick={() => onSave(item.id, "N", "N")}
								className="flex-1 py-3 rounded-xl text-base font-bold bg-red-100 text-red-700 border-2 border-red-300 hover:bg-red-200 active:scale-95 transition-all"
							>
								✗ 불합격
							</button>
						</div>
					) : (
						<>
							<input
								type={isInspection ? "number" : "text"}
								value={localValue}
								onChange={(e) => setLocalValue(e.target.value)}
								onKeyDown={(e) => e.key === "Enter" && handleSave()}
								placeholder={isInspection ? "측정값 입력" : "값 입력"}
								className="flex-1 px-4 py-3 rounded-xl border-2 border-gray-200 text-base font-medium focus:outline-none focus:border-blue-400"
								style={{ minHeight: 48 }}
							/>
							<button
								onClick={handleSave}
								disabled={!localValue.trim()}
								className="px-5 py-3 rounded-xl text-base font-bold text-white active:scale-95 transition-all disabled:opacity-40"
								style={{
									minHeight: DESIGN.button.touchMin,
									background: !localValue.trim()
										? "#9ca3af"
										: "linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)",
								}}
							>
								저장
							</button>
						</>
					)}
				</div>
			)}

			{/* Recorded value display */}
			{isRecorded && item.result_value && (
				<div className="flex items-center gap-2 text-sm mt-1">
					<span className="text-gray-500">결과:</span>
					<span
						className={`font-bold ${
							item.is_passed === "Y"
								? "text-green-600"
								: item.is_passed === "N"
									? "text-red-600"
									: "text-gray-700"
						}`}
					>
						{item.result_value === "Y"
							? "합격"
							: item.result_value === "N"
								? "불합격"
								: item.result_value}
					</span>
					{item.is_passed === "Y" && (
						<span className="text-xs px-2 py-0.5 rounded-full bg-green-100 text-green-700 font-bold">
							PASS
						</span>
					)}
					{item.is_passed === "N" && (
						<span className="text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-700 font-bold">
							FAIL
						</span>
					)}
				</div>
			)}

			{/* 사진 업로드 (설정으로 제어) */}
			{!disabled && showPhoto && (
				<div className="mt-2 flex items-center gap-2">
					<label className="flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-100 text-gray-600 text-sm font-medium cursor-pointer hover:bg-gray-200 active:scale-95 transition-all">
						<svg
							className="w-4 h-4"
							fill="none"
							stroke="currentColor"
							strokeWidth={2}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z"
							/>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0z"
							/>
						</svg>
						사진
						<input
							type="file"
							accept="image/*"
							capture="environment"
							className="hidden"
							onChange={async (e) => {
								const file = e.target.files?.[0];
								if (!file) return;
								const formData = new FormData();
								formData.append("file", file);
								formData.append("targetTable", "process_work_result");
								formData.append("targetObjid", item.id);
								try {
									const res = await fetch("/api/files", {
										method: "POST",
										headers: {
											Authorization: `Bearer ${localStorage.getItem("accessToken") || localStorage.getItem("token") || ""}`,
										},
										body: formData,
									});
									if (res.ok) {
										alert("사진 업로드 완료");
									} else {
										alert("업로드 실패");
									}
								} catch {
									alert("업로드 중 오류");
								}
								e.target.value = "";
							}}
						/>
					</label>
				</div>
			)}
		</div>
	);
}

/* ================================================================== */
/* Helper: Build range text for checklist items                        */
/* ================================================================== */

function buildRangeText(item: ChecklistItem): string {
	const parts: string[] = [];
	const lower = item.lower_limit;
	const upper = item.upper_limit;
	const unit = item.unit || "";

	if (lower && upper) {
		parts.push(`${lower}~${upper}${unit ? " " + unit : ""}`);
	} else if (lower) {
		parts.push(`하한: ${lower}${unit ? " " + unit : ""}`);
	} else if (upper) {
		parts.push(`상한: ${upper}${unit ? " " + unit : ""}`);
	} else if (item.spec_value) {
		parts.push(`기준: ${item.spec_value}`);
	}

	if (unit && !lower && !upper) {
		parts.push(`단위: ${unit}`);
	}

	return parts.join(" | ");
}

/* ================================================================== */
/* Material Quantity Keypad (소수점 지원, 자재 투입용)                  */
/* ================================================================== */

function MaterialQtyKeypad({
	open,
	onClose,
	onConfirm,
	initialValue,
	unit,
	requiredQty,
	itemName,
}: {
	open: boolean;
	onClose: () => void;
	onConfirm: (val: string) => void;
	initialValue: string;
	unit: string;
	requiredQty: number;
	itemName: string;
}) {
	const [val, setVal] = useState(initialValue || "0");

	React.useEffect(() => {
		if (open) setVal(initialValue || "0");
	}, [open, initialValue]);

	const press = (k: string) => {
		setVal((prev) => {
			if (k === "backspace") return prev.length <= 1 ? "0" : prev.slice(0, -1);
			if (k === "clear") return "0";
			if (k === "dot") return prev.includes(".") ? prev : prev + ".";
			if (k === "ref") return String(requiredQty);
			if (prev === "0" && k !== ".") return k;
			return prev + k;
		});
	};

	if (!open) return null;
	const numVal = parseFloat(val);
	const lower = requiredQty * 0.8;
	const upper = requiredQty * 1.2;
	const outOfRange = !isNaN(numVal) && (numVal < lower || numVal > upper);

	return (
		<div className="fixed inset-0 z-[60] flex items-center justify-center">
			<div className="absolute inset-0 bg-black/40" onClick={onClose} />
			<div className="relative bg-white rounded-2xl shadow-2xl p-4 w-[320px] z-10">
				<div className="text-center mb-3">
					<p className="text-sm text-gray-500 truncate">{itemName}</p>
					<p className="text-xs text-blue-500 mt-0.5">
						기준 {requiredQty} {unit} (±20%)
					</p>
					<p
						className={`text-3xl font-bold mt-2 ${outOfRange ? "text-amber-600" : "text-gray-900"}`}
						style={{ fontVariantNumeric: "tabular-nums" }}
					>
						{val} <span className="text-base text-gray-400">{unit}</span>
					</p>
				</div>
				<div className="grid grid-cols-3 gap-2 mb-3">
					{["1", "2", "3", "4", "5", "6", "7", "8", "9"].map((k) => (
						<button
							key={k}
							onClick={() => press(k)}
							className="h-14 rounded-xl bg-gray-100 text-xl font-bold text-gray-800 active:scale-95 active:bg-gray-200 transition-all"
						>
							{k}
						</button>
					))}
					<button
						onClick={() => press("dot")}
						className="h-14 rounded-xl bg-gray-100 text-xl font-bold text-gray-800 active:scale-95 transition-all"
					>
						.
					</button>
					<button
						onClick={() => press("0")}
						className="h-14 rounded-xl bg-gray-100 text-xl font-bold text-gray-800 active:scale-95 transition-all"
					>
						0
					</button>
					<button
						onClick={() => press("backspace")}
						className="h-14 rounded-xl bg-gray-200 text-base font-bold text-gray-600 active:scale-95 transition-all"
					>
						←
					</button>
				</div>
				<div className="flex gap-2 mb-2">
					<button
						onClick={() => press("clear")}
						className="flex-1 h-10 rounded-xl bg-gray-100 text-gray-600 text-sm font-bold active:scale-95"
					>
						초기화
					</button>
					<button
						onClick={() => press("ref")}
						className="flex-1 h-10 rounded-xl bg-blue-50 text-blue-600 text-sm font-bold active:scale-95"
					>
						기준값 ({requiredQty})
					</button>
				</div>
				<div className="flex gap-2">
					<button
						onClick={onClose}
						className="flex-1 h-12 rounded-xl bg-gray-100 text-gray-700 font-semibold active:scale-95"
					>
						취소
					</button>
					<button
						onClick={() => {
							onConfirm(val);
							onClose();
						}}
						className="flex-1 h-12 rounded-xl text-white font-bold active:scale-95"
						style={{
							background: outOfRange
								? "linear-gradient(135deg, #f59e0b, #d97706)"
								: "linear-gradient(135deg, #3b82f6, #1d4ed8)",
						}}
					>
						{outOfRange ? "확인 (범위 외)" : "확인"}
					</button>
				</div>
			</div>
		</div>
	);
}

/* ================================================================== */
/* Material Qty Input Row (키패드 트리거 버튼)                          */
/* ================================================================== */

function MaterialQtyInputRow({
	material,
	value,
	onChange,
}: {
	material: {
		id: string;
		child_item_name: string;
		required_qty: number;
		unit: string;
	};
	value: string;
	onChange: (v: string) => void;
}) {
	const [open, setOpen] = useState(false);
	return (
		<div className="flex items-center shrink-0">
			<button
				type="button"
				onClick={() => setOpen(true)}
				className="px-8 py-4 rounded-xl border-2 border-blue-300 text-xl font-bold text-blue-700 hover:border-blue-500 active:scale-[0.96] transition-all bg-blue-50 min-w-[120px] text-center"
			>
				{value || (
					<span className="text-blue-300 font-semibold">입력</span>
				)}
			</button>
			<MaterialQtyKeypad
				open={open}
				onClose={() => setOpen(false)}
				onConfirm={onChange}
				initialValue={value}
				unit={material.unit}
				requiredQty={material.required_qty}
				itemName={material.child_item_name}
			/>
		</div>
	);
}

/* ================================================================== */
/* Material Input Section                                              */
/* ================================================================== */

function MaterialInputSection({ processId }: { processId: string }) {
	const [bomMaterials, setBomMaterials] = React.useState<
		Array<{
			id: string;
			child_item_id: string;
			child_item_code: string;
			child_item_name: string;
			bom_qty: number;
			unit: string;
			required_qty: number;
			input_qty: number;
		}>
	>([]);
	const [inputValues, setInputValues] = React.useState<Record<string, string>>(
		{},
	);
	const [inputted, setInputted] = React.useState<
		Array<{
			id: string;
			item_code: string;
			item_name: string;
			input_qty: string;
			unit: string;
			recorded_at: string;
		}>
	>([]);
	const [loading, setLoading] = React.useState(true);
	const [saving, setSaving] = React.useState(false);
	const [defaultWarehouseCode, setDefaultWarehouseCode] =
		React.useState<string>("");

	React.useEffect(() => {
		const fetchData = async () => {
			setLoading(true);
			try {
				const [bomRes, inputRes, whRes] = await Promise.all([
					apiClient.get(`/pop/production/bom-materials/${processId}`),
					apiClient.get(`/pop/production/material-inputs/${processId}`),
					apiClient.get("/pop/production/warehouses"),
				]);
				setBomMaterials(bomRes.data?.data?.materials || []);
				setInputted(inputRes.data?.data || []);
				// 첫 번째 창고를 기본 자재 출고 창고로 사용 (재고 차감용)
				const wh = whRes.data?.data?.[0];
				if (wh?.warehouse_code) setDefaultWarehouseCode(wh.warehouse_code);
			} catch {
				/* non-critical */
			}
			setLoading(false);
		};
		fetchData();
	}, [processId]);

	const handleSave = async () => {
		const inputs = bomMaterials
			.filter((m) => {
				const val = parseFloat(inputValues[m.id] || "0");
				return val > 0;
			})
			.map((m) => ({
				child_item_id: m.child_item_id,
				child_item_code: m.child_item_code,
				child_item_name: m.child_item_name,
				input_qty: parseFloat(inputValues[m.id] || "0"),
				unit: m.unit,
				bom_detail_id: m.id,
				required_qty: m.required_qty,
				// 재고 차감을 위한 창고 코드 (기본 창고)
				warehouse_code: defaultWarehouseCode || undefined,
				location_code: defaultWarehouseCode || undefined,
			}));

		if (inputs.length === 0) {
			alert("투입 수량을 입력해주세요.");
			return;
		}

		setSaving(true);
		try {
			const res = await apiClient.post("/pop/production/material-input", {
				work_order_process_id: processId,
				inputs,
			});
			if (res.data?.success) {
				alert(res.data.message || "투입 완료");
				setInputValues({});
				// 재조회
				const inputRes = await apiClient.get(
					`/pop/production/material-inputs/${processId}`,
				);
				setInputted(inputRes.data?.data || []);
			} else {
				alert(res.data?.message || "투입 실패");
			}
		} catch {
			alert("투입 중 오류");
		}
		setSaving(false);
	};

	if (loading) {
		return (
			<div className="text-center py-8 text-gray-400">자재 정보 조회중...</div>
		);
	}

	return (
		<div className="space-y-3">
			{/* BOM 기준 자재 목록 — 컴팩트 */}
			<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-3">
				<div className="flex items-center justify-between mb-2">
					<h3 className="text-sm font-bold text-gray-900">BOM 자재 목록</h3>
					<span className="text-xs text-gray-400">{bomMaterials.length}건</span>
				</div>
				{bomMaterials.length === 0 ? (
					<p className="text-sm text-gray-400 py-4 text-center">
						BOM 자재 정보가 없습니다
					</p>
				) : (
					<div>
						<div className="divide-y divide-gray-200">
							{bomMaterials.map((m) => (
								<div key={m.id} className="flex items-center gap-2 py-3">
									{/* 자재명(코드) + 소요량 */}
									<div className="flex-1 min-w-0">
										<span className="text-base font-bold text-gray-900">{m.child_item_name}</span>
										<span className="text-sm text-gray-400 ml-1">({m.child_item_code})</span>
										<span className="text-base font-bold text-blue-600 ml-3">소요 {m.required_qty}</span>
									</div>
									{/* 입력 버튼 + 단위 */}
									<MaterialQtyInputRow
										material={m}
										value={inputValues[m.id] || ""}
										onChange={(v) =>
											setInputValues((prev) => ({ ...prev, [m.id]: v }))
										}
									/>
									<span className="text-base font-semibold text-gray-500 shrink-0 w-8">{m.unit}</span>
								</div>
							))}
						</div>
						<button
							onClick={handleSave}
							disabled={saving}
							className="w-full mt-4 py-4 rounded-xl text-lg font-bold text-white active:scale-[0.98] transition-all disabled:opacity-40"
							style={{
								background: "linear-gradient(135deg, #3b82f6, #1d4ed8)",
							}}
						>
							{saving ? "저장중..." : "자재 투입 확정"}
						</button>
					</div>
				)}
			</div>

			{/* 투입 이력 */}
			{inputted.length > 0 && (
				<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4">
					<h3 className="text-base font-bold text-gray-900 mb-3">투입 이력</h3>
					{inputted.map((item) => (
						<div
							key={item.id}
							className="flex items-center justify-between py-2 border-b border-gray-100 last:border-0"
						>
							<div>
								<p className="text-sm font-semibold text-gray-900">
									{item.item_name}
								</p>
								<p className="text-xs text-gray-400">{item.item_code}</p>
							</div>
							<p className="text-base font-bold text-green-600">
								{item.input_qty} {item.unit}
							</p>
						</div>
					))}
				</div>
			)}
		</div>
	);
}
