"use client";

import React, { useState, useEffect, useCallback, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
  DialogDescription,
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Checkbox } from "@/components/ui/checkbox";
import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from "@/components/ui/resizable";
import {
  Plus,
  Trash2,
  Loader2,
  Download,
  Package,
  Search,
  X,
  Settings2,
  ChevronRight,
  ChevronsLeft,
  ChevronLeft,
  ChevronsRight,
  RefreshCw,
  Inbox,
  Filter,
  Check,
  ArrowUp,
  ArrowDown,
  Save,
} from "lucide-react";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { useAuth } from "@/hooks/useAuth";
import { useConfirmDialog } from "@/components/common/ConfirmDialog";
import { toast } from "sonner";
import { useTableSettings } from "@/hooks/useTableSettings";
import { TableSettingsModal } from "@/components/common/TableSettingsModal";
import {
  DynamicSearchFilter,
  FilterValue,
} from "@/components/common/DynamicSearchFilter";
import { exportToExcel } from "@/lib/utils/excelExport";
import {
  getOutsourcingOutboundList,
  getCandidates,
  createOutsourcingOutbound,
  deleteOutsourcingOutbound,
  generateOutsourcingOutboundNumber,
  getOutsourcingWarehouses,
  type OutsourcingCandidate,
  type OutsourcingOutboundItem,
  type WarehouseOption,
} from "@/lib/api/outsourcingOutbound";

// ===== 상태 뱃지 색상 =====
const OUTBOUND_STATUS_OPTIONS = [
  { value: "대기", label: "대기", color: "bg-secondary text-secondary-foreground" },
  { value: "출고완료", label: "출고완료", color: "bg-primary/10 text-primary" },
  { value: "출고취소", label: "출고취소", color: "bg-destructive/10 text-destructive" },
];

const getStatusColor = (status: string) =>
  OUTBOUND_STATUS_OPTIONS.find((s) => s.value === status)?.color ||
  "bg-muted text-muted-foreground";

// ===== 메인 그리드 컬럼 =====
const GRID_COLUMNS = [
  { key: "outbound_number", label: "외주출고번호" },
  { key: "outbound_date", label: "출고일" },
  { key: "reference_number", label: "작업지시번호" },
  { key: "customer_name", label: "외주사" },
  { key: "item_code", label: "품목코드" },
  { key: "item_name", label: "품목명" },
  { key: "specification", label: "규격" },
  { key: "outbound_qty", label: "출고수량" },
  { key: "warehouse_name", label: "출고창고" },
  { key: "outbound_status", label: "상태" },
  { key: "memo", label: "비고" },
];

// 체크박스(1) + GRID_COLUMNS(11) = 12
const TOTAL_COLS = 12;

// ===== 헤더 필터 Popover =====
function HeaderFilterPopover({
  colKey,
  colLabel,
  uniqueValues,
  filterValues,
  onToggle,
  onClear,
}: {
  colKey: string;
  colLabel: string;
  uniqueValues: string[];
  filterValues: Set<string>;
  onToggle: (colKey: string, value: string) => void;
  onClear: (colKey: string) => void;
}) {
  const [filterSearch, setFilterSearch] = useState("");
  const hasFilter = filterValues.size > 0;
  const filteredValues = uniqueValues.filter(
    (v) => !filterSearch || v.toLowerCase().includes(filterSearch.toLowerCase()),
  );

  return (
    <Popover>
      <PopoverTrigger asChild>
        <button
          onClick={(e) => e.stopPropagation()}
          className={cn(
            "hover:bg-primary/20 rounded p-0.5 transition-colors shrink-0",
            hasFilter && "text-primary bg-primary/10",
          )}
          title="필터"
        >
          <Filter className="h-3 w-3" />
        </button>
      </PopoverTrigger>
      <PopoverContent
        className="w-56 p-2"
        align="start"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="space-y-2">
          <div className="flex items-center justify-between border-b pb-2">
            <span className="text-xs font-medium">필터: {colLabel}</span>
            {hasFilter && (
              <button
                onClick={() => onClear(colKey)}
                className="text-primary text-xs hover:underline"
              >
                초기화
              </button>
            )}
          </div>
          <div className="relative">
            <Search className="absolute left-2 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground" />
            <Input
              value={filterSearch}
              onChange={(e) => setFilterSearch(e.target.value)}
              placeholder="검색..."
              className="h-7 text-xs pl-7"
            />
          </div>
          <div className="max-h-52 space-y-0.5 overflow-y-auto">
            {filteredValues.slice(0, 100).map((val) => {
              const isSelected = filterValues.has(val);
              return (
                <div
                  key={val}
                  className={cn(
                    "hover:bg-muted flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs",
                    isSelected && "bg-primary/10",
                  )}
                  onClick={() => onToggle(colKey, val)}
                >
                  <div
                    className={cn(
                      "flex h-4 w-4 items-center justify-center rounded border shrink-0",
                      isSelected
                        ? "bg-primary border-primary"
                        : "border-input",
                    )}
                  >
                    {isSelected && (
                      <Check className="text-primary-foreground h-3 w-3" />
                    )}
                  </div>
                  <span className="truncate">{val || "(빈 값)"}</span>
                </div>
              );
            })}
            {filteredValues.length > 100 && (
              <div className="text-muted-foreground px-2 py-1 text-xs">
                ...외 {filteredValues.length - 100}개
              </div>
            )}
          </div>
        </div>
      </PopoverContent>
    </Popover>
  );
}

