"use client";

import React, { useState, useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import {
  ArrowLeft, Save, RefreshCw, Trash2, Search,
  Database, Workflow, Clock, ChevronRight,
} from "lucide-react";
import { toast } from "sonner";
import { showErrorToast } from "@/lib/utils/toastUtils";
import { useTabStore } from "@/stores/tabStore";
import {
  BatchAPI,
  BatchMapping,
  ConnectionInfo,
  ColumnInfo,
  BatchMappingRequest,
  type NodeFlowInfo,
  type BatchExecutionType,
} from "@/lib/api/batch";

const SCHEDULE_PRESETS = [
  { label: "5분마다", cron: "*/5 * * * *", preview: "5분마다 실행돼요" },
  { label: "30분마다", cron: "*/30 * * * *", preview: "30분마다 실행돼요" },
  { label: "매시간", cron: "0 * * * *", preview: "매시간 정각에 실행돼요" },
  { label: "매일 오전 7시", cron: "0 7 * * *", preview: "매일 오전 7시에 실행돼요" },
  { label: "매일 오전 9시", cron: "0 9 * * *", preview: "매일 오전 9시에 실행돼요" },
  { label: "매일 자정", cron: "0 0 * * *", preview: "매일 밤 12시에 실행돼요" },
  { label: "매주 월요일", cron: "0 9 * * 1", preview: "매주 월요일 오전 9시에 실행돼요" },
  { label: "매월 1일", cron: "0 9 1 * *", preview: "매월 1일 오전 9시에 실행돼요" },
];

function buildCustomCron(repeat: string, dow: string, hour: string, minute: string): string {
  const h = hour;
  const m = minute;
  if (repeat === "daily") return `${m} ${h} * * *`;
  if (repeat === "weekly") return `${m} ${h} * * ${dow}`;
  if (repeat === "monthly") return `${m} ${h} 1 * *`;
  return `${m} ${h} * * *`;
}

function customCronPreview(repeat: string, dow: string, hour: string, minute: string): string {
  const dowNames: Record<string, string> = { "1": "월요일", "2": "화요일", "3": "수요일", "4": "목요일", "5": "금요일", "6": "토요일", "0": "일요일" };
  const h = Number(hour);
  const ampm = h < 12 ? "오전" : "오후";
  const displayH = h === 0 ? 12 : h > 12 ? h - 12 : h;
  const time = `${ampm} ${displayH}시${minute !== "0" ? ` ${minute}분` : ""}`;
  if (repeat === "daily") return `매일 ${time}에 실행돼요`;
  if (repeat === "weekly") return `매주 ${dowNames[dow] || dow} ${time}에 실행돼요`;
  if (repeat === "monthly") return `매월 1일 ${time}에 실행돼요`;
  return `매일 ${time}에 실행돼요`;
}

export default function BatchCreatePage() {
  const { openTab } = useTabStore();

  const [executionType, setExecutionType] = useState<BatchExecutionType>(() => {
    if (typeof window !== "undefined") {
      const stored = sessionStorage.getItem("batch_create_type");
      if (stored === "node_flow") {
        sessionStorage.removeItem("batch_create_type");
        return "node_flow";
      }
      sessionStorage.removeItem("batch_create_type");
    }
    return "mapping";
  });
  const [nodeFlows, setNodeFlows] = useState<NodeFlowInfo[]>([]);
  const [selectedFlowId, setSelectedFlowId] = useState<number | null>(null);
  const [nodeFlowContext, setNodeFlowContext] = useState("");
  const [flowSearch, setFlowSearch] = useState("");

  const [batchName, setBatchName] = useState("");
  const [description, setDescription] = useState("");

  // 스케줄 관련
  const [scheduleMode, setScheduleMode] = useState<"preset" | "custom">("preset");
  const [selectedPresetIndex, setSelectedPresetIndex] = useState(3); // 매일 오전 7시
  const [customRepeat, setCustomRepeat] = useState("daily");
  const [customDow, setCustomDow] = useState("1");
  const [customHour, setCustomHour] = useState("9");
  const [customMinute, setCustomMinute] = useState("0");

  const cronSchedule = useMemo(() => {
    if (scheduleMode === "preset") return SCHEDULE_PRESETS[selectedPresetIndex].cron;
    return buildCustomCron(customRepeat, customDow, customHour, customMinute);
  }, [scheduleMode, selectedPresetIndex, customRepeat, customDow, customHour, customMinute]);

  const schedulePreview = useMemo(() => {
    if (scheduleMode === "preset") return SCHEDULE_PRESETS[selectedPresetIndex].preview;
    return customCronPreview(customRepeat, customDow, customHour, customMinute);
  }, [scheduleMode, selectedPresetIndex, customRepeat, customDow, customHour, customMinute]);

  const [connections, setConnections] = useState<ConnectionInfo[]>([]);
  const [fromConnection, setFromConnection] = useState<ConnectionInfo | null>(null);
  const [toConnection, setToConnection] = useState<ConnectionInfo | null>(null);
  const [fromTables, setFromTables] = useState<string[]>([]);
  const [toTables, setToTables] = useState<string[]>([]);
  const [fromTable, setFromTable] = useState("");
  const [toTable, setToTable] = useState("");
  const [fromColumns, setFromColumns] = useState<ColumnInfo[]>([]);
  const [toColumns, setToColumns] = useState<ColumnInfo[]>([]);

  const [selectedFromColumn, setSelectedFromColumn] = useState<ColumnInfo | null>(null);
  const [mappings, setMappings] = useState<BatchMapping[]>([]);

  const [loading, setLoading] = useState(false);
  const [loadingConnections, setLoadingConnections] = useState(false);

  useEffect(() => {
    if (executionType === "node_flow") {
      BatchAPI.getNodeFlows().then(setNodeFlows);
    }
  }, [executionType]);

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

  const loadConnections = async () => {
    setLoadingConnections(true);
    try {
      const data = await BatchAPI.getConnections();
      setConnections(Array.isArray(data) ? data : []);
    } catch (error) {
      console.error("커넥션 로드 실패:", error);
      toast.error("커넥션 목록을 불러올 수 없어요");
      setConnections([]);
    } finally {
      setLoadingConnections(false);
    }
  };

  const handleFromConnectionChange = async (connectionId: string) => {
    if (connectionId === "unknown") return;
    const connection = connections.find(conn => conn.type === "internal" ? connectionId === "internal" : conn.id?.toString() === connectionId);
    if (!connection) return;
    setFromConnection(connection);
    setFromTable(""); setFromTables([]); setFromColumns([]); setSelectedFromColumn(null);
    try {
      const tables = await BatchAPI.getTablesFromConnection(connection);
      setFromTables(Array.isArray(tables) ? tables : []);
    } catch { toast.error("테이블 목록을 불러올 수 없어요"); }
  };

  const handleToConnectionChange = async (connectionId: string) => {
    if (connectionId === "unknown") return;
    const connection = connections.find(conn => conn.type === "internal" ? connectionId === "internal" : conn.id?.toString() === connectionId);
    if (!connection) return;
    setToConnection(connection); setToTable(""); setToTables([]); setToColumns([]);
    try {
      const tables = await BatchAPI.getTablesFromConnection(connection);
      setToTables(Array.isArray(tables) ? tables : []);
    } catch { toast.error("테이블 목록을 불러올 수 없어요"); }
  };

  const handleFromTableChange = async (tableName: string) => {
    setFromTable(tableName); setFromColumns([]); setSelectedFromColumn(null);
    if (!fromConnection || !tableName) return;
    try {
      const columns = await BatchAPI.getTableColumns(fromConnection, tableName);
      setFromColumns(Array.isArray(columns) ? columns : []);
    } catch (error) {
      showErrorToast("컬럼 목록을 불러올 수 없어요", error, { guidance: "테이블 정보를 확인해 주세요." });
    }
  };

  const handleToTableChange = async (tableName: string) => {
    setToTable(tableName); setToColumns([]);
    if (!toConnection || !tableName) return;
    try {
      const columns = await BatchAPI.getTableColumns(toConnection, tableName);
      setToColumns(Array.isArray(columns) ? columns : []);
    } catch { toast.error("컬럼 목록을 불러올 수 없어요"); }
  };

  const handleFromColumnClick = (column: ColumnInfo) => {
    setSelectedFromColumn(column);
    toast.info(`FROM 컬럼 선택: ${column.column_name}`);
  };

  const handleToColumnClick = (toColumn: ColumnInfo) => {
    if (!selectedFromColumn || !fromConnection || !toConnection) {
      toast.error("먼저 왼쪽(FROM)에서 컬럼을 선택해주세요");
      return;
    }
    const toKey = `${toConnection.type}:${toConnection.id || "internal"}:${toTable}:${toColumn.column_name}`;
    const existingMapping = mappings.find(m => `${m.to_connection_type}:${m.to_connection_id || "internal"}:${m.to_table_name}:${m.to_column_name}` === toKey);
    if (existingMapping) { toast.error("같은 대상 컬럼에 중복 매핑할 수 없어요"); return; }

    setMappings([...mappings, {
      from_connection_type: fromConnection.type,
      from_connection_id: fromConnection.id ?? undefined,
      from_table_name: fromTable,
      from_column_name: selectedFromColumn.column_name,
      from_column_type: selectedFromColumn.data_type || "",
      to_connection_type: toConnection.type,
      to_connection_id: toConnection.id ?? undefined,
      to_table_name: toTable,
      to_column_name: toColumn.column_name,
      to_column_type: toColumn.data_type || "",
      mapping_order: mappings.length + 1,
    }]);
    setSelectedFromColumn(null);
    toast.success(`매핑 완료: ${selectedFromColumn.column_name} → ${toColumn.column_name}`);
  };

  const removeMapping = (index: number) => {
    setMappings(mappings.filter((_, i) => i !== index).map((m, i) => ({ ...m, mapping_order: i + 1 })));
    toast.success("매핑을 삭제했어요");
  };

  const goBack = () => openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });

  const saveBatchConfig = async () => {
    if (!batchName.trim()) { toast.error("배치 이름을 입력해주세요"); return; }

    if (executionType === "node_flow") {
      if (!selectedFlowId) { toast.error("실행할 플로우를 선택해주세요"); return; }
      let parsedContext: Record<string, any> | undefined;
      if (nodeFlowContext.trim()) {
        try { parsedContext = JSON.parse(nodeFlowContext); } catch { toast.error("추가 데이터의 JSON 형식이 올바르지 않아요"); return; }
      }
      setLoading(true);
      try {
        await BatchAPI.createBatchConfig({ batchName, description: description || undefined, cronSchedule, mappings: [], isActive: true, executionType: "node_flow", nodeFlowId: selectedFlowId, nodeFlowContext: parsedContext });
        toast.success("배치를 저장했어요!");
        goBack();
      } catch (error) { showErrorToast("저장에 실패했어요", error, { guidance: "입력 내용을 확인하고 다시 시도해 주세요." }); } finally { setLoading(false); }
      return;
    }

    if (mappings.length === 0) { toast.error("컬럼 매핑을 하나 이상 추가해주세요"); return; }
    setLoading(true);
    try {
      await BatchAPI.createBatchConfig({ batchName, description: description || undefined, cronSchedule, mappings, isActive: true });
      toast.success("배치를 저장했어요!");
      goBack();
    } catch (error) { showErrorToast("저장에 실패했어요", error, { guidance: "입력 내용을 확인하고 다시 시도해 주세요." }); } finally { setLoading(false); }
  };

  const selectedFlow = nodeFlows.find(f => f.flow_id === selectedFlowId);

  return (
    <div className="mx-auto h-full max-w-[640px] space-y-7 overflow-y-auto p-4 sm:p-6">
      {/* 헤더 */}
      <div>
        <button onClick={goBack} className="mb-2 flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground">
          <ArrowLeft className="h-3.5 w-3.5" />
          배치 관리로 돌아가기
        </button>
        <div className="flex items-center justify-between">
          <div>
            <h1 className="text-xl font-bold tracking-tight">새 배치 등록</h1>
            <p className="mt-1 text-xs text-muted-foreground">데이터를 자동으로 처리하는 배치를 만들어 보세요</p>
          </div>
          <Button
            size="sm"
            onClick={saveBatchConfig}
            disabled={loading || (executionType === "mapping" && mappings.length === 0) || (executionType === "node_flow" && !selectedFlowId)}
            className="h-8 gap-1 text-xs"
          >
            {loading ? <RefreshCw className="h-3.5 w-3.5 animate-spin" /> : <Save className="h-3.5 w-3.5" />}
            {loading ? "저장 중..." : "저장하기"}
          </Button>
        </div>
      </div>

      {/* 실행 방식 선택 */}
      <div>
        <h2 className="mb-3 text-sm font-bold">어떤 방식으로 실행할까요?</h2>
        <div className="grid grid-cols-2 gap-3">
          <button
            onClick={() => setExecutionType("mapping")}
            className={`group relative flex items-center gap-3 rounded-xl border-2 p-4 text-left transition-all ${
              executionType === "mapping"
                ? "border-primary bg-primary/5"
                : "border-border hover:border-muted-foreground/30 hover:bg-muted/50"
            }`}
          >
            <div className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${executionType === "mapping" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`}>
              <Database className="h-5 w-5" />
            </div>
            <div className="min-w-0">
              <div className="text-sm font-semibold">데이터 복사</div>
              <div className="text-[11px] text-muted-foreground">테이블 간 데이터를 옮겨요</div>
            </div>
            {executionType === "mapping" && <div className="absolute right-3 top-3 h-2 w-2 rounded-full bg-primary" />}
          </button>
          <button
            onClick={() => setExecutionType("node_flow")}
            className={`group relative flex items-center gap-3 rounded-xl border-2 p-4 text-left transition-all ${
              executionType === "node_flow"
                ? "border-primary bg-primary/5"
                : "border-border hover:border-muted-foreground/30 hover:bg-muted/50"
            }`}
          >
            <div className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${executionType === "node_flow" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`}>
              <Workflow className="h-5 w-5" />
            </div>
            <div className="min-w-0">
              <div className="text-sm font-semibold">노드 플로우</div>
              <div className="text-[11px] text-muted-foreground">만들어 둔 플로우를 실행해요</div>
            </div>
            {executionType === "node_flow" && <div className="absolute right-3 top-3 h-2 w-2 rounded-full bg-primary" />}
          </button>
        </div>
      </div>

      {/* 기본 정보 */}
      <div>
        <h2 className="mb-3 text-sm font-bold">기본 정보</h2>
        <div className="space-y-4">
          <div className="space-y-1.5">
            <Label htmlFor="batchName" className="text-xs font-medium">배치 이름 <span className="text-destructive">*</span></Label>
            <Input id="batchName" value={batchName} onChange={e => setBatchName(e.target.value)} placeholder="예: 매출 데이터 동기화" className="h-10 text-sm" />
            <p className="text-[11px] text-muted-foreground">어떤 작업인지 한눈에 알 수 있게 적어주세요</p>
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="description" className="text-xs font-medium">설명</Label>
            <Textarea id="description" value={description} onChange={e => setDescription(e.target.value)} placeholder="이 배치가 어떤 일을 하는지 적어주세요" rows={2} className="resize-none text-sm" />
          </div>
        </div>
      </div>

      {/* 실행 스케줄 */}
      <div>
        <h2 className="mb-1 text-sm font-bold">언제 실행할까요?</h2>
        <p className="mb-3 text-[12px] text-muted-foreground">자주 쓰는 스케줄을 골라주세요. 원하는 게 없으면 직접 설정할 수도 있어요.</p>
        <div className="rounded-xl border bg-card p-5">
          {/* 프리셋 버튼들 */}
          <div className="mb-4 flex flex-wrap gap-2">
            {SCHEDULE_PRESETS.map((preset, i) => (
              <button
                key={preset.cron}
                onClick={() => { setScheduleMode("preset"); setSelectedPresetIndex(i); }}
                className={`rounded-full border px-3.5 py-1.5 text-[12px] font-medium transition-all ${
                  scheduleMode === "preset" && selectedPresetIndex === i
                    ? "border-primary bg-primary/10 text-primary"
                    : "border-border text-muted-foreground hover:border-primary/50 hover:text-primary"
                }`}
              >
                {preset.label}
              </button>
            ))}
            <button
              onClick={() => setScheduleMode("custom")}
              className={`rounded-full border border-dashed px-3.5 py-1.5 text-[12px] font-medium transition-all ${
                scheduleMode === "custom"
                  ? "border-primary bg-primary/10 text-primary"
                  : "border-border text-muted-foreground hover:border-primary/50 hover:text-primary"
              }`}
            >
              직접 설정
            </button>
          </div>

          {/* 직접 설정 UI */}
          {scheduleMode === "custom" && (
            <div className="mb-4 flex flex-wrap items-center gap-3">
              <div className="space-y-1">
                <span className="text-[11px] font-medium text-muted-foreground">반복</span>
                <Select value={customRepeat} onValueChange={setCustomRepeat}>
                  <SelectTrigger className="h-9 w-[100px] text-xs"><SelectValue /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="daily">매일</SelectItem>
                    <SelectItem value="weekly">매주</SelectItem>
                    <SelectItem value="monthly">매월</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              {customRepeat === "weekly" && (
                <div className="space-y-1">
                  <span className="text-[11px] font-medium text-muted-foreground">요일</span>
                  <Select value={customDow} onValueChange={setCustomDow}>
                    <SelectTrigger className="h-9 w-[100px] text-xs"><SelectValue /></SelectTrigger>
                    <SelectContent>
                      <SelectItem value="1">월요일</SelectItem>
                      <SelectItem value="2">화요일</SelectItem>
                      <SelectItem value="3">수요일</SelectItem>
                      <SelectItem value="4">목요일</SelectItem>
                      <SelectItem value="5">금요일</SelectItem>
                      <SelectItem value="6">토요일</SelectItem>
                      <SelectItem value="0">일요일</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
              )}
              <div className="space-y-1">
                <span className="text-[11px] font-medium text-muted-foreground">시</span>
                <Select value={customHour} onValueChange={setCustomHour}>
                  <SelectTrigger className="h-9 w-[90px] text-xs"><SelectValue /></SelectTrigger>
                  <SelectContent>
                    {Array.from({ length: 24 }).map((_, h) => (
                      <SelectItem key={h} value={String(h)}>
                        {h < 12 ? `오전 ${h === 0 ? 12 : h}시` : `오후 ${h === 12 ? 12 : h - 12}시`}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
              <div className="space-y-1">
                <span className="text-[11px] font-medium text-muted-foreground">분</span>
                <Select value={customMinute} onValueChange={setCustomMinute}>
                  <SelectTrigger className="h-9 w-[80px] text-xs"><SelectValue /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="0">0분</SelectItem>
                    <SelectItem value="15">15분</SelectItem>
                    <SelectItem value="30">30분</SelectItem>
                    <SelectItem value="45">45분</SelectItem>
                  </SelectContent>
                </Select>
              </div>
            </div>
          )}

          {/* 미리보기 */}
          <div className="flex items-center gap-2 rounded-lg bg-primary/5 px-4 py-3">
            <Clock className="h-4 w-4 shrink-0 text-primary" />
            <span className="text-[13px] font-medium text-primary">{schedulePreview}</span>
          </div>
        </div>
      </div>

      {/* 노드 플로우 설정 */}
      {executionType === "node_flow" && (
        <div>
          <h2 className="mb-1 text-sm font-bold">어떤 플로우를 실행할까요?</h2>
          <p className="mb-3 text-[12px] text-muted-foreground">제어관리에서 만들어 둔 노드 플로우를 선택해주세요</p>

          {nodeFlows.length === 0 ? (
            <div className="flex h-32 items-center justify-center rounded-lg border border-dashed">
              <p className="text-xs text-muted-foreground">등록된 노드 플로우가 없어요</p>
            </div>
          ) : (
            <div className="space-y-2">
              <div className="relative">
                <Search className="absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
                <Input
                  value={flowSearch}
                  onChange={e => setFlowSearch(e.target.value)}
                  placeholder="플로우 이름으로 검색하세요"
                  className="h-8 pl-9 text-xs"
                />
              </div>
              <div className="max-h-[240px] space-y-2 overflow-y-auto">
                {nodeFlows
                  .filter(flow => !flowSearch || flow.flow_name.toLowerCase().includes(flowSearch.toLowerCase()) || (flow.description || "").toLowerCase().includes(flowSearch.toLowerCase()))
                  .map(flow => (
                    <button
                      key={flow.flow_id}
                      onClick={() => setSelectedFlowId(flow.flow_id === selectedFlowId ? null : flow.flow_id)}
                      className={`flex w-full items-center gap-3 rounded-lg border p-3.5 text-left transition-all ${
                        selectedFlowId === flow.flow_id
                          ? "border-primary bg-primary/5"
                          : "border-border hover:border-primary/30"
                      }`}
                    >
                      <div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${selectedFlowId === flow.flow_id ? "bg-primary/10 text-primary" : "bg-indigo-500/10 text-indigo-500"}`}>
                        <Workflow className="h-4 w-4" />
                      </div>
                      <div className="min-w-0 flex-1">
                        <p className="text-sm font-semibold">{flow.flow_name}</p>
                        <p className="text-[11px] text-muted-foreground">
                          {flow.description || "설명 없음"} &middot; 노드 {flow.node_count}개
                        </p>
                      </div>
                      {selectedFlowId === flow.flow_id && (
                        <svg className="h-4 w-4 shrink-0 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20 6 9 17l-5-5"/></svg>
                      )}
                    </button>
                  ))}
                {nodeFlows.filter(flow => !flowSearch || flow.flow_name.toLowerCase().includes(flowSearch.toLowerCase()) || (flow.description || "").toLowerCase().includes(flowSearch.toLowerCase())).length === 0 && (
                  <p className="py-6 text-center text-xs text-muted-foreground">검색 결과가 없어요</p>
                )}
              </div>
            </div>
          )}

          {selectedFlow && (
            <div className="mt-4 space-y-1.5">
              <Label className="text-xs font-medium">실행할 때 전달할 데이터 <span className="text-muted-foreground">(선택)</span></Label>
              <Textarea
                value={nodeFlowContext}
                onChange={e => setNodeFlowContext(e.target.value)}
                placeholder='예: {"target_status": "퇴사"}'
                rows={3}
                className="resize-none font-mono text-xs"
              />
              <p className="text-[11px] text-muted-foreground">플로우가 실행될 때 참고할 데이터를 JSON 형식으로 적어주세요. 비워두면 기본값을 사용해요.</p>
            </div>
          )}
        </div>
      )}

      {/* 매핑 설정 (데이터 복사 타입) */}
      {executionType === "mapping" && (
        <>
          <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
            {/* FROM */}
            <div className="space-y-3 rounded-xl border border-emerald-500/20 p-4 sm:p-5">
              <div className="flex items-center gap-2">
                <div className="flex h-6 w-6 items-center justify-center rounded bg-emerald-500/10 text-emerald-500">
                  <Database className="h-3.5 w-3.5" />
                </div>
                <span className="text-sm font-semibold">원본 (FROM)</span>
              </div>
              <p className="text-[11px] text-muted-foreground">데이터를 가져올 곳을 선택해주세요</p>

              <div className="space-y-1.5">
                <Label className="text-xs">커넥션</Label>
                <Select value={fromConnection?.type === "internal" ? "internal" : fromConnection?.id?.toString() || ""} onValueChange={handleFromConnectionChange} disabled={loadingConnections}>
                  <SelectTrigger className="h-9 text-sm"><SelectValue placeholder={loadingConnections ? "불러오는 중..." : "커넥션을 선택해주세요"} /></SelectTrigger>
                  <SelectContent>
                    {Array.isArray(connections) && connections.map(conn => (
                      <SelectItem key={conn.type === "internal" ? "internal" : conn.id?.toString() || conn.name} value={conn.type === "internal" ? "internal" : conn.id?.toString() || "unknown"}>
                        {conn.name} ({conn.type})
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1.5">
                <Label className="text-xs">테이블</Label>
                <Select value={fromTable} onValueChange={handleFromTableChange} disabled={!fromConnection}>
                  <SelectTrigger className="h-9 text-sm"><SelectValue placeholder="테이블을 선택해주세요" /></SelectTrigger>
                  <SelectContent>{fromTables.map(t => <SelectItem key={t} value={t}>{t}</SelectItem>)}</SelectContent>
                </Select>
              </div>

              {fromTable && (
                <div className="space-y-1.5">
                  <Label className="text-xs font-medium">{fromTable} 컬럼</Label>
                  <div className="max-h-56 space-y-1 overflow-y-auto rounded-md border bg-background p-2">
                    {fromColumns.map(col => (
                      <button
                        key={col.column_name}
                        onClick={() => handleFromColumnClick(col)}
                        className={`flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm transition-colors ${
                          selectedFromColumn?.column_name === col.column_name ? "bg-emerald-500/10 text-emerald-600 ring-1 ring-emerald-500/30" : "hover:bg-muted"
                        }`}
                      >
                        <span className="font-medium">{col.column_name}</span>
                        <span className="text-[10px] text-muted-foreground">{col.data_type}</span>
                      </button>
                    ))}
                    {fromColumns.length === 0 && <div className="py-6 text-center text-xs text-muted-foreground">컬럼을 불러오는 중...</div>}
                  </div>
                </div>
              )}
            </div>

            {/* TO */}
            <div className="space-y-3 rounded-xl border border-sky-500/20 p-4 sm:p-5">
              <div className="flex items-center gap-2">
                <div className="flex h-6 w-6 items-center justify-center rounded bg-sky-500/10 text-sky-500">
                  <Database className="h-3.5 w-3.5" />
                </div>
                <span className="text-sm font-semibold">대상 (TO)</span>
              </div>
              <p className="text-[11px] text-muted-foreground">왼쪽에서 컬럼을 선택한 뒤, 여기서 대상 컬럼을 클릭해주세요</p>

              <div className="space-y-1.5">
                <Label className="text-xs">커넥션</Label>
                <Select value={toConnection?.type === "internal" ? "internal" : toConnection?.id?.toString() || ""} onValueChange={handleToConnectionChange} disabled={loadingConnections}>
                  <SelectTrigger className="h-9 text-sm"><SelectValue placeholder={loadingConnections ? "불러오는 중..." : "커넥션을 선택해주세요"} /></SelectTrigger>
                  <SelectContent>
                    {Array.isArray(connections) && connections.map(conn => (
                      <SelectItem key={conn.type === "internal" ? "internal" : conn.id?.toString() || conn.name} value={conn.type === "internal" ? "internal" : conn.id?.toString() || "unknown"}>
                        {conn.name} ({conn.type})
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="space-y-1.5">
                <Label className="text-xs">테이블</Label>
                <Select value={toTable} onValueChange={handleToTableChange} disabled={!toConnection}>
                  <SelectTrigger className="h-9 text-sm"><SelectValue placeholder="테이블을 선택해주세요" /></SelectTrigger>
                  <SelectContent>{toTables.map(t => <SelectItem key={t} value={t}>{t}</SelectItem>)}</SelectContent>
                </Select>
              </div>

              {toTable && (
                <div className="space-y-1.5">
                  <Label className="text-xs font-medium">{toTable} 컬럼</Label>
                  <div className="max-h-56 space-y-1 overflow-y-auto rounded-md border bg-background p-2">
                    {toColumns.map(col => (
                      <button
                        key={col.column_name}
                        onClick={() => handleToColumnClick(col)}
                        className={`flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm transition-colors ${
                          selectedFromColumn ? "hover:bg-sky-500/10 hover:text-sky-600" : "cursor-not-allowed opacity-50"
                        }`}
                        disabled={!selectedFromColumn}
                      >
                        <span className="font-medium">{col.column_name}</span>
                        <span className="text-[10px] text-muted-foreground">{col.data_type}</span>
                      </button>
                    ))}
                    {toColumns.length === 0 && <div className="py-6 text-center text-xs text-muted-foreground">컬럼을 불러오는 중...</div>}
                  </div>
                </div>
              )}
            </div>
          </div>

          {/* 매핑 현황 */}
          {mappings.length > 0 && (
            <div className="space-y-3 rounded-xl border p-4 sm:p-5">
              <div className="flex items-center gap-2 text-sm font-semibold">
                컬럼 매핑
                <Badge variant="secondary" className="h-5 px-1.5 text-[10px]">{mappings.length}</Badge>
              </div>
              <div className="space-y-1.5">
                {mappings.map((m, i) => (
                  <div key={i} className="group flex items-center gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm">
                    <span className="w-5 shrink-0 text-center text-[10px] text-muted-foreground">{i + 1}</span>
                    <div className="flex min-w-0 flex-1 items-center gap-2">
                      <span className="truncate font-mono text-xs">
                        <span className="text-emerald-500">{m.from_table_name}</span>.{m.from_column_name}
                      </span>
                      <ChevronRight className="h-3 w-3 shrink-0 text-muted-foreground" />
                      <span className="truncate font-mono text-xs">
                        <span className="text-sky-500">{m.to_table_name}</span>.{m.to_column_name}
                      </span>
                    </div>
                    <button onClick={() => removeMapping(i)} className="shrink-0 rounded p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100">
                      <Trash2 className="h-3.5 w-3.5" />
                    </button>
                  </div>
                ))}
              </div>
            </div>
          )}
        </>
      )}

      {/* 하단 액션 */}
      <div className="flex justify-end gap-2 border-t pt-5">
        <Button variant="outline" size="sm" onClick={goBack} className="h-9 text-xs">취소</Button>
        <Button
          size="sm"
          onClick={saveBatchConfig}
          disabled={loading || (executionType === "mapping" && mappings.length === 0) || (executionType === "node_flow" && !selectedFlowId)}
          className="h-9 gap-1 text-xs"
        >
          {loading ? <RefreshCw className="h-3.5 w-3.5 animate-spin" /> : <Save className="h-3.5 w-3.5" />}
          {loading ? "저장 중..." : "저장하기"}
        </Button>
      </div>
    </div>
  );
}
