"use client";

import { useRouter } from "next/navigation";
import React, {
	useCallback,
	useEffect,
	useMemo,
	useRef,
	useState,
} from "react";
import { useAuth } from "@/hooks/useAuth";
import { apiClient } from "@/lib/api/client";
import { dataApi } from "@/lib/api/data";
import { ConfirmModal } from "../common/ConfirmModal";
import { AcceptProcessModal } from "./AcceptProcessModal";
import { ProcessDetailModal, type ProcessStep } from "./ProcessDetailModal";
import { ProcessWork } from "./ProcessWork";

/* 텍스트가 넘칠 때 자동 슬라이드 (마키) */
function AutoScrollText({
	children,
	className,
}: {
	children: React.ReactNode;
	className?: string;
}) {
	const outerRef = useRef<HTMLDivElement>(null);
	const innerRef = useRef<HTMLSpanElement>(null);
	const [needsScroll, setNeedsScroll] = useState(false);

	useEffect(() => {
		const check = () => {
			if (outerRef.current && innerRef.current) {
				setNeedsScroll(
					innerRef.current.scrollWidth > outerRef.current.clientWidth + 2,
				);
			}
		};
		check();
		window.addEventListener("resize", check);
		return () => window.removeEventListener("resize", check);
	}, [children]);

	return (
		<div
			ref={outerRef}
			className={`overflow-hidden whitespace-nowrap ${className || ""}`}
		>
			<span
				ref={innerRef}
				className="inline-block"
				style={
					needsScroll
						? { animation: "cardMarquee 8s linear infinite" }
						: undefined
				}
			>
				{children}
				{needsScroll && <span className="inline-block w-12" />}
				{needsScroll && children}
			</span>
			{needsScroll && (
				<style>{`@keyframes cardMarquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }`}</style>
			)}
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Types                                                               */
/* ------------------------------------------------------------------ */

interface WorkInstruction {
	id: string;
	work_instruction_no: string;
	item_id: string;
	item_name: string;
	item_code: string;
	item_number: string;
	qty: number;
	completed_qty: number;
	status: string;
	progress_status: string;
	routing: string | null;
	start_date: string;
	end_date: string;
	equipment_id: string;
	work_team: string;
	worker: string;
}

interface WorkOrderProcess {
	id: string;
	wo_id: string;
	seq_no: string;
	process_code: string;
	process_name: string;
	status: "acceptable" | "waiting" | "in_progress" | "completed";
	plan_qty: string;
	input_qty: string;
	good_qty: string;
	defect_qty: string;
	concession_qty: string;
	total_production_qty: string;
	parent_process_id: string | null;
	is_rework: string;
	rework_source_id: string | null;
	result_status: string;
	started_at: string | null;
	completed_at: string | null;
	accepted_by?: string;
	accepted_at?: string | null;
	created_date?: string;
	batch_id?: string | null;
	equipment_code?: string;
}

interface ProcessMng {
	id: string;
	process_code: string;
	process_name: string;
}

interface EquipmentMng {
	id: string;
	equipment_code: string;
	equipment_name: string;
}

type TabFilter = "all" | "acceptable" | "in_progress" | "waiting" | "completed";

/* ------------------------------------------------------------------ */
/* Constants                                                           */
/* ------------------------------------------------------------------ */

const CARD_COLS_KEY = "workorder-card-cols";
const DEFAULT_COLS = 1;

const TABS: {
	key: TabFilter;
	label: string;
	color: string;
	bgActive: string;
}[] = [
	{
		key: "acceptable",
		label: "접수가능",
		color: "text-amber-700",
		bgActive: "bg-amber-50",
	},
	{
		key: "in_progress",
		label: "진행중",
		color: "text-blue-700",
		bgActive: "bg-blue-50",
	},
	{
		key: "waiting",
		label: "대기",
		color: "text-gray-500",
		bgActive: "bg-gray-50",
	},
	{
		key: "completed",
		label: "완료",
		color: "text-green-700",
		bgActive: "bg-green-50",
	},
];

const STATUS_BADGE: Record<
	string,
	{ bg: string; text: string; label: string; dot: string; prefix: string }
> = {
	acceptable: {
		bg: "bg-amber-100",
		text: "text-amber-700",
		label: "접수가능",
		dot: "bg-amber-500",
		prefix: "",
	},
	waiting: {
		bg: "bg-gray-100",
		text: "text-gray-500",
		label: "대기",
		dot: "bg-gray-400",
		prefix: "",
	},
	in_progress: {
		bg: "bg-blue-100",
		text: "text-blue-700",
		label: "진행중",
		dot: "bg-blue-500",
		prefix: "\u25CF ",
	},
	completed: {
		bg: "bg-green-100",
		text: "text-green-700",
		label: "완료",
		dot: "bg-green-500",
		prefix: "\u2713 ",
	},
};

const BORDER_LEFT_COLOR: Record<string, string> = {
	acceptable: "border-l-amber-400",
	in_progress: "border-l-blue-500",
	waiting: "border-l-gray-300",
	completed: "border-l-green-500",
};

const COLS_OPTIONS = [1, 2, 3] as const;
const COLS_GRID_CLASS: Record<number, string> = {
	1: "grid-cols-1",
	2: "grid-cols-1 sm:grid-cols-2",
	3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
};

/* ------------------------------------------------------------------ */
/* Fullscreen Work Modal with My-Work Drawer                           */
/* ------------------------------------------------------------------ */

function FullscreenWorkModal({
	processId,
	myProcesses,
	instructionMap,
	itemNameMap,
	multiBatchInfo,
	onSwitch,
	onClose,
}: {
	processId: string;
	myProcesses: WorkOrderProcess[];
	instructionMap: Record<string, WorkInstruction>;
	itemNameMap: Record<string, string>;
	multiBatchInfo: Record<string, { isMulti: boolean; index: number; total: number; itemType: string }>;
	onSwitch: (id: string) => void;
	onClose: () => void;
}) {
	const [drawerOpen, setDrawerOpen] = React.useState(false);

	return (
		<div className="fixed inset-0 z-[100] bg-white flex">
			{/* Drawer tab handle (left edge, middle) */}
			<button
				onClick={() => setDrawerOpen((v) => !v)}
				className={`absolute left-0 top-1/2 -translate-y-1/2 z-[115] h-20 w-5 flex items-center justify-center transition-all ${
					drawerOpen ? "left-[280px]" : "left-0"
				}`}
				style={{
					background: "#6d28d9",
					borderRadius: "0 8px 8px 0",
					boxShadow: "2px 0 8px rgba(0,0,0,0.15)",
				}}
			>
				<svg
					className={`w-3 h-3 text-white transition-transform ${drawerOpen ? "rotate-180" : ""}`}
					fill="none"
					stroke="currentColor"
					strokeWidth={3}
					viewBox="0 0 24 24"
				>
					<path
						strokeLinecap="round"
						strokeLinejoin="round"
						d="M8.25 4.5l7.5 7.5-7.5 7.5"
					/>
				</svg>
			</button>

			{/* Drawer overlay */}
			{drawerOpen && (
				<div
					className="absolute inset-0 bg-black/20 z-[111]"
					onClick={() => setDrawerOpen(false)}
				/>
			)}

			{/* Drawer panel */}
			<div
				className={`absolute left-0 top-0 bottom-0 z-[112] bg-white border-r border-gray-200 shadow-xl transition-transform duration-300 flex flex-col ${
					drawerOpen ? "translate-x-0" : "-translate-x-full"
				}`}
				style={{ width: 280 }}
			>
				<div className="p-4 border-b border-gray-100">
					<h3 className="text-base font-bold text-gray-900">내 접수 목록</h3>
					<p className="text-xs text-gray-400 mt-1">{myProcesses.length}건</p>
				</div>
				<div className="flex-1 overflow-y-auto p-2">
					{myProcesses.map((proc) => {
						const wi = instructionMap[proc.wo_id];
						const isActive = proc.id === processId;
						return (
							<button
								key={proc.id}
								onClick={() => {
									onSwitch(proc.id);
									setDrawerOpen(false);
								}}
								className={`w-full text-left p-3.5 rounded-xl mb-2 transition-all active:scale-[0.98] ${
									isActive
										? "bg-blue-50 border-2 border-blue-400"
										: "bg-gray-50 border border-gray-200 hover:bg-gray-100"
								}`}
							>
								<div className="text-base font-bold text-gray-900 mb-1">
									{wi?.work_instruction_no || "작업지시"}
								</div>
								{(() => {
									const bInfo = multiBatchInfo[proc.id];
									if (!bInfo) return null;
									const typeLabel = bInfo.itemType || "";
									return bInfo.isMulti ? (
										<span className="bg-blue-100 text-blue-700 text-xs font-bold px-2 py-0.5 rounded-full self-start mb-0.5">
											다중 {bInfo.index}/{bInfo.total}{typeLabel ? ` · ${typeLabel}` : ""}
										</span>
									) : (
										<span className="bg-gray-100 text-gray-600 text-xs font-bold px-2 py-0.5 rounded-full self-start mb-0.5">
											단일{typeLabel ? ` · ${typeLabel}` : ""}
										</span>
									);
								})()}
								<AutoScrollText className="text-sm text-gray-500 mb-0.5">
									📦 {proc.batch_id
										? `${itemNameMap[proc.batch_id] || proc.batch_id}(${proc.batch_id})`
										: `${wi?.item_name || ""}${wi?.item_code || wi?.item_number ? `(${wi?.item_code || wi?.item_number})` : ""}`}
								</AutoScrollText>
								<div className="text-sm text-gray-600 mb-0.5">
									{proc.process_name} · {proc.equipment_code || "미배정"}
								</div>
								<div className="text-sm font-semibold text-blue-600">
									접수 {proc.input_qty || 0}EA
								</div>
							</button>
						);
					})}
					{myProcesses.length === 0 && (
						<p className="text-sm text-gray-400 text-center py-8">
							접수한 작업이 없습니다
						</p>
					)}
				</div>
			</div>

			{/* Close button */}
			<button
				onClick={onClose}
				className="absolute top-4 right-4 z-[110] w-12 h-12 rounded-full bg-black/10 hover:bg-black/20 flex items-center justify-center active:scale-95 transition-all"
			>
				<svg
					className="w-6 h-6 text-gray-700"
					fill="none"
					stroke="currentColor"
					strokeWidth={2.5}
					viewBox="0 0 24 24"
				>
					<path
						strokeLinecap="round"
						strokeLinejoin="round"
						d="M6 18L18 6M6 6l12 12"
					/>
				</svg>
			</button>

			{/* ProcessWork content */}
			<div className="flex-1 overflow-auto">
				<ProcessWork key={processId} processId={processId} />
			</div>
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Filter Selector Modal                                               */
/* ------------------------------------------------------------------ */

function FilterSelectorModal({
	open,
	onClose,
	title,
	options,
	selectedValue,
	onSelect,
}: {
	open: boolean;
	onClose: () => void;
	title: string;
	options: { value: string; label: string }[];
	selectedValue: string;
	onSelect: (value: string) => void;
}) {
	if (!open) return null;

	return (
		<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
			<div className="absolute inset-0 bg-black/40" onClick={onClose} />
			<div className="relative bg-white w-full max-w-md rounded-t-2xl sm:rounded-2xl shadow-2xl z-10 max-h-[70vh] flex flex-col">
				<div className="flex items-center justify-between px-5 py-4 border-b border-gray-100">
					<h3 className="text-base font-bold text-gray-900">{title}</h3>
					<button
						onClick={onClose}
						className="w-8 h-8 rounded-lg bg-gray-100 flex items-center justify-center text-gray-400 hover:bg-gray-200 active:scale-95 transition-all"
					>
						<svg
							className="w-4 h-4"
							fill="none"
							stroke="currentColor"
							strokeWidth={2.5}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M6 18L18 6M6 6l12 12"
							/>
						</svg>
					</button>
				</div>
				<div className="flex-1 overflow-y-auto p-3">
					<div className="flex flex-col gap-1.5">
						{options.map((opt) => {
							const isSelected = opt.value === selectedValue;
							return (
								<button
									key={opt.value}
									onClick={() => {
										onSelect(opt.value);
										onClose();
									}}
									className={`w-full text-left px-4 py-3.5 rounded-xl text-sm font-semibold transition-all active:scale-[0.98] ${
										isSelected
											? "bg-blue-50 text-blue-700 ring-2 ring-blue-200"
											: "bg-gray-50 text-gray-700 hover:bg-gray-100"
									}`}
								>
									<div className="flex items-center justify-between">
										<span>{opt.label}</span>
										{isSelected && (
											<svg
												className="w-5 h-5 text-blue-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>
								</button>
							);
						})}
					</div>
				</div>
			</div>
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Compressed Process Steps (center-aligned)                           */
/* ------------------------------------------------------------------ */

function CompressedProcessSteps({
	processes,
	currentSeqNo,
	status,
	onClick,
	batchId,
	allProcesses,
}: {
	processes: WorkOrderProcess[];
	currentSeqNo: string;
	status: string;
	onClick?: () => void;
	batchId?: string;
	allProcesses?: WorkOrderProcess[];
}) {
	const sorted = [...processes]
		.filter((p) => !p.parent_process_id && (
			// 같은 batch_id끼리만 표시 (다중 품목 구분)
			(!batchId && !p.batch_id) ||
			(batchId && p.batch_id === batchId)
		))
		.sort((a, b) => parseInt(a.seq_no, 10) - parseInt(b.seq_no, 10));

	if (sorted.length === 0) return null;

	const currentIdx = sorted.findIndex((p) => p.seq_no === currentSeqNo);
	if (currentIdx < 0) return null;

	// For completed status: batch_id 기반 진행률 표시
	if (status === "completed") {
		// 같은 batch_id를 가진 SPLIT들이 어느 seq까지 완료했는지 추적
		let maxCompletedSeq = parseInt(currentSeqNo, 10); // 최소한 현재 seq까지는 완료

		if (batchId && allProcesses) {
			const batchSplits = allProcesses.filter(
				(p) =>
					p.batch_id === batchId &&
					p.parent_process_id &&
					p.status === "completed",
			);
			for (const s of batchSplits) {
				const sSeq = parseInt(s.seq_no, 10);
				if (sSeq > maxCompletedSeq) maxCompletedSeq = sSeq;
			}
		}

		const completedCount = sorted.filter(
			(p) => parseInt(p.seq_no, 10) <= maxCompletedSeq,
		).length;
		const allDone = completedCount === sorted.length;

		return (
			<div
				className="flex items-center justify-center gap-0.5 mb-3 py-2 px-3 bg-green-50 rounded-xl cursor-pointer hover:bg-green-100 transition"
				onClick={onClick}
			>
				{sorted.map((proc, idx) => {
					const seqNum = parseInt(proc.seq_no, 10);
					const isDone = seqNum <= maxCompletedSeq;
					return (
						<React.Fragment key={proc.id}>
							{idx > 0 && (
								<span
									className={`w-3 h-0.5 ${isDone ? "bg-green-400" : "bg-gray-300"} shrink-0`}
								/>
							)}
							<span
								className={`rounded-full flex items-center justify-center shrink-0 text-[9px] font-bold ${
									isDone
										? "bg-green-500 text-white"
										: "bg-gray-200 text-gray-400"
								}`}
								style={{
									width: idx === currentIdx ? 28 : 24,
									height: idx === currentIdx ? 28 : 24,
									fontSize: idx === currentIdx ? 10 : 9,
								}}
							>
								{isDone ? "\u2713" : idx + 1}
							</span>
						</React.Fragment>
					);
				})}
				<span className="text-[10px] text-green-600 font-bold ml-2">
					{allDone ? "전체 완료" : `${completedCount}/${sorted.length} 완료`}
				</span>
			</div>
		);
	}

	// Compressed view: show prev, current, next + collapsed counts
	const prevIdx = currentIdx - 1;
	const nextIdx = currentIdx + 1;
	const beforeCollapsed = prevIdx > 0 ? prevIdx : 0;
	const afterCollapsed =
		sorted.length - 1 - (nextIdx < sorted.length ? nextIdx : currentIdx);

	// 공정명 축약 (4글자 초과 시 잘라내기)
	const shortName = (name: string) => {
		if (!name) return "?";
		const clean = name.replace(/^제조반_/, "");
		return clean.length > 4 ? clean.slice(0, 4) : clean;
	};

	const stepLabel = (proc: WorkOrderProcess, isCurrent: boolean) => {
		const isCompleted = proc.status === "completed";
		const isInProgress =
			proc.status === "in_progress" || proc.status === "acceptable";

		if (isCurrent) {
			const bgColor = isInProgress
				? "bg-blue-500"
				: isCompleted
					? "bg-green-500"
					: "bg-amber-400";
			const ringColor = isInProgress
				? "ring-blue-200"
				: isCompleted
					? "ring-green-200"
					: "ring-amber-200";
			return (
				<span
					className={`rounded-lg ${bgColor} text-white ring-2 ${ringColor} flex items-center justify-center shrink-0 font-bold px-3 ${isInProgress ? "animate-pulse" : ""}`}
					style={{ height: 36, fontSize: 14, whiteSpace: "nowrap" }}
				>
					{shortName(proc.process_name)}
				</span>
			);
		}

		if (isCompleted) {
			return (
				<span
					className="rounded-lg bg-green-100 text-green-700 border border-green-400 flex items-center justify-center shrink-0 font-bold px-2.5"
					style={{ height: 32, fontSize: 13, whiteSpace: "nowrap" }}
				>
					{shortName(proc.process_name)}
				</span>
			);
		}

		return (
			<span
				className="rounded-lg bg-gray-100 text-gray-500 border border-gray-300 flex items-center justify-center shrink-0 font-bold px-2.5"
				style={{ height: 32, fontSize: 13, whiteSpace: "nowrap" }}
			>
				{shortName(proc.process_name)}
			</span>
		);
	};

	const lineBetween = (
		fromProc: WorkOrderProcess,
		toProc: WorkOrderProcess,
	) => {
		const fromDone = fromProc.status === "completed";
		const toDone = toProc.status === "completed";
		const color = fromDone || toDone ? "bg-green-400" : "bg-gray-200";
		return <span className={`w-3 h-0.5 ${color} shrink-0`} />;
	};

	// 모든 상태에서 클릭 가능 (공정 상세 모달)
	const isClickable = !!onClick;

	return (
		<div
			className={`flex items-center justify-center gap-1 mb-3 py-3 px-4 bg-gray-50 rounded-xl transition ${
				isClickable ? "cursor-pointer hover:bg-gray-100" : ""
			}`}
			onClick={isClickable ? onClick : undefined}
		>
			{/* Collapsed before */}
			{beforeCollapsed > 0 && (
				<>
					<span
						className="rounded-lg bg-green-100 text-green-700 border border-green-300 flex items-center justify-center shrink-0 text-xs font-bold px-2"
						style={{ height: 32 }}
					>
						+{beforeCollapsed}
					</span>
					<span className="w-3 h-0.5 bg-green-400 shrink-0" />
				</>
			)}

			{/* Previous step */}
			{prevIdx >= 0 && (
				<>
					{stepLabel(sorted[prevIdx], false)}
					<span className="w-3 h-0.5 bg-gray-300 shrink-0" />
				</>
			)}

			{/* Current step */}
			{stepLabel(sorted[currentIdx], true)}

			{/* Next step */}
			{nextIdx < sorted.length && (
				<>
					<span className="w-3 h-0.5 bg-gray-300 shrink-0" />
					{stepLabel(sorted[nextIdx], false)}
				</>
			)}

			{/* Collapsed after */}
			{afterCollapsed > 0 && (
				<>
					<span className="w-3 h-0.5 bg-gray-200 shrink-0" />
					<span
						className="rounded-lg bg-gray-200 text-gray-500 flex items-center justify-center shrink-0 text-xs font-bold px-2"
						style={{ height: 32 }}
					>
						+{afterCollapsed}
					</span>
				</>
			)}

			{/* Chevron for clickable */}
			{isClickable && (
				<svg
					className="w-4 h-4 text-gray-400 ml-2 shrink-0"
					fill="none"
					stroke="currentColor"
					strokeWidth={2}
					viewBox="0 0 24 24"
				>
					<path
						strokeLinecap="round"
						strokeLinejoin="round"
						d="M8.25 4.5l7.5 7.5-7.5 7.5"
					/>
				</svg>
			)}
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Card sub-components by status                                       */
/* ------------------------------------------------------------------ */

/** Acceptable card body */
function AcceptableCardBody({
	planQty,
	prevGoodQty,
	availableQty,
	reworkAvailableQty,
}: {
	planQty: number;
	prevGoodQty: number | null;
	availableQty: number;
	reworkAvailableQty?: number;
}) {
	return (
		<>
			<div className="grid grid-cols-3 gap-2 mb-3">
				<div className="text-center bg-gray-50 rounded-xl py-4">
					<div className="text-sm text-gray-400">지시수량</div>
					<div className="text-3xl font-extrabold text-gray-900">
						{planQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-emerald-50 rounded-xl py-4">
					<div className="text-sm text-emerald-500">전공정양품</div>
					<div className="text-3xl font-extrabold text-emerald-600">
						{prevGoodQty !== null ? prevGoodQty.toLocaleString() : "-"}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-4">
					<div className="text-sm text-blue-500">접수가능</div>
					<div
						className="font-extrabold text-blue-600"
						style={{ fontSize: 28 }}
					>
						{availableQty.toLocaleString()}
					</div>
				</div>
			</div>
			{reworkAvailableQty && reworkAvailableQty > 0 ? (
				reworkAvailableQty >= availableQty ? null : (
					<div className="flex items-center gap-2 mb-3 px-3 py-2 bg-amber-50 border border-amber-200 rounded-xl">
						<span className="text-xs font-bold text-white bg-amber-500 px-2 py-0.5 rounded-full">
							리워크
						</span>
						<span className="text-xs text-amber-700 font-medium">
							재작업 물량 {reworkAvailableQty}개 포함
						</span>
					</div>
				)
			) : null}
		</>
	);
}

/** In-progress card body */
function InProgressCardBody({
	inputQty,
	goodQty,
	defectQty,
	remainQty,
	progressPct,
	additionalAvailable,
}: {
	inputQty: number;
	goodQty: number;
	defectQty: number;
	remainQty: number;
	progressPct: number;
	additionalAvailable: number;
}) {
	return (
		<>
			{/* Progress bar */}
			<div className="flex items-center gap-2 mb-3">
				<div className="h-2.5 flex-1 bg-gray-100 rounded-full overflow-hidden">
					<div
						className="h-full bg-blue-500 rounded-full transition-all"
						style={{ width: `${progressPct}%` }}
					/>
				</div>
				<span className="text-xs font-bold text-blue-600">{progressPct}%</span>
			</div>

			{/* 4-col qty grid */}
			<div className="grid grid-cols-4 gap-1.5 mb-2">
				<div className="text-center bg-blue-50 rounded-xl py-3">
					<div className="text-sm text-blue-500">접수</div>
					<div className="text-3xl font-extrabold text-blue-700">
						{inputQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-emerald-50 rounded-xl py-3">
					<div className="text-sm text-emerald-500">양품</div>
					<div className="text-3xl font-extrabold text-emerald-700">
						{goodQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-red-50 rounded-xl py-3">
					<div className="text-sm text-red-500">불량</div>
					<div className="text-3xl font-extrabold text-red-700">
						{defectQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-amber-50 rounded-xl py-3">
					<div className="text-sm text-amber-500">잔여</div>
					<div className="text-3xl font-extrabold text-amber-700">
						{remainQty.toLocaleString()}
					</div>
				</div>
			</div>
			{additionalAvailable > 0 && (
				<div className="text-right text-sm text-violet-600 font-semibold">
					추가접수가능 {additionalAvailable.toLocaleString()}
				</div>
			)}
		</>
	);
}

/** Waiting card body */
function WaitingCardBody({
	planQty,
	prevProcessName,
	prevProgressPct,
	currentProcessName,
	currentSeqNo,
}: {
	planQty: number;
	prevProcessName: string | null;
	prevProgressPct: number | null;
	currentProcessName: string;
	currentSeqNo: number;
}) {
	return (
		<>
			<div className="grid grid-cols-3 gap-2 mb-3">
				<div className="text-center bg-gray-50 rounded-xl py-3">
					<div className="text-sm text-gray-400">지시수량</div>
					<div className="text-3xl font-extrabold text-gray-900">
						{planQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-3">
					<div className="text-sm text-blue-500">이전공정</div>
					<div className="text-base font-bold text-blue-600 truncate">
						{prevProcessName || "-"}
					</div>
					{prevProgressPct !== null && (
						<div className="text-xs text-blue-400 mt-1">
							진행중 ({prevProgressPct}%)
						</div>
					)}
				</div>
				<div className="text-center bg-gray-50 rounded-xl py-3">
					<div className="text-sm text-gray-400">현재공정</div>
					<div className="text-base font-bold text-gray-600 truncate">
						{currentProcessName}
					</div>
					<div className="text-xs text-gray-400 mt-1">
						{currentSeqNo}번째 공정
					</div>
				</div>
			</div>
			<div className="text-center text-sm text-gray-400">
				이전공정 완료 시 접수 가능합니다
			</div>
		</>
	);
}

/** Completed card body */
function CompletedCardBody({
	goodQty,
	defectQty,
	planQty,
	inputQty,
}: {
	goodQty: number;
	defectQty: number;
	planQty: number;
	inputQty: number;
}) {
	return (
		<div className="grid grid-cols-4 gap-1.5">
			<div className="text-center bg-emerald-50 rounded-xl py-3">
				<div className="text-sm text-emerald-500">양품</div>
				<div className="text-3xl font-extrabold text-emerald-700">
					{goodQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-red-50 rounded-xl py-3">
				<div className="text-sm text-red-500">불량</div>
				<div className="text-3xl font-extrabold text-red-700">
					{defectQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-gray-50 rounded-xl py-3">
				<div className="text-sm text-gray-400">지시</div>
				<div className="text-3xl font-extrabold text-gray-600">
					{planQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-blue-50 rounded-xl py-3">
				<div className="text-sm text-blue-500">접수</div>
				<div className="text-3xl font-extrabold text-blue-700">
					{inputQty.toLocaleString()}
				</div>
			</div>
		</div>
	);
}

/** Rework card body */
function ReworkCardBody({
	reworkQty,
	availableQty,
	originProcessName,
	originProcessCode,
	originDefectQty,
	reworkRound,
}: {
	reworkQty: number;
	availableQty: number;
	originProcessName: string;
	originProcessCode: string;
	originDefectQty: number;
	reworkRound: number;
}) {
	return (
		<>
			{/* Rework info summary */}
			<div className="mb-3 p-2.5 bg-orange-50 border border-orange-200 rounded-xl">
				<div className="flex items-center gap-1.5 text-sm font-semibold text-orange-700">
					<span>{originProcessName || originProcessCode}</span>
					<span className="text-orange-400">&rarr;</span>
					<span>불량 {originDefectQty}개</span>
					<span className="text-orange-400">&rarr;</span>
					<span className="bg-orange-500 text-white text-xs px-1.5 py-0.5 rounded font-bold">
						재작업 {reworkRound}회차
					</span>
				</div>
			</div>

			<div className="grid grid-cols-2 gap-3 mb-3">
				<div className="text-center bg-orange-50 rounded-xl py-4">
					<div className="text-sm text-orange-500">재작업수량</div>
					<div
						className="font-extrabold text-orange-600"
						style={{ fontSize: 32 }}
					>
						{reworkQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-4">
					<div className="text-sm text-blue-500">접수가능</div>
					<div
						className="font-extrabold text-blue-600"
						style={{ fontSize: 32 }}
					>
						{availableQty.toLocaleString()}
					</div>
				</div>
			</div>
		</>
	);
}

/* ------------------------------------------------------------------ */
/* Main Component                                                      */
/* ------------------------------------------------------------------ */

export function WorkOrderList() {
	const router = useRouter();
	const { user } = useAuth();
	const currentUserId = user?.userId || "";

	/* ---- State ---- */
	const [instructions, setInstructions] = useState<WorkInstruction[]>([]);
	const [allProcesses, setAllProcesses] = useState<WorkOrderProcess[]>([]);
	const [processList, setProcessList] = useState<ProcessMng[]>([]);
	const [equipmentList, setEquipmentList] = useState<EquipmentMng[]>([]);
	const [itemNameMap, setItemNameMap] = useState<Record<string, string>>({});
	const [itemTypeMap, setItemTypeMap] = useState<Record<string, string>>({});
	const [loading, setLoading] = useState(true);
	const [syncing, setSyncing] = useState(false);
	const [activeTab, setActiveTab] = useState<TabFilter>("acceptable");

	/* Column layout */
	const [cardCols, setCardCols] = useState<number>(DEFAULT_COLS);

	/* Filter state */
	const [selectedProcess, setSelectedProcess] = useState<string>("__all__");
	const [selectedEquipment, setSelectedEquipment] = useState<string>("__all__");
	const [processModalOpen, setProcessModalOpen] = useState(false);
	const [equipmentModalOpen, setEquipmentModalOpen] = useState(false);

	/* Accept Modal */
	const [acceptModal, setAcceptModal] = useState<{
		open: boolean;
		processId: string;
		processName: string;
		seqNo: string;
		maxQty: number;
		reworkSourceId?: string;
	}>({ open: false, processId: "", processName: "", seqNo: "", maxQty: 0 });
	const [acceptLoading, setAcceptLoading] = useState(false);

	const [cancelConfirm, setCancelConfirm] = useState<{
		open: boolean;
		processId: string;
	}>({ open: false, processId: "" });

	/* Process Detail Modal */
	const [detailModal, setDetailModal] = useState<{
		open: boolean;
		wiNo: string;
		totalQty: number;
		steps: ProcessStep[];
		woId?: string;
		showReworkHistory?: boolean;
	}>({ open: false, wiNo: "", totalQty: 0, steps: [] });

	/* ---- Load saved column preference ---- */
	useEffect(() => {
		try {
			const saved = localStorage.getItem(CARD_COLS_KEY);
			if (saved) {
				const n = parseInt(saved, 10);
				if (COLS_OPTIONS.includes(n as 1 | 2 | 3)) setCardCols(n);
			}
		} catch {
			// ignore
		}
	}, []);

	const handleSetCols = (n: number) => {
		setCardCols(n);
		try {
			localStorage.setItem(CARD_COLS_KEY, String(n));
		} catch {
			// ignore
		}
	};

	/* ---- Data Fetching ---- */
	const fetchAll = useCallback(async () => {
		setLoading(true);
		try {
			try {
				await apiClient.post("/pop/production/sync-work-instructions");
			} catch {
				// Non-fatal
			}

			const wiRes = await apiClient.get("/work-instruction/list");
			let wiRaw: Record<string, unknown>[] = [];
			if (wiRes.data?.data) {
				wiRaw = Array.isArray(wiRes.data.data)
					? wiRes.data.data
					: wiRes.data.data.rows || [];
			} else if (Array.isArray(wiRes.data)) {
				wiRaw = wiRes.data;
			}
			// wi_id → id 매핑 + 중복 제거 (header+detail 조인이므로 첫 행만)
			// item_number → item_name 매핑 구축 (다중 품목 표시용)
			const seen = new Set<string>();
			const wiData: WorkInstruction[] = [];
			const newItemNameMap: Record<string, string> = {};
			const newItemTypeMap: Record<string, string> = {};
			for (const raw of wiRaw) {
				const wiId = String(raw.wi_id || raw.id || "");
				// item_number → item_name / item_type 매핑 (모든 행에서 수집)
				const rawItemNumber = String(raw.item_number || "");
				const rawItemName = String(raw.item_name || "");
				const rawItemType = String(raw.item_type || "");
				if (rawItemNumber && rawItemName) {
					newItemNameMap[rawItemNumber] = rawItemName;
				}
				if (rawItemNumber && rawItemType) {
					newItemTypeMap[rawItemNumber] = rawItemType;
				}
				if (!wiId || seen.has(wiId)) continue;
				seen.add(wiId);
				wiData.push({
					...raw,
					id: wiId,
					item_name: rawItemName,
					item_code: String(raw.item_code || ""),
					item_number: rawItemNumber,
					qty: parseInt(String(raw.total_qty || raw.qty || 0), 10),
				} as unknown as WorkInstruction);
			}
			setInstructions(wiData);
			setItemNameMap(newItemNameMap);
			setItemTypeMap(newItemTypeMap);

			const procRes = await dataApi.getTableData("work_order_process", {
				size: 1000,
			});
			setAllProcesses((procRes.data ?? []) as WorkOrderProcess[]);

			try {
				const pmRes = await dataApi.getTableData("process_mng", { size: 500 });
				setProcessList((pmRes.data ?? []) as ProcessMng[]);
			} catch {
				// Non-fatal
			}

			try {
				const eqRes = await dataApi.getTableData("equipment_mng", {
					size: 500,
				});
				setEquipmentList((eqRes.data ?? []) as EquipmentMng[]);
			} catch {
				// Non-fatal
			}
		} catch (error) {
			// eslint-disable-next-line no-console
			console.error("[WorkOrderList] fetchAll error:", error);
		} finally {
			setLoading(false);
		}
	}, []);

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

	const syncAndFetch = useCallback(async () => {
		setSyncing(true);
		try {
			await apiClient.post("/pop/production/sync-work-instructions");
		} catch {
			// Non-fatal
		} finally {
			setSyncing(false);
		}
		await fetchAll();
	}, [fetchAll]);

	/* ---- Lookup maps ---- */
	const instructionMap = useMemo(() => {
		const map: Record<string, WorkInstruction> = {};
		for (const wi of instructions) {
			map[wi.id] = wi;
		}
		return map;
	}, [instructions]);

	const processesByWo = useMemo(() => {
		const map: Record<string, WorkOrderProcess[]> = {};
		for (const proc of allProcesses) {
			if (!proc.wo_id) continue;
			if (!map[proc.wo_id]) map[proc.wo_id] = [];
			map[proc.wo_id].push(proc);
		}
		return map;
	}, [allProcesses]);

	/** 다중품목 판단: wo_id별 DISTINCT batch_id 집합 + 순번 매핑 */
	const multiBatchInfo = useMemo(() => {
		// wo_id → 고유 batch_id 목록 (마스터 행 기준)
		const woBatches: Record<string, string[]> = {};
		for (const proc of allProcesses) {
			if (proc.parent_process_id) continue; // 마스터만
			if (!proc.wo_id) continue;
			if (!woBatches[proc.wo_id]) woBatches[proc.wo_id] = [];
			const bid = proc.batch_id || "";
			if (bid && !woBatches[proc.wo_id].includes(bid)) {
				woBatches[proc.wo_id].push(bid);
			}
		}
		// proc.id → { isMulti, index, total, itemType }
		const info: Record<string, { isMulti: boolean; index: number; total: number; itemType: string }> = {};
		for (const proc of allProcesses) {
			if (!proc.wo_id) continue;
			const batches = woBatches[proc.wo_id] || [];
			const bid = proc.batch_id || "";
			const isMulti = batches.length > 1;
			const index = bid ? batches.indexOf(bid) + 1 : 1;
			const total = Math.max(batches.length, 1);
			// item_type: batch_id가 있으면 itemTypeMap에서, 없으면 WI의 item_number로
			let itemType = "";
			if (bid) {
				itemType = itemTypeMap[bid] || "";
			}
			if (!itemType) {
				const wi = instructionMap[proc.wo_id];
				if (wi?.item_number) {
					itemType = itemTypeMap[wi.item_number] || "";
				}
			}
			info[proc.id] = { isMulti, index, total, itemType };
		}
		return info;
	}, [allProcesses, itemTypeMap, instructionMap]);

	const masterProcesses = useMemo(() => {
		// 마스터 행 + 분할 행(진행중/완료/리워크) — 중복 제거
		const seen = new Set<string>();
		return allProcesses.filter((p) => {
			if (seen.has(p.id)) return false;
			const include =
				!p.parent_process_id || // 마스터 행
				p.status === "in_progress" ||
				p.status === "completed" || // 분할 행
				p.is_rework === "Y" ||
				p.is_rework === "true" ||
				p.is_rework === "1"; // 재작업
			if (include) seen.add(p.id);
			return include;
		});
	}, [allProcesses]);

	const equipmentMap = useMemo(() => {
		const map: Record<string, EquipmentMng> = {};
		for (const eq of equipmentList) {
			map[eq.id] = eq;
			if (eq.equipment_code) map[eq.equipment_code] = eq;
		}
		return map;
	}, [equipmentList]);

	/* ---- Filter options ---- */
	const processFilterOptions = useMemo(() => {
		const opts: { value: string; label: string }[] = [
			{ value: "__all__", label: "공정을 선택하세요" },
		];
		const seen = new Set<string>();
		for (const proc of masterProcesses) {
			if (proc.process_code && !seen.has(proc.process_code)) {
				seen.add(proc.process_code);
				const pmEntry = processList.find(
					(pm) => pm.process_code === proc.process_code,
				);
				opts.push({
					value: proc.process_code,
					label:
						pmEntry?.process_name || proc.process_name || proc.process_code,
				});
			}
		}
		return opts;
	}, [masterProcesses, processList]);

	const equipmentFilterOptions = useMemo(() => {
		const opts: { value: string; label: string }[] = [
			{ value: "__all__", label: "전체 설비" },
		];
		const seen = new Set<string>();
		for (const eq of equipmentList) {
			if (eq.equipment_code && !seen.has(eq.equipment_code)) {
				seen.add(eq.equipment_code);
				opts.push({
					value: eq.equipment_code,
					label: eq.equipment_name || eq.equipment_code,
				});
			}
		}
		return opts;
	}, [equipmentList]);

	const selectedProcessLabel =
		processFilterOptions.find((o) => o.value === selectedProcess)?.label ||
		"전체 공정";
	const selectedEquipmentLabel =
		equipmentFilterOptions.find((o) => o.value === selectedEquipment)?.label ||
		"전체 설비";

	/* ---- Filtered processes ---- */
	const filteredProcesses = useMemo(() => {
		if (selectedProcess === "__all__") return []; // 공정 미선택 시 빈 목록
		return masterProcesses.filter((proc) => {
			const isRework =
				proc.is_rework === "Y" ||
				proc.is_rework === "true" ||
				proc.is_rework === "1";
			const isMaster = !proc.parent_process_id;
			// 완료/진행중 탭에서는 SPLIT만 표시 (마스터 제외)
			if (
				isMaster &&
				!isRework &&
				(activeTab === "completed" || activeTab === "in_progress")
			)
				return false;
			// 리워크 마스터가 in_progress/completed면 SPLIT이 생성된 것 → 리워크 마스터 숨김 (SPLIT은 표시)
			if (
				isRework &&
				!proc.parent_process_id &&
				(proc.status === "in_progress" || proc.status === "completed")
			)
				return false;
			// 재작업 카드는 공정 필터 무시 (모든 공정에서 표시)
			if (!isRework && proc.process_code !== selectedProcess) return false;
			if (selectedEquipment !== "__all__") {
				const wi = instructionMap[proc.wo_id];
				if (!wi) return false;
				const eqId = wi.equipment_id;
				const eq = equipmentMap[eqId];
				if (!eq || eq.equipment_code !== selectedEquipment) return false;
			}
			if (activeTab !== "all" && proc.status !== activeTab) return false;
			return true;
		});
	}, [
		masterProcesses,
		selectedProcess,
		selectedEquipment,
		activeTab,
		instructionMap,
		equipmentMap,
		currentUserId,
		allProcesses,
	]);

	/* ---- Tab counts ---- */
	const tabCounts = useMemo(() => {
		const preFiltered = masterProcesses.filter((proc) => {
			const isRework =
				proc.is_rework === "Y" ||
				proc.is_rework === "true" ||
				proc.is_rework === "1";
			// 재작업 카드는 공정 필터 무시 (모든 공정에서 표시)
			if (
				selectedProcess !== "__all__" &&
				!isRework &&
				proc.process_code !== selectedProcess
			)
				return false;
			if (selectedEquipment !== "__all__") {
				const wi = instructionMap[proc.wo_id];
				if (!wi) return false;
				const eq = equipmentMap[wi.equipment_id];
				if (!eq || eq.equipment_code !== selectedEquipment) return false;
			}
			return true;
		});

		const counts: Record<TabFilter, number> = {
			all: preFiltered.length,
			acceptable: 0,
			in_progress: 0,
			waiting: 0,
			completed: 0,
		};
		for (const proc of preFiltered) {
			const isMaster = !proc.parent_process_id;
			const isRw =
				proc.is_rework === "Y" ||
				proc.is_rework === "true" ||
				proc.is_rework === "1";
			// 리워크 마스터가 in_progress/completed면 SPLIT이 있으므로 카운트 제외
			if (
				isRw &&
				!proc.parent_process_id &&
				(proc.status === "in_progress" || proc.status === "completed")
			)
				continue;
			if (proc.status === "acceptable") counts.acceptable++;
			else if (proc.status === "in_progress" && (!isMaster || isRw))
				counts.in_progress++;
			else if (proc.status === "completed" && (!isMaster || isRw))
				counts.completed++;
			else counts.waiting++;
		}
		return counts;
	}, [
		masterProcesses,
		selectedProcess,
		selectedEquipment,
		instructionMap,
		equipmentMap,
	]);

	/* ---- Accept handler ---- */
	const openAcceptModal = async (
		processId: string,
		processName: string,
		seqNo: string,
		reworkSourceId?: string,
	) => {
		try {
			const res = await apiClient.get("/pop/production/available-qty", {
				params: { work_order_process_id: processId },
			});
			const data = res.data?.data;
			const maxQty = data?.availableQty ?? 0;
			setAcceptModal({
				open: true,
				processId,
				processName,
				seqNo,
				maxQty,
				reworkSourceId,
			});
		} catch {
			alert("접수가능량 조회 실패");
		}
	};

	const handleAccept = async (qty: number) => {
		setAcceptLoading(true);
		try {
			const body: Record<string, unknown> = {
				work_order_process_id: acceptModal.processId,
				accept_qty: qty,
			};
			// 리워크 추적: rework_source_id가 있으면 전달 (원점 복귀 추적)
			if (acceptModal.reworkSourceId) {
				body.rework_source_id = acceptModal.reworkSourceId;
			}
			const res = await apiClient.post("/pop/production/accept-process", body);
			if (res.data?.success) {
				setAcceptModal((m) => ({ ...m, open: false }));
				await fetchAll();
			} else {
				alert(res.data?.message || "접수 실패");
			}
		} catch (error: any) {
			alert(error.response?.data?.message || "접수 중 오류 발생");
		} finally {
			setAcceptLoading(false);
		}
	};

	/* ---- Open work detail as fullscreen modal ---- */
	const [workModalProcessId, setWorkModalProcessId] = useState<string | null>(
		null,
	);

	const goToWork = (processId: string) => {
		setWorkModalProcessId(processId);
	};

	/* ---- Open process detail modal ---- */
	const openDetailModal = (proc: WorkOrderProcess) => {
		const wi = instructionMap[proc.wo_id];
		const siblings = (processesByWo[proc.wo_id] || [])
			.filter((p) => !p.parent_process_id && (
				// 같은 batch_id끼리만 형제 (다중 품목 구분)
				(!proc.batch_id && !p.batch_id) ||
				(proc.batch_id && p.batch_id === proc.batch_id)
			))
			.sort((a, b) => parseInt(a.seq_no, 10) - parseInt(b.seq_no, 10));

		const totalQty = wi ? wi.qty : parseInt(proc.plan_qty || "0", 10);

		const steps: ProcessStep[] = siblings.map((s) => {
			const sInput = parseInt(s.input_qty || "0", 10);
			const sGood = parseInt(s.good_qty || "0", 10);
			const sDefect = parseInt(s.defect_qty || "0", 10);
			const sPlan = parseInt(s.plan_qty || "0", 10);
			// Available = plan - input (simplified)
			const avail = Math.max(0, sPlan - sInput);
			return {
				no: parseInt(s.seq_no, 10),
				name: s.process_name || s.process_code,
				code: s.process_code,
				status: s.status,
				inputQty: sInput,
				goodQty: sGood,
				defectQty: sDefect,
				planQty: sPlan,
				availableQty: avail,
			};
		});

		// Check if this wo has any rework processes
		const hasReworks = allProcesses.some(
			(p) =>
				p.wo_id === proc.wo_id &&
				(p.is_rework === "Y" || p.is_rework === "true" || p.is_rework === "1"),
		);

		setDetailModal({
			open: true,
			wiNo: wi?.work_instruction_no || "작업지시",
			totalQty,
			steps,
			woId: proc.wo_id,
			showReworkHistory: hasReworks,
		});
	};

	/* ---- Helper: get split order label (접수 #N) ---- */
	const splitOrderMap = useMemo(() => {
		// 같은 wo_id + seq_no를 가진 SPLIT들을 그룹화하여 순서 부여
		const groups: Record<string, WorkOrderProcess[]> = {};
		for (const proc of allProcesses) {
			if (!proc.parent_process_id) continue; // 마스터 행은 제외
			if (proc.status !== "in_progress" && proc.status !== "completed")
				continue;
			const key = `${proc.wo_id}__${proc.seq_no}`;
			if (!groups[key]) groups[key] = [];
			groups[key].push(proc);
		}

		const result: Record<string, { order: number; total: number }> = {};
		for (const key of Object.keys(groups)) {
			const splits = groups[key];
			if (splits.length <= 1) continue; // 1개면 순서 표시 불필요
			// accepted_at 기준 정렬 (없으면 started_at, 그마저 없으면 id)
			splits.sort((a, b) => {
				const ta = a.accepted_at
					? new Date(a.accepted_at).getTime()
					: a.started_at
						? new Date(a.started_at).getTime()
						: 0;
				const tb = b.accepted_at
					? new Date(b.accepted_at).getTime()
					: b.started_at
						? new Date(b.started_at).getTime()
						: 0;
				return ta - tb || a.id.localeCompare(b.id);
			});
			for (let i = 0; i < splits.length; i++) {
				result[splits[i].id] = { order: i + 1, total: splits.length };
			}
		}
		return result;
	}, [allProcesses]);

	/* ---- Helper: get previous process info ---- */
	const getPrevProcessInfo = (proc: WorkOrderProcess) => {
		const siblings = (processesByWo[proc.wo_id] || [])
			.filter((p) => !p.parent_process_id && (
				// 같은 batch_id끼리만 형제 (다중 품목 구분)
				(!proc.batch_id && !p.batch_id) ||
				(proc.batch_id && p.batch_id === proc.batch_id)
			))
			.sort((a, b) => parseInt(a.seq_no, 10) - parseInt(b.seq_no, 10));

		const currentIdx = siblings.findIndex((p) => p.id === proc.id);
		if (currentIdx <= 0)
			return {
				prevGoodQty: null as number | null,
				prevProcessName: null as string | null,
				prevProgressPct: null as number | null,
			};

		const prev = siblings[currentIdx - 1];
		const prevGood = parseInt(prev.good_qty || "0", 10);
		const prevPlan = parseInt(prev.plan_qty || "0", 10);
		const prevPct = prevPlan > 0 ? Math.round((prevGood / prevPlan) * 100) : 0;
		// 앞공정에서 리워크로 완료된 양품 수량
		const prevSeqNo = prev.seq_no;
		const reworkGoodFromPrev = allProcesses
			.filter(
				(p) =>
					p.wo_id === proc.wo_id &&
					p.seq_no === prevSeqNo &&
					p.parent_process_id &&
					p.status === "completed" &&
					(p.is_rework === "Y" ||
						p.is_rework === "true" ||
						p.is_rework === "1"),
			)
			.reduce((sum, p) => sum + parseInt(p.good_qty || "0", 10), 0);
		// 현재 공정에서 이미 리워크로 접수된 수량
		const reworkConsumedHere = allProcesses
			.filter(
				(p) =>
					p.wo_id === proc.wo_id &&
					p.seq_no === proc.seq_no &&
					p.parent_process_id &&
					(p.is_rework === "Y" ||
						p.is_rework === "true" ||
						p.is_rework === "1"),
			)
			.reduce((sum, p) => sum + parseInt(p.input_qty || "0", 10), 0);
		const reworkAvailableQty = Math.max(
			0,
			reworkGoodFromPrev - reworkConsumedHere,
		);

		// 접수가능 수량을 초과하지 않도록 제한
		const inputQtyNum = parseInt(proc.input_qty || "0", 10);
		const actualAvailable = Math.max(0, prevGood - inputQtyNum);
		const clampedReworkAvailable = Math.min(
			reworkAvailableQty,
			actualAvailable,
		);

		return {
			prevGoodQty: prevGood,
			prevProcessName: prev.process_name || prev.process_code,
			prevProgressPct:
				prev.status === "in_progress"
					? prevPct
					: prev.status === "completed"
						? 100
						: null,
			reworkAvailableQty: clampedReworkAvailable,
		};
	};

	/* ---- Helper: get equipment name ---- */
	const getEquipmentName = (proc: WorkOrderProcess): string => {
		const wi = instructionMap[proc.wo_id];
		if (!wi?.equipment_id) return "미배정";
		const eq = equipmentMap[wi.equipment_id];
		return eq?.equipment_name || "미배정";
	};

	/* ---- Render ---- */
	return (
		<div className="flex flex-col gap-3">
			{/* Sync + Column toggle row */}
			<div className="flex items-center justify-between">
				<div className="flex items-center gap-2">
					<span className="text-xs text-gray-500">카드 열:</span>
					{COLS_OPTIONS.map((n) => (
						<button
							key={n}
							onClick={() => handleSetCols(n)}
							className={`px-3.5 py-1.5 rounded-lg text-xs font-semibold border transition-all ${
								cardCols === n
									? "bg-blue-600 text-white border-blue-600"
									: "bg-white text-gray-600 border-gray-200 hover:bg-gray-50"
							}`}
						>
							{n}열
						</button>
					))}
				</div>
				<button
					onClick={syncAndFetch}
					disabled={syncing}
					className="shrink-0 px-4 py-2 rounded-xl text-xs font-semibold bg-blue-50 text-blue-600 border border-blue-200 hover:bg-blue-100 active:scale-95 transition-all disabled:opacity-50"
				>
					{syncing ? "동기화중..." : "새로고침"}
				</button>
			</div>

			{/* ===== Filter Area ===== */}
			<div className="flex gap-2">
				<button
					onClick={() => setProcessModalOpen(true)}
					className={`flex-1 flex items-center justify-between gap-2 px-4 py-3 rounded-xl border-2 text-sm font-semibold transition-all active:scale-[0.98] ${
						selectedProcess !== "__all__"
							? "border-blue-300 bg-blue-50 text-blue-700"
							: "border-gray-200 bg-white text-gray-700"
					}`}
				>
					<div className="flex items-center gap-2 min-w-0">
						<svg
							className="w-4 h-4 shrink-0 text-gray-400"
							fill="none"
							stroke="currentColor"
							strokeWidth={2}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"
							/>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
							/>
						</svg>
						<span className="truncate">{selectedProcessLabel}</span>
					</div>
					<svg
						className="w-4 h-4 shrink-0 text-gray-400"
						fill="none"
						stroke="currentColor"
						strokeWidth={2}
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							d="M19.5 8.25l-7.5 7.5-7.5-7.5"
						/>
					</svg>
				</button>

				<button
					onClick={() => setEquipmentModalOpen(true)}
					className={`flex-1 flex items-center justify-between gap-2 px-4 py-3 rounded-xl border-2 text-sm font-semibold transition-all active:scale-[0.98] ${
						selectedEquipment !== "__all__"
							? "border-blue-300 bg-blue-50 text-blue-700"
							: "border-gray-200 bg-white text-gray-700"
					}`}
				>
					<div className="flex items-center gap-2 min-w-0">
						<svg
							className="w-4 h-4 shrink-0 text-gray-400"
							fill="none"
							stroke="currentColor"
							strokeWidth={2}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M11.42 15.17l-5.384 3.083A1.125 1.125 0 014.5 17.21V5.553a1.125 1.125 0 011.536-1.044l5.384 3.083m0 7.578l5.384 3.083A1.125 1.125 0 0018.375 17.21V5.553a1.125 1.125 0 00-1.536-1.044L11.42 7.592"
							/>
						</svg>
						<span className="truncate">{selectedEquipmentLabel}</span>
					</div>
					<svg
						className="w-4 h-4 shrink-0 text-gray-400"
						fill="none"
						stroke="currentColor"
						strokeWidth={2}
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							d="M19.5 8.25l-7.5 7.5-7.5-7.5"
						/>
					</svg>
				</button>
			</div>

			{/* ===== Tab bar ===== */}
			<div className="flex gap-1.5 overflow-x-auto no-scrollbar bg-gray-100 p-1 rounded-xl">
				{TABS.map((tab) => {
					const count = tabCounts[tab.key];
					const isActive = activeTab === tab.key;
					return (
						<button
							key={tab.key}
							onClick={() => setActiveTab(tab.key)}
							className={`flex-1 min-w-[60px] py-2.5 rounded-lg text-xs font-semibold transition-all active:scale-95 flex items-center justify-center gap-1.5 ${
								isActive
									? `${tab.bgActive} ${tab.color} shadow-sm`
									: "text-gray-500 hover:text-gray-700"
							}`}
						>
							{tab.label}
							{count > 0 && (
								<span
									className={`text-[10px] min-w-[18px] h-[18px] flex items-center justify-center rounded-full px-1 ${
										isActive ? "bg-black/10" : "bg-gray-200/60"
									}`}
								>
									{count}
								</span>
							)}
						</button>
					);
				})}
			</div>

			{/* Loading */}
			{loading && (
				<div className="flex items-center justify-center py-16">
					<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>
			)}

			{/* Empty state */}
			{!loading && filteredProcesses.length === 0 && (
				<div className="flex flex-col items-center justify-center py-16 gap-3">
					<div className="w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center">
						<svg
							className="w-8 h-8 text-gray-300"
							fill="none"
							stroke="currentColor"
							strokeWidth={1.5}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15a2.25 2.25 0 012.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V19.5a2.25 2.25 0 002.25 2.25h.75"
							/>
						</svg>
					</div>
					<p className="text-sm text-gray-400">
						{selectedProcess === "__all__"
							? "공정을 선택해주세요"
							: `${TABS.find((t) => t.key === activeTab)?.label || ""} 상태의 공정이 없습니다`}
					</p>
				</div>
			)}

			{/* ===== Process Cards Grid ===== */}
			{!loading && filteredProcesses.length > 0 && (
				<div
					className={`grid ${COLS_GRID_CLASS[cardCols] || "grid-cols-1"} gap-4`}
				>
					{filteredProcesses
						.sort((a, b) => {
							const order: Record<string, number> = {
								acceptable: 0,
								in_progress: 1,
								waiting: 2,
								completed: 3,
							};
							const diff = (order[a.status] ?? 2) - (order[b.status] ?? 2);
							if (diff !== 0) return diff;
							return parseInt(a.seq_no, 10) - parseInt(b.seq_no, 10);
						})
						.map((proc) => {
							const wi = instructionMap[proc.wo_id];
							const badge = STATUS_BADGE[proc.status] || STATUS_BADGE.waiting;
							const siblingProcesses = (processesByWo[proc.wo_id] || []).filter(
								(p) => !p.parent_process_id && (
									// 같은 batch_id끼리만 형제 (다중 품목 구분)
									(!proc.batch_id && !p.batch_id) ||
									(proc.batch_id && p.batch_id === proc.batch_id)
								),
							);
							const planQty = parseInt(proc.plan_qty || "0", 10);
							const goodQty = parseInt(proc.good_qty || "0", 10);
							const defectQty = parseInt(proc.defect_qty || "0", 10);
							const inputQty = parseInt(proc.input_qty || "0", 10);
							const isRework =
								proc.is_rework === "Y" ||
								proc.is_rework === "true" ||
								proc.is_rework === "1";
							const borderLeft = isRework
								? "border-l-orange-500"
								: BORDER_LEFT_COLOR[proc.status] || "border-l-gray-300";
							const eqName = getEquipmentName(proc);

							// Split processes (children of this master)
							const mySplits = allProcesses
								.filter((s) => s.parent_process_id === proc.id)
								.sort((a, b) => {
									const ta = a.started_at
										? new Date(a.started_at).getTime()
										: 0;
									const tb = b.started_at
										? new Date(b.started_at).getTime()
										: 0;
									return tb - ta;
								});

							const progressPct =
								planQty > 0
									? Math.min(100, Math.round((goodQty / planQty) * 100))
									: 0;
							const remainQty = Math.max(0, inputQty - goodQty - defectQty);
							const prevInfo = getPrevProcessInfo(proc);

							// Calculate available qty for acceptable
							const availableQty = isRework
								? inputQty // 리워크 카드는 input_qty 자체가 접수 대상
								: prevInfo.prevGoodQty !== null
									? Math.max(0, prevInfo.prevGoodQty - inputQty)
									: Math.max(0, planQty - inputQty);

							// Additional available for in_progress
							const additionalAvailable = Math.max(0, planQty - inputQty);

							// Split order label
							const splitInfo = splitOrderMap[proc.id];

							// 합류 불가 리워크 감지: 접수가능 물량이 전부 리워크일 때
							const reworkQtyAvail = prevInfo.reworkAvailableQty || 0;
							const normalAvail = availableQty - reworkQtyAvail;
							const isReworkOnly =
								!isRework &&
								proc.status === "acceptable" &&
								reworkQtyAvail > 0 &&
								normalAvail <= 0 &&
								availableQty > 0;

							// 리워크 표시 여부 (실제 리워크 카드 OR 합류불가 리워크)
							const showReworkBadge = isRework || isReworkOnly;

							// Rework info: origin process + rework round
							let reworkRound = 1;
							let originProcessName = proc.process_name || proc.process_code;
							let originProcessCode = proc.process_code;
							let originDefectQty = defectQty;
							if (isRework) {
								// 리워크 마스터 카드만 카운트 (SPLIT 제외 — parent_process_id 없는 것만)
								const reworkMasters = allProcesses.filter(
									(p) =>
										p.wo_id === proc.wo_id &&
										!p.parent_process_id &&
										(p.is_rework === "Y" ||
											p.is_rework === "true" ||
											p.is_rework === "1"),
								);
								const sortedReworks = [...reworkMasters].sort((a, b) => {
									const da = a.created_date
										? new Date(a.created_date).getTime()
										: 0;
									const db = b.created_date
										? new Date(b.created_date).getTime()
										: 0;
									return da - db || a.id.localeCompare(b.id);
								});
								// 현재 카드가 SPLIT이면 parent(마스터)의 위치로, 마스터면 직접 위치
								const masterId = proc.parent_process_id || proc.id;
								const myIdx = sortedReworks.findIndex((r) => r.id === masterId);
								reworkRound = myIdx >= 0 ? myIdx + 1 : 1;

								// Find origin (source) process
								if (proc.rework_source_id) {
									const origin = allProcesses.find(
										(p) => p.id === proc.rework_source_id,
									);
									if (origin) {
										originProcessName =
											origin.process_name || origin.process_code;
										originProcessCode = origin.process_code;
										originDefectQty = parseInt(origin.defect_qty || "0", 10);
									}
								}
							}

							return (
								<div
									key={`${proc.id}-${proc.status}-${proc.parent_process_id || "m"}`}
									className="card-item"
								>
									<div
										className={`bg-white rounded-2xl border-l-4 ${borderLeft} border border-gray-100 shadow-sm overflow-hidden flex flex-col ${
											proc.status === "waiting" ? "opacity-75" : ""
										} ${showReworkBadge ? "border-2 border-orange-200" : ""} ${
											proc.status === "in_progress"
												? "cursor-pointer active:scale-[0.99] transition-transform"
												: ""
										}`}
										style={{ height: "100%" }}
										onClick={
											proc.status === "in_progress"
												? () =>
														goToWork(
															proc.parent_process_id
																? proc.id
																: mySplits[0]?.id || proc.id,
														)
												: undefined
										}
									>
										<div className="p-4 flex-1 flex flex-col">
											{/* Header: Work instruction number + status badge */}
											<div className="flex items-center justify-between mb-1">
												<div className="flex items-center gap-2 min-w-0">
													{showReworkBadge && (
														<span className="bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded shrink-0">
															🔄 리워크
														</span>
													)}
													<h3 className="text-xl font-extrabold text-gray-900 truncate">
														{wi?.work_instruction_no || "작업지시"}
														{splitInfo && (
															<span className="text-sm font-bold text-blue-500 ml-1">
																(접수 #{splitInfo.order})
															</span>
														)}
													</h3>
												</div>
												<span
													className={`shrink-0 ml-2 text-sm font-bold px-3 py-1.5 rounded-full ${badge.bg} ${badge.text}`}
												>
													{badge.prefix}
													{badge.label}
												</span>
											</div>

											{/* 단일/다중품목 뱃지 */}
											{(() => {
												const bInfo = multiBatchInfo[proc.id];
												if (!bInfo) return null;
												const typeLabel = bInfo.itemType || "";
												return (
													<div className="flex items-center gap-1.5 mb-1">
														{bInfo.isMulti ? (
															<span className="bg-blue-100 text-blue-700 text-xs font-bold px-2 py-0.5 rounded-full">
																다중 {bInfo.index}/{bInfo.total}{typeLabel ? ` · ${typeLabel}` : ""}
															</span>
														) : (
															<span className="bg-gray-100 text-gray-600 text-xs font-bold px-2 py-0.5 rounded-full">
																단일{typeLabel ? ` · ${typeLabel}` : ""}
															</span>
														)}
													</div>
												);
											})()}

											{/* Sub-info: item name + equipment */}
											<AutoScrollText className="text-sm text-gray-500 mb-3">
												📦 {proc.batch_id
													? `${itemNameMap[proc.batch_id] || proc.batch_id}(${proc.batch_id})`
													: `${wi?.item_name || "품목"}${wi?.item_code || wi?.item_number ? `(${wi?.item_code || wi?.item_number})` : ""}`}
												{" · "}
												{!isRework
													? `⚙️ ${eqName}`
													: `⚙️ ${proc.process_name || proc.process_code}`}
											</AutoScrollText>

											{/* Process steps (compressed) — both normal and rework cards */}
											{siblingProcesses.length > 1 && (
												<CompressedProcessSteps
													processes={processesByWo[proc.wo_id] || []}
													currentSeqNo={proc.seq_no}
													status={proc.status}
													onClick={() => openDetailModal(proc)}
													batchId={
														proc.batch_id ?? undefined
													}
													allProcesses={allProcesses}
												/>
											)}

											{/* Status-specific body (pushed to bottom with mt-auto) */}
											<div className="mt-auto">
												{isRework ? (
													<ReworkCardBody
														reworkQty={planQty}
														availableQty={availableQty}
														originProcessName={originProcessName}
														originProcessCode={originProcessCode}
														originDefectQty={originDefectQty}
														reworkRound={reworkRound}
													/>
												) : proc.status === "acceptable" ? (
													<AcceptableCardBody
														planQty={planQty}
														prevGoodQty={prevInfo.prevGoodQty}
														availableQty={availableQty}
														reworkAvailableQty={prevInfo.reworkAvailableQty}
													/>
												) : proc.status === "in_progress" ? (
													<InProgressCardBody
														inputQty={inputQty}
														goodQty={goodQty}
														defectQty={defectQty}
														remainQty={remainQty}
														progressPct={progressPct}
														additionalAvailable={additionalAvailable}
													/>
												) : proc.status === "waiting" ? (
													<WaitingCardBody
														planQty={planQty}
														prevProcessName={prevInfo.prevProcessName}
														prevProgressPct={prevInfo.prevProgressPct}
														currentProcessName={
															proc.process_name || proc.process_code
														}
														currentSeqNo={parseInt(proc.seq_no, 10)}
													/>
												) : proc.status === "completed" ? (
													<CompletedCardBody
														goodQty={goodQty}
														defectQty={defectQty}
														planQty={planQty}
														inputQty={inputQty}
													/>
												) : null}
											</div>
										</div>

										{/* Action button (full width, bottom) */}
										{isRework && proc.status === "acceptable" && (
											<button
												onClick={() =>
													openAcceptModal(
														proc.id,
														proc.process_name,
														proc.seq_no,
														(proc as unknown as Record<string, unknown>)
															.rework_source_id as string | undefined,
													)
												}
												className="w-full py-3.5 text-sm font-bold text-white active:scale-[0.98] transition-all"
												style={{
													background:
														"linear-gradient(to bottom, #fb923c, #ea580c)",
												}}
											>
												리워크 접수
											</button>
										)}
										{!isRework &&
											proc.status === "acceptable" &&
											availableQty > 0 && (
												<button
													onClick={() =>
														openAcceptModal(
															proc.id,
															proc.process_name,
															proc.seq_no,
														)
													}
													className="w-full py-3.5 text-sm font-bold text-white active:scale-[0.98] transition-all"
													style={{
														background:
															"linear-gradient(to bottom, #fbbf24, #d97706)",
													}}
												>
													접수
												</button>
											)}
										{!isRework &&
											proc.status === "acceptable" &&
											availableQty <= 0 &&
											inputQty > 0 && (
												<div className="w-full py-3 text-sm font-bold text-center text-gray-400 bg-gray-100">
													전량 접수 완료
												</div>
											)}
										{proc.status === "in_progress" &&
											parseInt(proc.total_production_qty || "0", 10) === 0 &&
											proc.parent_process_id && (
												<button
													onClick={(e) => {
														e.stopPropagation();
														setCancelConfirm({
															open: true,
															processId: proc.id,
														});
													}}
													className="w-full py-4 text-base font-bold text-red-500 border-t-2 border-red-100 bg-red-50/50 active:scale-[0.98] transition-all"
												>
													접수 취소
												</button>
											)}
									</div>
								</div>
							);
						})}
				</div>
			)}

			{/* ===== Filter Modals ===== */}
			<FilterSelectorModal
				open={processModalOpen}
				onClose={() => setProcessModalOpen(false)}
				title="공정 선택"
				options={processFilterOptions}
				selectedValue={selectedProcess}
				onSelect={setSelectedProcess}
			/>
			<FilterSelectorModal
				open={equipmentModalOpen}
				onClose={() => setEquipmentModalOpen(false)}
				title="설비 선택"
				options={equipmentFilterOptions}
				selectedValue={selectedEquipment}
				onSelect={setSelectedEquipment}
			/>

			{/* Accept Process Modal */}
			<AcceptProcessModal
				open={acceptModal.open}
				onClose={() => setAcceptModal((m) => ({ ...m, open: false }))}
				onConfirm={handleAccept}
				maxQty={acceptModal.maxQty}
				processName={acceptModal.processName}
				seqNo={acceptModal.seqNo}
				loading={acceptLoading}
			/>

			{/* Process Detail Modal */}
			<ProcessDetailModal
				open={detailModal.open}
				onClose={() => setDetailModal((m) => ({ ...m, open: false }))}
				workInstructionNo={detailModal.wiNo}
				totalQty={detailModal.totalQty}
				steps={detailModal.steps}
				woId={detailModal.woId}
				showReworkHistory={detailModal.showReworkHistory}
			/>

			{/* Fullscreen Work Modal */}
			{workModalProcessId && (
				<FullscreenWorkModal
					processId={workModalProcessId}
					myProcesses={allProcesses.filter(
						(p) =>
							p.parent_process_id &&
							p.accepted_by === currentUserId &&
							p.status === "in_progress",
					)}
					instructionMap={instructionMap}
					itemNameMap={itemNameMap}
					multiBatchInfo={multiBatchInfo}
					onSwitch={(id) => setWorkModalProcessId(id)}
					onClose={() => {
						setWorkModalProcessId(null);
						fetchAll();
					}}
				/>
			)}

			{/* Cancel accept confirm modal */}
			<ConfirmModal
				open={cancelConfirm.open}
				title="접수 취소"
				message="접수를 취소하시겠습니까?"
				confirmText="취소"
				cancelText="닫기"
				variant="danger"
				onConfirm={async () => {
					const pid = cancelConfirm.processId;
					setCancelConfirm({ open: false, processId: "" });
					try {
						const res = await apiClient.post("/pop/production/cancel-accept", {
							work_order_process_id: pid,
						});
						if (res.data?.success) {
							fetchAll();
						} else {
							alert(res.data?.message || "취소 실패");
						}
					} catch (err: unknown) {
						const e2 = err as { response?: { data?: { message?: string } } };
						alert(e2.response?.data?.message || "취소 중 오류");
					}
				}}
				onCancel={() => setCancelConfirm({ open: false, processId: "" })}
			/>
		</div>
	);
}