// ===== 선택 품목 인터페이스 (등록 모달에서 사용) =====
interface SelectedItem {
  key: string; // candidate의 completed_process_id
  instruction_no: string;
  completed_process_name: string;
  next_process_name: string;
  item_code: string;
  item_name: string;
  spec: string;
  material: string;
  unit: string;
  outbound_qty: number; // 기본값: good_qty
  subcontractor_code: string;
  subcontractor_name: string;
  completed_process_id: string;
}

// ===== 메인 페이지 =====
export default function OutsourcingOutboundPage() {
  const ts = useTableSettings(
    "c16-outsourcing-outbound",
    "outbound_mng",
    GRID_COLUMNS,
  );
  const { user } = useAuth();
  const { confirm, ConfirmDialogComponent } = useConfirmDialog();

  // 목록 데이터
  const [data, setData] = useState<OutsourcingOutboundItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [checkedIds, setCheckedIds] = useState<string[]>([]);

  // 페이지네이션
  const [currentPage, setCurrentPage] = useState(1);
  const [pageSize, setPageSize] = useState(20);
  const [pageSizeInput, setPageSizeInput] = useState("20");

  // 검색 필터
  const [searchFilters, setSearchFilters] = useState<FilterValue[]>([]);

  // 헤더 필터 & 정렬
  const [headerFilters, setHeaderFilters] = useState<
    Record<string, Set<string>>
  >({});
  const [sortState, setSortState] = useState<{
    key: string;
    direction: "asc" | "desc";
  } | null>(null);

  // 등록 모달
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [modalOutboundNo, setModalOutboundNo] = useState("");
  const [modalOutboundDate, setModalOutboundDate] = useState("");
  const [modalWarehouse, setModalWarehouse] = useState("");
  const [modalManager, setModalManager] = useState("");
  const [modalMemo, setModalMemo] = useState("");
  const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([]);
  const [saving, setSaving] = useState(false);

  // 후보(대상) 데이터
  const [candidates, setCandidates] = useState<OutsourcingCandidate[]>([]);
  const [candidateKeyword, setCandidateKeyword] = useState("");
  const [candidateLoading, setCandidateLoading] = useState(false);

  // 창고 목록
  const [warehouses, setWarehouses] = useState<WarehouseOption[]>([]);

  // ===== 목록 조회 =====
  const fetchList = useCallback(async () => {
    setLoading(true);
    try {
      const params: Record<string, string | undefined> = {};
      for (const f of searchFilters) {
        if (!f.value) continue;
        if (f.columnName === "outbound_status") params.outbound_status = f.value;
        else if (
          f.columnName === "outbound_date" &&
          f.operator === "between"
        ) {
          const [from, to] = f.value.split("~").map((s) => s.trim());
          if (from) params.date_from = from;
          if (to) params.date_to = to;
        } else {
          params.search_keyword = f.value;
        }
      }
      const res = await getOutsourcingOutboundList(params);
      if (res.success) setData(res.data);
    } catch {
      // ignore
    } finally {
      setLoading(false);
    }
  }, [searchFilters]);

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

  // 창고 목록 로드
  useEffect(() => {
    (async () => {
      try {
        const res = await getOutsourcingWarehouses();
        if (res.success) setWarehouses(res.data);
      } catch {
        // ignore
      }
    })();
  }, []);

  // ===== 플랫 행 생성 =====
  const flatRows = useMemo(() => {
    return data.map((row) => ({
      ...row,
      warehouse_name: row.warehouse_name || row.warehouse_code || "",
      specification: row.specification || "",
    }));
  }, [data]);

  // 컬럼별 고유값 (헤더 필터용)
  const columnUniqueValues = useMemo(() => {
    const result: Record<string, string[]> = {};
    for (const col of GRID_COLUMNS) {
      const values = new Set<string>();
      flatRows.forEach((row) => {
        const val = (row as any)[col.key];
        if (val !== null && val !== undefined && val !== "")
          values.add(String(val));
      });
      result[col.key] = Array.from(values).sort();
    }
    return result;
  }, [flatRows]);

  // 필터 + 정렬 적용된 데이터
  const filteredRows = useMemo(() => {
    let rows = [...flatRows];

    // 헤더 필터
    for (const [colKey, values] of Object.entries(headerFilters)) {
      if (values.size === 0) continue;
      rows = rows.filter((row) => {
        const cellVal =
          (row as any)[colKey] != null ? String((row as any)[colKey]) : "";
        return values.has(cellVal);
      });
    }

    // 정렬
    if (sortState) {
      const { key, direction } = sortState;
      rows.sort((a, b) => {
        const av = (a as any)[key] ?? "";
        const bv = (b as any)[key] ?? "";
        const na = Number(av);
        const nb = Number(bv);
        if (!isNaN(na) && !isNaN(nb))
          return direction === "asc" ? na - nb : nb - na;
        return direction === "asc"
          ? String(av).localeCompare(String(bv))
          : String(bv).localeCompare(String(av));
      });
    }

    return rows;
  }, [flatRows, headerFilters, sortState]);

  // ===== 페이지네이션 =====
  const totalPages = Math.max(1, Math.ceil(filteredRows.length / pageSize));
  const safePage = Math.min(Math.max(1, currentPage), totalPages);
  const paginatedRows = useMemo(() => {
    const start = (safePage - 1) * pageSize;
    return filteredRows.slice(start, start + pageSize);
  }, [filteredRows, safePage, pageSize]);

  const applyPageSize = () => {
    const n = parseInt(pageSizeInput, 10);
    if (!isNaN(n) && n >= 1) {
      setPageSize(n);
      setCurrentPage(1);
    } else {
      setPageSizeInput(String(pageSize));
    }
  };

  const getPageNumbers = (): (number | "...")[] => {
    const pages: (number | "...")[] = [];
    if (totalPages <= 7) {
      for (let i = 1; i <= totalPages; i++) pages.push(i);
    } else {
      pages.push(1);
      if (safePage > 3) pages.push("...");
      for (
        let i = Math.max(2, safePage - 1);
        i <= Math.min(totalPages - 1, safePage + 1);
        i++
      )
        pages.push(i);
      if (safePage < totalPages - 2) pages.push("...");
      pages.push(totalPages);
    }
    return pages;
  };

  // 필터 변경 시 첫 페이지로 이동
  useEffect(() => {
    setCurrentPage(1);
  }, [headerFilters, sortState, filteredRows.length]);

  // ===== 헤더 필터 핸들러 =====
  const toggleHeaderFilter = (colKey: string, value: string) => {
    setHeaderFilters((prev) => {
      const next = { ...prev };
      const set = new Set(next[colKey] || []);
      if (set.has(value)) set.delete(value);
      else set.add(value);
      if (set.size === 0) delete next[colKey];
      else next[colKey] = set;
      return next;
    });
  };

  const clearHeaderFilter = (colKey: string) => {
    setHeaderFilters((prev) => {
      const next = { ...prev };
      delete next[colKey];
      return next;
    });
  };

  const handleSort = (key: string) => {
    setSortState((prev) =>
      prev?.key === key
        ? prev.direction === "asc"
          ? { key, direction: "desc" }
          : null
        : { key, direction: "asc" },
    );
  };

  // ===== 삭제 =====
  const handleDelete = async () => {
    if (checkedIds.length === 0) return;
    const ok = await confirm(
      `선택한 ${checkedIds.length}건을 삭제하시겠습니까?`,
      { variant: "destructive", confirmText: "삭제" },
    );
    if (!ok) return;
    try {
      for (const id of checkedIds) {
        await deleteOutsourcingOutbound(id);
      }
      toast.success("삭제 완료");
      setCheckedIds([]);
      fetchList();
    } catch {
      toast.error("삭제 중 오류가 발생했습니다.");
    }
  };

  // ===== 엑셀 다운로드 =====
  const handleExcelDownload = async () => {
    if (filteredRows.length === 0) {
      toast.error("다운로드할 데이터가 없습니다.");
      return;
    }
    const excelData = filteredRows.map((row) => ({
      외주출고번호: row.outbound_number || "",
      출고일: row.outbound_date || "",
      작업지시번호: row.reference_number || "",
      외주사: row.customer_name || "",
      품목코드: row.item_code || "",
      품목명: row.item_name || "",
      규격: row.specification || "",
      출고수량: row.outbound_qty || 0,
      출고창고: row.warehouse_name || "",
      상태: row.outbound_status || "",
      비고: row.memo || "",
    }));
    await exportToExcel(excelData, "외주출고목록.xlsx", "외주출고");
    toast.success("엑셀 다운로드 완료");
  };

  // ===== 등록 모달 =====
  const loadCandidates = useCallback(async (keyword?: string) => {
    setCandidateLoading(true);
    try {
      const res = await getCandidates(keyword || undefined);
      if (res.success) setCandidates(res.data);
    } catch {
      // ignore
    } finally {
      setCandidateLoading(false);
    }
  }, []);

  const openRegisterModal = async () => {
    setModalOutboundDate(new Date().toISOString().split("T")[0]);
    setModalWarehouse("");
    setModalManager(user?.userName || "");
    setModalMemo("");
    setSelectedItems([]);
    setCandidateKeyword("");
    setCandidates([]);
    setIsModalOpen(true);

    try {
      const [numRes] = await Promise.all([
        generateOutsourcingOutboundNumber(),
        loadCandidates(),
      ]);
      if (numRes.success) setModalOutboundNo(numRes.data);
    } catch {
      setModalOutboundNo("");
    }
  };

  const searchCandidates = useCallback(async () => {
    await loadCandidates(candidateKeyword || undefined);
  }, [candidateKeyword, loadCandidates]);

  // 후보 → 선택 품목 추가
  const addCandidate = (c: OutsourcingCandidate) => {
    const key = c.completed_process_id;
    if (selectedItems.some((s) => s.key === key)) return;
    setSelectedItems((prev) => [
      ...prev,
      {
        key,
        instruction_no: c.instruction_no,
        completed_process_name: c.completed_process_name,
        next_process_name: c.next_process_name,
        item_code: c.item_code,
        item_name: c.item_name,
        spec: c.spec || "",
        material: c.material || "",
        unit: c.unit || "",
        outbound_qty: c.good_qty,
        subcontractor_code: c.subcontractor_code,
        subcontractor_name: c.subcontractor_name,
        completed_process_id: c.completed_process_id,
      },
    ]);
  };

  // 선택 품목 수량 변경
  const updateItemQty = (key: string, qty: number) => {
    setSelectedItems((prev) =>
      prev.map((item) =>
        item.key === key ? { ...item, outbound_qty: qty } : item,
      ),
    );
  };

  // 선택 품목 삭제
  const removeItem = (key: string) => {
    setSelectedItems((prev) => prev.filter((item) => item.key !== key));
  };

  // ===== 저장 =====
  const handleSave = async () => {
    if (selectedItems.length === 0) {
      toast.error("출고할 품목을 선택해주세요.");
      return;
    }
    if (!modalOutboundDate) {
      toast.error("출고일을 입력해주세요.");
      return;
    }

    const zeroQtyItems = selectedItems.filter(
      (i) => !i.outbound_qty || i.outbound_qty <= 0,
    );
    if (zeroQtyItems.length > 0) {
      toast.error("출고수량이 0인 품목이 있습니다. 수량을 입력해주세요.");
      return;
    }

    setSaving(true);
    try {
      const res = await createOutsourcingOutbound({
        outbound_number: modalOutboundNo,
        outbound_date: modalOutboundDate,
        warehouse_code: modalWarehouse || undefined,
        memo: modalMemo || undefined,
        items: selectedItems.map((item) => ({
          reference_number: item.instruction_no,
          subcontractor_code: item.subcontractor_code,
          subcontractor_name: item.subcontractor_name,
          item_code: item.item_code,
          item_name: item.item_name,
          spec: item.spec,
          material: item.material,
          unit: item.unit,
          outbound_qty: item.outbound_qty,
          completed_process_id: item.completed_process_id,
        })),
      });

      if (res.success) {
        toast.success(res.message || "외주출고 등록 완료");
        setIsModalOpen(false);
        fetchList();
      }
    } catch (err: any) {
      const msg =
        err?.response?.data?.message || "외주출고 등록 중 오류가 발생했습니다.";
      toast.error(msg);
    } finally {
      setSaving(false);
    }
  };

  // 합계 계산
  const totalSummary = useMemo(() => {
    return {
      count: selectedItems.length,
      qty: selectedItems.reduce((sum, i) => sum + (i.outbound_qty || 0), 0),
    };
  }, [selectedItems]);

  return (
    <div className="flex h-[calc(100vh-4rem)] flex-col gap-3 p-3">
      {/* 검색 영역 */}
      <DynamicSearchFilter
        tableName="outbound_mng"
        filterId="c16-outsourcing-outbound"
        onFilterChange={setSearchFilters}
        externalFilterConfig={ts.filterConfig}
        dataCount={data.length}
      />

      {/* 외주출고 목록 테이블 */}
      <div className="flex flex-1 flex-col overflow-hidden rounded-lg border bg-card">
        {/* 패널 헤더 */}
        <div className="flex items-center justify-between border-b bg-muted/30 px-4 py-2.5 shrink-0">
          <div className="flex items-center gap-2">
            <Package className="h-4 w-4 text-muted-foreground" />
            <span className="text-sm font-semibold">외주출고 목록</span>
            <span className="rounded-full border border-primary/15 bg-primary/8 px-2 py-0.5 font-mono text-[11px] font-bold text-primary">
              {filteredRows.length}건
            </span>
          </div>
          <div className="flex gap-2">
            <Button
              size="sm"
              variant="outline"
              onClick={handleExcelDownload}
              className="h-8"
            >
              <Download className="mr-1 h-3.5 w-3.5" />
              엑셀
            </Button>
            <Button size="sm" onClick={openRegisterModal} className="h-8">
              <Plus className="mr-1 h-3.5 w-3.5" />
              외주출고 등록
            </Button>
            <Button
              size="sm"
              variant="destructive"
              onClick={handleDelete}
              disabled={checkedIds.length === 0}
              className="h-8"
            >
              <Trash2 className="mr-1 h-3.5 w-3.5" />
              삭제 ({checkedIds.length})
            </Button>
            <Button
              variant="ghost"
              size="sm"
              className="h-8"
              onClick={() => ts.setOpen(true)}
              title="테이블 설정"
            >
              <Settings2 className="h-4 w-4" />
            </Button>
          </div>
        </div>

        <div className="flex-1 overflow-auto">
          <Table noWrapper style={{ minWidth: "1200px" }}>
            <colgroup>
              <col style={{ width: "40px" }} />
              <col style={{ width: "150px" }} />
              <col style={{ width: "100px" }} />
              <col style={{ width: "140px" }} />
              <col style={{ width: "120px" }} />
              <col style={{ width: "100px" }} />
              <col style={{ width: "150px" }} />
              <col style={{ width: "100px" }} />
              <col style={{ width: "90px" }} />
              <col style={{ width: "100px" }} />
              <col style={{ width: "80px" }} />
              <col style={{ width: "130px" }} />
            </colgroup>
            <TableHeader className="sticky top-0 z-10">
              <TableRow className="bg-muted hover:bg-muted">
                <TableHead
                  className="text-center cursor-pointer"
                  onClick={() => {
                    const allFilteredIds = filteredRows.map((r) => r.id);
                    const allChecked =
                      allFilteredIds.length > 0 &&
                      allFilteredIds.every((id) => checkedIds.includes(id));
                    setCheckedIds(allChecked ? [] : allFilteredIds);
                  }}
                >
                  <Checkbox
                    checked={(() => {
                      const allFilteredIds = filteredRows.map((r) => r.id);
                      return (
                        allFilteredIds.length > 0 &&
                        allFilteredIds.every((id) => checkedIds.includes(id))
                      );
                    })()}
                    onCheckedChange={() => {}}
                  />
                </TableHead>
                {GRID_COLUMNS.map((col) => {
                  const isRight = ["outbound_qty"].includes(col.key);
                  return (
                    <TableHead
                      key={col.key}
                      className={cn(
                        "text-[11px] font-bold uppercase tracking-wide text-muted-foreground select-none",
                        isRight && "text-right",
                      )}
                    >
                      <div
                        className={cn(
                          "inline-flex items-center gap-1",
                          isRight && "justify-end w-full",
                        )}
                      >
                        <div
                          className="flex items-center gap-1 cursor-pointer min-w-0"
                          onClick={() => handleSort(col.key)}
                        >
                          <span className="truncate">{col.label}</span>
                          {sortState?.key === col.key &&
                            (sortState.direction === "asc" ? (
                              <ArrowUp className="h-3 w-3 text-primary shrink-0" />
                            ) : (
                              <ArrowDown className="h-3 w-3 text-primary shrink-0" />
                            ))}
                        </div>
                        {(columnUniqueValues[col.key] || []).length > 0 && (
                          <HeaderFilterPopover
                            colKey={col.key}
                            colLabel={col.label}
                            uniqueValues={columnUniqueValues[col.key] || []}
                            filterValues={
                              headerFilters[col.key] || new Set<string>()
                            }
                            onToggle={toggleHeaderFilter}
                            onClear={clearHeaderFilter}
                          />
                        )}
                      </div>
                    </TableHead>
                  );
                })}
              </TableRow>
            </TableHeader>
            <TableBody>
              {loading ? (
                <TableRow>
                  <TableCell colSpan={TOTAL_COLS} className="py-16 text-center">
                    <Loader2 className="mx-auto h-6 w-6 animate-spin text-primary" />
                  </TableCell>
                </TableRow>
              ) : filteredRows.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={TOTAL_COLS} className="py-16 text-center">
                    <div className="flex flex-col items-center gap-2 text-muted-foreground">
                      <Package className="h-8 w-8 opacity-30" />
                      <span className="text-sm">
                        등록된 외주출고 내역이 없어요
                      </span>
                    </div>
                  </TableCell>
                </TableRow>
              ) : (
                paginatedRows.map((row) => {
                  const isChecked = checkedIds.includes(row.id);
                  return (
                    <TableRow
                      key={row.id}
                      className={cn(
                        "cursor-pointer border-l-[3px] border-l-transparent transition-all",
                        isChecked
                          ? "border-l-primary bg-primary/5"
                          : "hover:bg-accent/50",
                      )}
                      onClick={() => {
                        setCheckedIds((prev) =>
                          prev.includes(row.id)
                            ? prev.filter((id) => id !== row.id)
                            : [...prev, row.id],
                        );
                      }}
                    >
                      <TableCell
                        className="text-center cursor-pointer"
                        onClick={(e) => {
                          e.stopPropagation();
                          setCheckedIds((prev) =>
                            prev.includes(row.id)
                              ? prev.filter((id) => id !== row.id)
                              : [...prev, row.id],
                          );
                        }}
                      >
                        <Checkbox
                          checked={isChecked}
                          onCheckedChange={() => {}}
                        />
                      </TableCell>
                      <TableCell className="font-mono whitespace-nowrap text-[13px]">
                        {row.outbound_number || ""}
                      </TableCell>
                      <TableCell className="whitespace-nowrap text-[13px]">
                        {row.outbound_date
                          ? new Date(row.outbound_date).toLocaleDateString(
                              "ko-KR",
                            )
                          : ""}
                      </TableCell>
                      <TableCell className="text-[13px] truncate max-w-[140px]">
                        <span className="block truncate">
                          {row.reference_number || ""}
                        </span>
                      </TableCell>
                      <TableCell className="text-[13px] truncate max-w-[120px]">
                        <span className="block truncate">
                          {row.customer_name || ""}
                        </span>
                      </TableCell>
                      <TableCell className="font-mono text-[13px]">
                        {row.item_code || ""}
                      </TableCell>
                      <TableCell className="text-[13px] max-w-[150px]">
                        <span
                          className="block truncate"
                          title={row.item_name || ""}
                        >
                          {row.item_name || ""}
                        </span>
                      </TableCell>
                      <TableCell className="text-[13px] text-muted-foreground">
                        {row.specification || ""}
                      </TableCell>
                      <TableCell className="text-right font-mono text-[13px]">
                        {row.outbound_qty
                          ? Number(row.outbound_qty).toLocaleString()
                          : ""}
                      </TableCell>
                      <TableCell className="text-[13px] truncate max-w-[100px]">
                        <span className="block truncate">
                          {row.warehouse_name || ""}
                        </span>
                      </TableCell>
                      <TableCell className="text-[13px]">
                        <Badge
                          variant="outline"
                          className={cn(
                            "text-[11px]",
                            getStatusColor(row.outbound_status),
                          )}
                        >
                          {row.outbound_status || "-"}
                        </Badge>
                      </TableCell>
                      <TableCell className="text-muted-foreground">
                        <span className="block truncate max-w-[130px]">
                          {row.memo || ""}
                        </span>
                      </TableCell>
                    </TableRow>
                  );
                })
              )}
            </TableBody>
          </Table>
        </div>

        {/* 페이지네이션 */}
        <div className="flex items-center justify-between border-t px-4 py-2.5 text-xs text-muted-foreground shrink-0">
          <div className="flex items-center gap-3">
            <div className="flex items-center gap-1">
              <span>전체</span>
              <span className="font-medium text-foreground">
                {filteredRows.length.toLocaleString()}
              </span>
              <span>건</span>
            </div>
            <div className="flex items-center gap-1.5">
              <Input
                type="number"
                min={1}
                value={pageSizeInput}
                onChange={(e) => setPageSizeInput(e.target.value)}
                onBlur={applyPageSize}
                onKeyDown={(e) => {
                  if (e.key === "Enter") {
                    e.preventDefault();
                    applyPageSize();
                  }
                }}
                className="h-7 w-16 text-center text-xs"
              />
              <span>건씩 보기</span>
            </div>
          </div>
          <div className="flex items-center gap-0.5">
            <button
              onClick={() => setCurrentPage(1)}
              disabled={safePage === 1}
              className="h-7 w-7 flex items-center justify-center rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed"
            >
              <ChevronsLeft className="h-3.5 w-3.5" />
            </button>
            <button
              onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
              disabled={safePage === 1}
              className="h-7 w-7 flex items-center justify-center rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed"
            >
              <ChevronLeft className="h-3.5 w-3.5" />
            </button>
            {getPageNumbers().map((page, idx) =>
              page === "..." ? (
                <span
                  key={`dot-${idx}`}
                  className="h-7 w-7 flex items-center justify-center text-muted-foreground"
                >
                  ...
                </span>
              ) : (
                <button
                  key={page}
                  onClick={() => setCurrentPage(page as number)}
                  className={cn(
                    "h-7 w-7 flex items-center justify-center rounded text-xs",
                    page === safePage
                      ? "bg-primary text-primary-foreground font-medium"
                      : "hover:bg-muted",
                  )}
                >
                  {page}
                </button>
              ),
            )}
            <button
              onClick={() =>
                setCurrentPage((p) => Math.min(totalPages, p + 1))
              }
              disabled={safePage === totalPages}
              className="h-7 w-7 flex items-center justify-center rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed"
            >
              <ChevronRight className="h-3.5 w-3.5" />
            </button>
            <button
              onClick={() => setCurrentPage(totalPages)}
              disabled={safePage === totalPages}
              className="h-7 w-7 flex items-center justify-center rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed"
            >
              <ChevronsRight className="h-3.5 w-3.5" />
            </button>
          </div>
          <div className="flex items-center gap-1.5">
            <Input
              type="number"
              min={1}
              max={totalPages}
              placeholder={String(safePage)}
              className="h-7 w-14 text-center text-xs"
              onKeyDown={(e) => {
                if (e.key === "Enter") {
                  const val = parseInt(
                    (e.target as HTMLInputElement).value,
                    10,
                  );
                  if (!isNaN(val) && val >= 1 && val <= totalPages) {
                    setCurrentPage(val);
                    (e.target as HTMLInputElement).value = "";
                    (e.target as HTMLInputElement).blur();
                  }
                }
              }}
              onBlur={(e) => {
                const val = parseInt(e.target.value, 10);
                if (!isNaN(val) && val >= 1 && val <= totalPages) {
                  setCurrentPage(val);
                }
                e.target.value = "";
              }}
            />
            <span>/ {totalPages} 페이지</span>
          </div>
        </div>
      </div>

      {/* 외주출고 등록 모달 */}
      <Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
        <DialogContent className="flex flex-col gap-0 p-0 sm:max-w-[1400px] w-[95vw] h-[85vh] overflow-hidden">
          <DialogHeader className="shrink-0 border-b px-6 py-4">
            <DialogTitle>외주출고 등록</DialogTitle>
            <DialogDescription>
              좌측에서 외주출고 대상을 검색하여 우측에 추가한 후 저장해주세요.
            </DialogDescription>
          </DialogHeader>

          {/* 메인 콘텐츠 */}
          <div className="flex-1 overflow-hidden">
            <ResizablePanelGroup direction="horizontal">
              {/* 좌측 패널: 외주출고 대상 */}
              <ResizablePanel defaultSize={55} minSize={35}>
                <div className="flex h-full flex-col">
                  {/* 상단: 제목 + 검색 */}
                  <div className="flex items-center gap-2 border-b px-4 py-3">
                    <span className="text-[13px] font-bold text-foreground shrink-0">
                      외주출고 대상
                    </span>
                    <Input
                      placeholder="작업지시번호 / 품목명 / 외주사"
                      value={candidateKeyword}
                      onChange={(e) => setCandidateKeyword(e.target.value)}
                      onKeyDown={(e) =>
                        e.key === "Enter" && searchCandidates()
                      }
                      className="h-8 flex-1 text-xs"
                    />
                    <Button
                      size="sm"
                      onClick={searchCandidates}
                      className="h-8"
                    >
                      <Search className="mr-1 h-3 w-3" />
                      검색
                    </Button>
                    <Button
                      size="sm"
                      variant="outline"
                      onClick={() => {
                        setCandidateKeyword("");
                        loadCandidates();
                      }}
                      className="h-8"
                    >
                      <RefreshCw className="h-3 w-3" />
                    </Button>
                  </div>

                  {/* 대상 목록 헤더 */}
                  <div className="flex items-center justify-between border-b bg-muted/30 px-4 py-2 shrink-0">
                    <div className="flex items-center gap-2">
                      <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                        대상 목록
                      </span>
                      {candidates.length > 0 && (
                        <span className="rounded-full border border-primary/15 bg-primary/8 px-2 py-0.5 font-mono text-[11px] font-bold text-primary">
                          {candidates.length}건
                        </span>
                      )}
                    </div>
                  </div>

                  {/* 대상 테이블 */}
                  <div className="flex-1 overflow-auto">
                    {candidateLoading ? (
                      <div className="flex h-40 items-center justify-center">
                        <Loader2 className="h-5 w-5 animate-spin" />
                      </div>
                    ) : candidates.length === 0 ? (
                      <div className="m-4 flex h-32 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border text-xs text-muted-foreground">
                        <Inbox className="h-8 w-8 text-muted-foreground/30" />
                        외주출고 대상이 없습니다
                      </div>
                    ) : (
                      <Table>
                        <TableHeader className="sticky top-0 z-10">
                          <TableRow className="bg-muted hover:bg-muted">
                            <TableHead className="w-[40px] p-2" />
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              작업지시번호
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              완료공정
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              외주공정
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              품목
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              규격
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              외주사
                            </TableHead>
                            <TableHead className="w-[70px] text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              양품수량
                            </TableHead>
                          </TableRow>
                        </TableHeader>
                        <TableBody>
                          {candidates.map((c) => {
                            const isSelected = selectedItems.some(
                              (s) => s.key === c.completed_process_id,
                            );
                            return (
                              <TableRow
                                key={c.completed_process_id}
                                className={cn(
                                  "cursor-pointer text-xs transition-colors",
                                  isSelected && "bg-primary/5",
                                )}
                                onClick={() => !isSelected && addCandidate(c)}
                              >
                                <TableCell className="p-2 text-center">
                                  {isSelected ? (
                                    <Badge className="bg-primary/20 text-primary text-[10px]">
                                      추가됨
                                    </Badge>
                                  ) : (
                                    <ChevronRight className="text-muted-foreground h-4 w-4" />
                                  )}
                                </TableCell>
                                <TableCell
                                  className="max-w-[130px] truncate p-2 font-medium"
                                  title={c.instruction_no}
                                >
                                  {c.instruction_no}
                                </TableCell>
                                <TableCell className="p-2 text-[11px]">
                                  {c.completed_process_name}
                                </TableCell>
                                <TableCell className="p-2 text-[11px]">
                                  {c.next_process_name}
                                </TableCell>
                                <TableCell className="max-w-[150px] p-2">
                                  <div className="flex flex-col">
                                    <span
                                      className="truncate font-medium"
                                      title={c.item_name}
                                    >
                                      {c.item_name}
                                    </span>
                                    <span
                                      className="text-muted-foreground truncate text-[10px]"
                                      title={c.item_code}
                                    >
                                      {c.item_code}
                                    </span>
                                  </div>
                                </TableCell>
                                <TableCell
                                  className="max-w-[80px] truncate p-2 text-[11px]"
                                  title={c.spec || "-"}
                                >
                                  {c.spec || "-"}
                                </TableCell>
                                <TableCell
                                  className="max-w-[100px] truncate p-2 text-[11px]"
                                  title={c.subcontractor_name}
                                >
                                  {c.subcontractor_name}
                                </TableCell>
                                <TableCell className="p-2 text-right font-semibold text-primary">
                                  {Number(c.good_qty).toLocaleString()}
                                </TableCell>
                              </TableRow>
                            );
                          })}
                        </TableBody>
                      </Table>
                    )}
                  </div>
                </div>
              </ResizablePanel>

              <ResizableHandle
                withHandle
                onPointerDown={(e) => e.stopPropagation()}
              />

              {/* 우측 패널: 출고 정보 + 선택 품목 */}
              <ResizablePanel defaultSize={45} minSize={25}>
                <div className="flex h-full flex-col">
                  {/* 출고 정보 폼 */}
                  <div className="space-y-3 border-b bg-muted/30 px-4 py-3">
                    <h4 className="text-[13px] font-bold text-foreground">
                      출고 정보
                    </h4>
                    <div className="grid grid-cols-2 gap-3">
                      <div className="space-y-1">
                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                          외주출고번호
                        </span>
                        <Input
                          value={modalOutboundNo}
                          readOnly
                          className="bg-muted cursor-not-allowed h-8 font-mono text-xs"
                        />
                      </div>
                      <div className="space-y-1">
                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                          출고일 <span className="text-destructive">*</span>
                        </span>
                        <Input
                          type="date"
                          value={modalOutboundDate}
                          onChange={(e) => setModalOutboundDate(e.target.value)}
                          className="h-8 text-xs"
                        />
                      </div>
                      <div className="space-y-1">
                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                          출고창고
                        </span>
                        <Select
                          value={modalWarehouse || "__none__"}
                          onValueChange={(v) =>
                            setModalWarehouse(v === "__none__" ? "" : v)
                          }
                        >
                          <SelectTrigger className="h-8 text-xs">
                            <SelectValue placeholder="창고 선택" />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value="__none__">창고 선택</SelectItem>
                            {warehouses.map((w) => (
                              <SelectItem
                                key={w.warehouse_code}
                                value={w.warehouse_code}
                              >
                                {w.warehouse_name}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                      <div className="space-y-1">
                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                          담당자
                        </span>
                        <Input
                          value={modalManager}
                          onChange={(e) => setModalManager(e.target.value)}
                          placeholder="담당자"
                          className="h-8 text-xs"
                        />
                      </div>
                      <div className="col-span-2 space-y-1">
                        <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                          메모
                        </span>
                        <Input
                          value={modalMemo}
                          onChange={(e) => setModalMemo(e.target.value)}
                          placeholder="메모"
                          className="h-8 text-xs"
                        />
                      </div>
                    </div>
                  </div>

                  {/* 선택 품목 테이블 */}
                  <div className="flex-1 overflow-auto px-4 py-2">
                    <div className="mb-2 flex items-center gap-2">
                      <span className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                        선택 품목
                      </span>
                      <span className="rounded-full border border-primary/15 bg-primary/8 px-2 py-0.5 font-mono text-[11px] font-bold text-primary">
                        {selectedItems.length}건
                      </span>
                    </div>

                    {selectedItems.length === 0 ? (
                      <div className="flex h-32 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border text-xs text-muted-foreground">
                        <Package className="h-8 w-8 text-muted-foreground/30" />
                        좌측에서 대상을 선택하여 추가해주세요
                      </div>
                    ) : (
                      <Table>
                        <TableHeader className="sticky top-0 z-10">
                          <TableRow className="bg-muted hover:bg-muted">
                            <TableHead className="w-[30px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              No
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              작업지시
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              외주사
                            </TableHead>
                            <TableHead className="p-2 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              품목명
                            </TableHead>
                            <TableHead className="w-[90px] text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
                              출고수량
                            </TableHead>
                            <TableHead className="w-[30px] p-2" />
                          </TableRow>
                        </TableHeader>
                        <TableBody>
                          {selectedItems.map((item, idx) => (
                            <TableRow key={item.key} className="text-xs">
                              <TableCell className="p-2 text-center">
                                {idx + 1}
                              </TableCell>
                              <TableCell
                                className="max-w-[120px] p-2 text-[11px]"
                                title={item.instruction_no}
                              >
                                <span className="block truncate">
                                  {item.instruction_no}
                                </span>
                              </TableCell>
                              <TableCell
                                className="max-w-[100px] p-2 text-[11px]"
                                title={item.subcontractor_name}
                              >
                                <span className="block truncate">
                                  {item.subcontractor_name}
                                </span>
                              </TableCell>
                              <TableCell className="max-w-[150px] p-2">
                                <div className="flex flex-col">
                                  <span
                                    className="truncate font-medium"
                                    title={item.item_name}
                                  >
                                    {item.item_name}
                                  </span>
                                  <span
                                    className="text-muted-foreground truncate text-[10px]"
                                    title={`${item.item_code}${item.spec ? ` | ${item.spec}` : ""}`}
                                  >
                                    {item.item_code}
                                    {item.spec ? ` | ${item.spec}` : ""}
                                  </span>
                                </div>
                              </TableCell>
                              <TableCell className="p-2 text-right">
                                <Input
                                  type="number"
                                  value={item.outbound_qty || ""}
                                  onChange={(e) =>
                                    updateItemQty(
                                      item.key,
                                      Number(e.target.value) || 0,
                                    )
                                  }
                                  className="h-7 w-[80px] text-right text-xs"
                                  min={0}
                                />
                              </TableCell>
                              <TableCell className="p-2 text-center">
                                <Button
                                  variant="ghost"
                                  size="icon"
                                  className="h-6 w-6"
                                  onClick={() => removeItem(item.key)}
                                >
                                  <X className="h-3 w-3" />
                                </Button>
                              </TableCell>
                            </TableRow>
                          ))}
                        </TableBody>
                      </Table>
                    )}
                  </div>
                </div>
              </ResizablePanel>
            </ResizablePanelGroup>
          </div>

          {/* 하단: 합계 + 버튼 */}
          <div className="shrink-0 border-t">
            <div className="flex w-full items-center justify-between px-6 py-3">
              <div className="text-muted-foreground text-xs">
                {selectedItems.length > 0 ? (
                  <>
                    {totalSummary.count}건 | 수량 합계:{" "}
                    {totalSummary.qty.toLocaleString()}
                  </>
                ) : (
                  "품목을 추가해주세요"
                )}
              </div>
              <div className="flex gap-2">
                <Button
                  variant="outline"
                  onClick={() => setIsModalOpen(false)}
                  className="h-9 text-sm"
                >
                  취소
                </Button>
                <Button
                  onClick={handleSave}
                  disabled={saving || selectedItems.length === 0}
                  className="h-9 text-sm"
                >
                  {saving ? (
                    <Loader2 className="mr-1 h-4 w-4 animate-spin" />
                  ) : (
                    <Save className="mr-1 h-4 w-4" />
                  )}
                  저장
                </Button>
              </div>
            </div>
          </div>
        </DialogContent>
      </Dialog>

      {/* 테이블 설정 모달 */}
      <TableSettingsModal
        open={ts.open}
        onOpenChange={ts.setOpen}
        tableName={ts.tableName}
        settingsId={ts.settingsId}
        defaultVisibleKeys={ts.defaultVisibleKeys}
        onSave={ts.applySettings}
      />

      {/* 확인 다이얼로그 */}
      {ConfirmDialogComponent}
    </div>
  );
}
