"use client";

import React, { useState, useEffect, useMemo } from "react";
import { useParams, useRouter } from "next/navigation";
import { useTabStore } from "@/stores/tabStore";
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 { Switch } from "@/components/ui/switch";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { RefreshCw, Save, ArrowLeft, Plus, Trash2, Database, Workflow, Clock, Info, Layers, Link, Search } from "lucide-react";
import { toast } from "sonner";
import {
  BatchAPI,
  BatchConfig,
  BatchMapping,
  ConnectionInfo,
  type NodeFlowInfo,
  type BatchExecutionType,
} from "@/lib/api/batch";
import { BatchManagementAPI } from "@/lib/api/batchManagement";

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 {
  if (repeat === "daily") return `${minute} ${hour} * * *`;
  if (repeat === "weekly") return `${minute} ${hour} * * ${dow}`;
  if (repeat === "monthly") return `${minute} ${hour} 1 * *`;
  return `${minute} ${hour} * * *`;
}

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}에 실행돼요`;
}

function parseCronToScheduleState(cron: string): { mode: "preset" | "custom"; presetIndex: number; repeat: string; dow: string; hour: string; minute: string } {
  const presetIdx = SCHEDULE_PRESETS.findIndex(p => p.cron === cron);
  if (presetIdx >= 0) return { mode: "preset", presetIndex: presetIdx, repeat: "daily", dow: "1", hour: "9", minute: "0" };
  const parts = cron.split(" ");
  if (parts.length < 5) return { mode: "preset", presetIndex: 3, repeat: "daily", dow: "1", hour: "9", minute: "0" };
  const [m, h, dom, , dw] = parts;
  const repeat = dw !== "*" ? "weekly" : dom !== "*" ? "monthly" : "daily";
  return { mode: "custom", presetIndex: -1, repeat, dow: dw !== "*" ? dw : "1", hour: h !== "*" ? h : "9", minute: m.startsWith("*/") ? "0" : m };
}

interface BatchColumnInfo {
  column_name: string;
  data_type: string;
  is_nullable: string;
}

// 배치 타입 감지 함수
const detectBatchType = (mapping: BatchMapping): 'db-to-db' | 'restapi-to-db' | 'db-to-restapi' => {
  const fromType = mapping.from_connection_type;
  const toType = mapping.to_connection_type;
  
  if (fromType === 'restapi' && (toType === 'internal' || toType === 'external')) {
    return 'restapi-to-db';
  } else if ((fromType === 'internal' || fromType === 'external') && toType === 'restapi') {
    return 'db-to-restapi';
  } else {
    return 'db-to-db';
  }
};

export default function BatchEditPage() {
  const params = useParams();
  const router = useRouter();
  const { openTab } = useTabStore();
  const batchId = parseInt(params.id as string);

  // 기본 상태
  const [loading, setLoading] = useState(false);
  const [batchConfig, setBatchConfig] = useState<BatchConfig | null>(null);
  const [batchName, setBatchName] = useState("");
  const [description, setDescription] = useState("");
  const [isActive, setIsActive] = useState("Y");

  // 스케줄 관련
  const [scheduleMode, setScheduleMode] = useState<"preset" | "custom">("preset");
  const [selectedPresetIndex, setSelectedPresetIndex] = useState(3);
  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" && selectedPresetIndex >= 0) return SCHEDULE_PRESETS[selectedPresetIndex].cron;
    return buildCustomCron(customRepeat, customDow, customHour, customMinute);
  }, [scheduleMode, selectedPresetIndex, customRepeat, customDow, customHour, customMinute]);

  const schedulePreview = useMemo(() => {
    if (scheduleMode === "preset" && selectedPresetIndex >= 0) return SCHEDULE_PRESETS[selectedPresetIndex].preview;
    return customCronPreview(customRepeat, customDow, customHour, customMinute);
  }, [scheduleMode, selectedPresetIndex, customRepeat, customDow, customHour, customMinute]);
  const [saveMode, setSaveMode] = useState<"INSERT" | "UPSERT">("INSERT");
  const [conflictKey, setConflictKey] = useState("");
  const [authServiceName, setAuthServiceName] = useState("");
  const [authServiceNames, setAuthServiceNames] = useState<string[]>([]);
  const [dataArrayPath, setDataArrayPath] = useState("");

  // 연결 정보
  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<BatchColumnInfo[]>([]);
  const [toColumns, setToColumns] = useState<BatchColumnInfo[]>([]);
  
  // 매핑 정보
  const [mappings, setMappings] = useState<BatchMapping[]>([]);
  
  // 배치 타입 감지
  const [batchType, setBatchType] = useState<'db-to-db' | 'restapi-to-db' | 'db-to-restapi' | null>(null);

  // 실행 타입 (mapping 또는 node_flow)
  const [executionType, setExecutionType] = useState<BatchExecutionType>("mapping");
  const [nodeFlows, setNodeFlows] = useState<NodeFlowInfo[]>([]);
  const [selectedFlowId, setSelectedFlowId] = useState<number | null>(null);
  const [nodeFlowContext, setNodeFlowContext] = useState("");
  const [flowSearch, setFlowSearch] = useState("");

  // REST API 미리보기 상태
  const [apiPreviewData, setApiPreviewData] = useState<any[]>([]);
  const [fromApiFields, setFromApiFields] = useState<string[]>([]);

  // 인증 토큰 모드 (직접 입력 / DB에서 선택)
  const [authTokenMode, setAuthTokenMode] = useState<"direct" | "db">("direct");
  const [fromApiKey, setFromApiKey] = useState("");

  // API 파라미터 설정
  const [apiParamType, setApiParamType] = useState<"none" | "url" | "query">("none");
  const [apiParamName, setApiParamName] = useState("");
  const [apiParamValue, setApiParamValue] = useState("");
  const [apiParamSource, setApiParamSource] = useState<"static" | "dynamic">("static");

  // 매핑 리스트 (새로운 UI용)
  interface MappingItem {
    id: string;
    dbColumn: string;
    sourceType: "api" | "fixed";
    apiField: string;
    fixedValue: string;
  }
  const [mappingList, setMappingList] = useState<MappingItem[]>([]);

  // 페이지 로드 시 배치 정보 조회
  useEffect(() => {
    if (batchId) {
      loadBatchConfig();
      loadConnections();
      loadAuthServiceNames();
    }
  }, [batchId]);

  // 인증 서비스명 목록 로드
  const loadAuthServiceNames = async () => {
    try {
      const names = await BatchAPI.getAuthServiceNames();
      setAuthServiceNames(names);
    } catch (error) {
      console.error("인증 서비스 목록 로드 실패:", error);
    }
  };

  // 연결 정보가 로드된 후 배치 설정의 연결 정보 설정
  useEffect(() => {
    if (batchConfig && connections.length > 0 && batchConfig.batch_mappings && batchConfig.batch_mappings.length > 0) {
      const firstMapping = batchConfig.batch_mappings[0];
      console.log("🔗 연결 정보 설정 시작:", firstMapping);
      
      // FROM 연결 정보 설정
      if (firstMapping.from_connection_type === 'internal') {
        setFromConnection({ type: 'internal', name: '내부 DB' });
        // 내부 DB 테이블 목록 로드
        BatchAPI.getTablesFromConnection({ type: 'internal', name: '내부 DB' }).then(tables => {
          console.log("📋 FROM 테이블 목록:", tables);
          setFromTables(tables);
          
          // 컬럼 정보도 로드
          if (firstMapping.from_table_name) {
            BatchAPI.getTableColumns({ type: 'internal', name: '내부 DB' }, firstMapping.from_table_name).then(columns => {
              console.log("📊 FROM 컬럼 목록:", columns);
              setFromColumns(columns);
            });
          }
        });
      } else if (firstMapping.from_connection_id) {
        const fromConn = connections.find(c => c.id === firstMapping.from_connection_id);
        if (fromConn) {
          setFromConnection(fromConn);
          // 외부 DB 테이블 목록 로드
          BatchAPI.getTablesFromConnection(fromConn).then(tables => {
            console.log("📋 FROM 테이블 목록:", tables);
            setFromTables(tables);
            
            // 컬럼 정보도 로드
            if (firstMapping.from_table_name) {
              BatchAPI.getTableColumns(fromConn, firstMapping.from_table_name).then(columns => {
                console.log("📊 FROM 컬럼 목록:", columns);
                setFromColumns(columns);
              });
            }
          });
        }
      }
      
      // TO 연결 정보 설정
      if (firstMapping.to_connection_type === 'internal') {
        setToConnection({ type: 'internal', name: '내부 DB' });
        // 내부 DB 테이블 목록 로드
        BatchAPI.getTablesFromConnection({ type: 'internal', name: '내부 DB' }).then(tables => {
          console.log("📋 TO 테이블 목록:", tables);
          setToTables(tables);
          
          // 컬럼 정보도 로드
          if (firstMapping.to_table_name) {
            BatchAPI.getTableColumns({ type: 'internal', name: '내부 DB' }, firstMapping.to_table_name).then(columns => {
              console.log("📊 TO 컬럼 목록:", columns);
              setToColumns(columns);
            });
          }
        });
      } else if (firstMapping.to_connection_id) {
        const toConn = connections.find(c => c.id === firstMapping.to_connection_id);
        if (toConn) {
          setToConnection(toConn);
          // 외부 DB 테이블 목록 로드
          BatchAPI.getTablesFromConnection(toConn).then(tables => {
            console.log("📋 TO 테이블 목록:", tables);
            setToTables(tables);
            
            // 컬럼 정보도 로드
            if (firstMapping.to_table_name) {
              BatchAPI.getTableColumns(toConn, firstMapping.to_table_name).then(columns => {
                console.log("📊 TO 컬럼 목록:", columns);
                setToColumns(columns);
              });
            }
          });
        }
      }
    }
  }, [batchConfig, connections]);

  // 배치 설정 조회
  const loadBatchConfig = async () => {
    try {
      setLoading(true);
      console.log("🔍 배치 설정 조회 시작:", batchId);
      
      const config = await BatchAPI.getBatchConfig(batchId);
      console.log("📋 조회된 배치 설정:", config);
      
      setBatchConfig(config);
      setBatchName(config.batch_name);
      setDescription(config.description || "");

      // 스케줄 파싱
      const schedState = parseCronToScheduleState(config.cron_schedule);
      setScheduleMode(schedState.mode);
      setSelectedPresetIndex(schedState.presetIndex);
      setCustomRepeat(schedState.repeat);
      setCustomDow(schedState.dow);
      setCustomHour(schedState.hour);
      setCustomMinute(schedState.minute);
      setIsActive(config.is_active || "Y");
      setSaveMode((config as any).save_mode || "INSERT");
      setConflictKey((config as any).conflict_key || "");
      setAuthServiceName((config as any).auth_service_name || "");
      setDataArrayPath((config as any).data_array_path || "");

      // 실행 타입 복원
      const configExecType = (config as any).execution_type as BatchExecutionType | undefined;
      if (configExecType === "node_flow") {
        setExecutionType("node_flow");
        setSelectedFlowId((config as any).node_flow_id || null);
        setNodeFlowContext((config as any).node_flow_context ? JSON.stringify((config as any).node_flow_context, null, 2) : "");
        BatchAPI.getNodeFlows().then(setNodeFlows);
      }
      
      // 인증 토큰 모드 설정
      if ((config as any).auth_service_name) {
        setAuthTokenMode("db");
      } else {
        setAuthTokenMode("direct");
      }
      
      if (config.batch_mappings && config.batch_mappings.length > 0) {
        // API 키 설정 (첫 번째 매핑에서)
        const firstMappingForApiKey = config.batch_mappings[0];
        if (firstMappingForApiKey.from_api_key) {
          setFromApiKey(firstMappingForApiKey.from_api_key);
        }
        console.log("📊 매핑 정보:", config.batch_mappings);
        console.log("📊 매핑 개수:", config.batch_mappings.length);
        config.batch_mappings.forEach((mapping, idx) => {
          console.log(`📊 매핑 #${idx + 1}:`, {
            from: `${mapping.from_column_name} (${mapping.from_column_type})`,
            to: `${mapping.to_column_name} (${mapping.to_column_type})`,
            type: mapping.mapping_type
          });
        });
        setMappings(config.batch_mappings);
        
        // 첫 번째 매핑에서 연결 및 테이블 정보 추출
        const firstMapping = config.batch_mappings[0];
        setFromTable(firstMapping.from_table_name);
        setToTable(firstMapping.to_table_name);
        
        // 배치 타입 감지
        const detectedBatchType = detectBatchType(firstMapping);
        setBatchType(detectedBatchType);
        console.log("🎯 감지된 배치 타입:", detectedBatchType);
        
        // FROM 연결 정보 설정
        if (firstMapping.from_connection_type === 'internal') {
          setFromConnection({ type: 'internal', name: '내부 DB' });
        } else if (firstMapping.from_connection_id) {
          // 외부 연결은 connections 로드 후 설정
          setTimeout(() => {
            const fromConn = connections.find(c => c.id === firstMapping.from_connection_id);
            if (fromConn) {
              setFromConnection(fromConn);
            }
          }, 100);
        }
        
        // TO 연결 정보 설정
        if (firstMapping.to_connection_type === 'internal') {
          setToConnection({ type: 'internal', name: '내부 DB' });
        } else if (firstMapping.to_connection_id) {
          // 외부 연결은 connections 로드 후 설정
          setTimeout(() => {
            const toConn = connections.find(c => c.id === firstMapping.to_connection_id);
            if (toConn) {
              setToConnection(toConn);
            }
          }, 100);
        }
        
        console.log("🔗 테이블 정보 설정:", {
          fromTable: firstMapping.from_table_name,
          toTable: firstMapping.to_table_name,
          fromConnectionType: firstMapping.from_connection_type,
          toConnectionType: firstMapping.to_connection_type
        });
        
        // 기존 매핑을 mappingList로 변환
        const convertedMappingList: MappingItem[] = config.batch_mappings.map((mapping, index) => ({
          id: `mapping-${index}-${Date.now()}`,
          dbColumn: mapping.to_column_name || "",
          sourceType: (mapping as any).mapping_type === "fixed" ? "fixed" as const : "api" as const,
          apiField: (mapping as any).mapping_type === "fixed" ? "" : mapping.from_column_name || "",
          fixedValue: (mapping as any).mapping_type === "fixed" ? mapping.from_column_name || "" : "",
        }));
        setMappingList(convertedMappingList);
        console.log("🔄 변환된 mappingList:", convertedMappingList);
      }
      
    } catch (error) {
      console.error("❌ 배치 설정 조회 오류:", error);
      toast.error("배치 설정을 불러오는데 실패했습니다.");
    } finally {
      setLoading(false);
    }
  };

  // 연결 정보 조회
  const loadConnections = async () => {
    try {
      const connectionList = await BatchAPI.getConnections();
      setConnections(connectionList);
    } catch (error) {
      console.error("연결 정보 조회 오류:", error);
      toast.error("연결 정보를 불러오는데 실패했습니다.");
    }
  };

  // FROM 연결 변경 시
  const handleFromConnectionChange = async (connectionId: string) => {
    const connection = connections.find(c => c.id?.toString() === connectionId) || 
                     (connectionId === 'internal' ? { type: 'internal' as const, name: '내부 DB' } : null);
    
    if (connection) {
      setFromConnection(connection);
      
      try {
        const tables = await BatchAPI.getTablesFromConnection(connection);
        setFromTables(tables);
        setFromTable("");
        setFromColumns([]);
      } catch (error) {
        console.error("테이블 목록 조회 오류:", error);
        toast.error("테이블 목록을 불러오는데 실패했습니다.");
      }
    }
  };

  // TO 연결 변경 시
  const handleToConnectionChange = async (connectionId: string) => {
    const connection = connections.find(c => c.id?.toString() === connectionId) || 
                     (connectionId === 'internal' ? { type: 'internal' as const, name: '내부 DB' } : null);
    
    if (connection) {
      setToConnection(connection);
      
      try {
        const tables = await BatchAPI.getTablesFromConnection(connection);
        setToTables(tables);
        setToTable("");
        setToColumns([]);
      } catch (error) {
        console.error("테이블 목록 조회 오류:", error);
        toast.error("테이블 목록을 불러오는데 실패했습니다.");
      }
    }
  };

  // FROM 테이블 변경 시
  const handleFromTableChange = async (tableName: string) => {
    setFromTable(tableName);
    
    if (fromConnection && tableName) {
      try {
        const columns = await BatchAPI.getTableColumns(fromConnection, tableName);
        setFromColumns(columns);
      } catch (error) {
        console.error("컬럼 정보 조회 오류:", error);
        toast.error("컬럼 정보를 불러오는데 실패했습니다.");
      }
    }
  };

  // TO 테이블 변경 시
  const handleToTableChange = async (tableName: string) => {
    setToTable(tableName);
    
    if (toConnection && tableName) {
      try {
        const columns = await BatchAPI.getTableColumns(toConnection, tableName);
        setToColumns(columns);
      } catch (error) {
        console.error("컬럼 정보 조회 오류:", error);
        toast.error("컬럼 정보를 불러오는데 실패했습니다.");
      }
    }
  };

  // 매핑 추가
  const addMapping = () => {
    const newMapping: BatchMapping = {
      from_connection_type: fromConnection?.type === 'internal' ? 'internal' : 'external',
      from_connection_id: fromConnection?.type === 'internal' ? undefined : fromConnection?.id,
      from_table_name: fromTable,
      from_column_name: '',
      from_column_type: '',
      to_connection_type: toConnection?.type === 'internal' ? 'internal' : 'external',
      to_connection_id: toConnection?.type === 'internal' ? undefined : toConnection?.id,
      to_table_name: toTable,
      to_column_name: '',
      to_column_type: '',
      mapping_type: 'direct',
      mapping_order: mappings.length + 1
    };
    
    setMappings([...mappings, newMapping]);
  };

  // REST API → DB 매핑 추가
  const addRestapiToDbMapping = () => {
    if (!batchConfig || !batchConfig.batch_mappings || batchConfig.batch_mappings.length === 0) {
      return;
    }

    const first = batchConfig.batch_mappings[0] as any;

    const newMapping: BatchMapping = {
      // FROM: REST API (기존 설정 그대로 복사)
      from_connection_type: "restapi" as any,
      from_connection_id: first.from_connection_id,
      from_table_name: first.from_table_name,
      from_column_name: "",
      from_column_type: "",
      // TO: DB (기존 설정 그대로 복사)
      to_connection_type: first.to_connection_type as any,
      to_connection_id: first.to_connection_id,
      to_table_name: first.to_table_name,
      to_column_name: "",
      to_column_type: "",
      mapping_type: (first.mapping_type as any) || "direct",
      mapping_order: mappings.length + 1,
    };

    setMappings((prev) => [...prev, newMapping]);
  };

  // mappingList 관련 함수들 (새로운 UI용)
  const addMappingListItem = () => {
    const newId = `mapping-${Date.now()}`;
    setMappingList((prev) => [
      ...prev,
      {
        id: newId,
        dbColumn: "",
        sourceType: "api",
        apiField: "",
        fixedValue: "",
      },
    ]);
  };

  const removeMappingListItem = (id: string) => {
    setMappingList((prev) => prev.filter((m) => m.id !== id));
  };

  const updateMappingListItem = (id: string, updates: Partial<MappingItem>) => {
    setMappingList((prev) => prev.map((m) => (m.id === id ? { ...m, ...updates } : m)));
  };

  // REST API 데이터 미리보기 (수정 화면용)
  const previewRestApiData = async () => {
    if (!mappings || mappings.length === 0) {
      toast.error("미리보기할 REST API 매핑이 없습니다.");
      return;
    }

    const first: any = mappings[0];

    if (!first.from_api_url || !first.from_table_name) {
      toast.error("API URL과 엔드포인트 정보가 없습니다.");
      return;
    }

    // DB 선택 모드일 때만 서비스명 검증 (직접 입력 모드는 빈 값 허용 - 공개 API용)
    if (authTokenMode === "db" && !authServiceName) {
      toast.error("인증 토큰 서비스를 선택해주세요.");
      return;
    }

    try {
      const method =
        (first.from_api_method as "GET" | "POST" | "PUT" | "DELETE") || "GET";

      const paramInfo =
        apiParamType !== "none" && apiParamName && apiParamValue
          ? {
              paramType: apiParamType,
              paramName: apiParamName,
              paramValue: apiParamValue,
              paramSource: apiParamSource,
            }
          : undefined;

      // authTokenMode에 따라 올바른 값 전달
      const result = await BatchManagementAPI.previewRestApiData(
        first.from_api_url,
        authTokenMode === "direct" ? fromApiKey : "", // 직접 입력일 때만 API 키 전달
        first.from_table_name,
        method,
        paramInfo,
        first.from_api_body || undefined,
        authTokenMode === "db" ? authServiceName : undefined, // DB 선택 모드일 때 서비스명 전달
        dataArrayPath || undefined
      );

      setApiPreviewData(result.samples || []);
      setFromApiFields(result.fields || []);

      toast.success(
        `API 데이터 미리보기 완료! ${result.fields.length}개 필드, ${result.samples.length}개 레코드`
      );
    } catch (error: any) {
      console.error("REST API 미리보기 오류:", error);
      toast.error(error?.message || "API 데이터 미리보기에 실패했습니다.");
    }
  };

  // 매핑 삭제
  const removeMapping = (index: number) => {
    const updatedMappings = mappings.filter((_, i) => i !== index);
    setMappings(updatedMappings);
  };

  // 매핑 업데이트
  const updateMapping = (index: number, field: keyof BatchMapping, value: any) => {
    setMappings(prevMappings => {
      const updatedMappings = [...prevMappings];
      updatedMappings[index] = { ...updatedMappings[index], [field]: value };
      return updatedMappings;
    });
  };

  // 배치 설정 저장
  const saveBatchConfig = async () => {
    if (!batchName || !cronSchedule) {
      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.updateBatchConfig(batchId, {
          batchName,
          description,
          cronSchedule,
          isActive: isActive as "Y" | "N",
          mappings: [],
          executionType: "node_flow",
          nodeFlowId: selectedFlowId,
          nodeFlowContext: parsedContext,
        });
        toast.success("배치 설정이 저장되었습니다!");
        openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });
      } catch (error) {
        console.error("배치 저장 실패:", error);
        toast.error("배치 저장에 실패했습니다.");
      } finally {
        setLoading(false);
      }
      return;
    }

    // 매핑 타입 저장 - restapi-to-db인 경우 mappingList 사용, 아닌 경우 mappings 사용
    const effectiveMappings = batchType === "restapi-to-db" ? mappingList : mappings;
    
    if (effectiveMappings.length === 0) {
      toast.error("매핑을 최소 하나 이상 설정해주세요.");
      return;
    }

    try {
      setLoading(true);
      
      // restapi-to-db인 경우 mappingList를 mappings 형식으로 변환
      let finalMappings: BatchMapping[] = mappings;
      
      if (batchType === "restapi-to-db" && batchConfig?.batch_mappings?.[0]) {
        const first = batchConfig.batch_mappings[0] as any;
        finalMappings = mappingList
          .filter((m) => m.dbColumn) // DB 컬럼이 선택된 것만
          .map((m, index) => ({
            // FROM: REST API (기존 설정 복사)
            from_connection_type: "restapi" as any,
            from_connection_id: first.from_connection_id,
            from_table_name: first.from_table_name,
            from_column_name: m.sourceType === "fixed" ? m.fixedValue : m.apiField,
            from_column_type: m.sourceType === "fixed" ? "text" : "text",
            from_api_url: mappings[0]?.from_api_url || first.from_api_url,
            from_api_key: authTokenMode === "direct" ? fromApiKey : first.from_api_key,
            from_api_method: mappings[0]?.from_api_method || first.from_api_method,
            from_api_body: mappings[0]?.from_api_body || first.from_api_body,
            // TO: DB (기존 설정 복사)
            to_connection_type: first.to_connection_type as any,
            to_connection_id: first.to_connection_id,
            to_table_name: toTable || first.to_table_name,
            to_column_name: m.dbColumn,
            to_column_type: toColumns.find((c) => c.column_name === m.dbColumn)?.data_type || "text",
            mapping_type: m.sourceType === "fixed" ? "fixed" : "direct",
            mapping_order: index + 1,
          })) as BatchMapping[];
      }
      
      await BatchAPI.updateBatchConfig(batchId, {
        batchName,
        description,
        cronSchedule,
        isActive,
        mappings: finalMappings,
        saveMode,
        conflictKey: saveMode === "UPSERT" ? conflictKey : null, // INSERT면 null로 명시적 삭제
        authServiceName: authTokenMode === "db" ? authServiceName : null, // 직접입력이면 null로 명시적 삭제
        dataArrayPath: dataArrayPath || null
      });
      
      toast.success("배치 설정이 성공적으로 수정되었습니다.");
      openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });
      
    } catch (error) {
      console.error("배치 설정 수정 실패:", error);
      toast.error("배치 설정 수정에 실패했습니다.");
    } finally {
      setLoading(false);
    }
  };

  const goBack = () => openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });
  const selectedFlow = nodeFlows.find(f => f.flow_id === selectedFlowId);

  if (loading && !batchConfig) {
    return (
      <div className="mx-auto max-w-5xl p-4 sm:p-6">
        <div className="flex h-64 items-center justify-center gap-2">
          <RefreshCw className="h-5 w-5 animate-spin text-muted-foreground" />
          <span className="text-sm text-muted-foreground">배치 설정을 불러오는 중...</span>
        </div>
      </div>
    );
  }

  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>
            <div className="flex items-center gap-2">
              <h1 className="text-xl font-bold tracking-tight">배치 수정</h1>
              {batchType && (
                <Badge variant="outline" className="h-5 text-[10px]">
                  {batchType === "db-to-db" && "DB → DB"}
                  {batchType === "restapi-to-db" && "API → DB"}
                  {batchType === "db-to-restapi" && "DB → API"}
                </Badge>
              )}
            </div>
            <p className="mt-1 text-xs text-muted-foreground">#{batchId} 배치 설정을 수정해요</p>
          </div>
          <Button size="sm" onClick={saveBatchConfig} disabled={loading} 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="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" />
          </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 className="flex items-center justify-between rounded-lg border px-4 py-3">
            <div>
              <p className="text-sm font-medium">배치 켜기</p>
              <p className="text-[11px] text-muted-foreground">{isActive === "Y" ? "스케줄에 따라 자동으로 실행돼요" : "배치가 꺼져 있어요"}</p>
            </div>
            <Switch checked={isActive === "Y"} onCheckedChange={checked => setIsActive(checked ? "Y" : "N")} />
          </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>

          {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>

      {/* 실행 타입 선택 */}
      <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"); if (nodeFlows.length === 0) BatchAPI.getNodeFlows().then(setNodeFlows); }}
            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>

      {/* 노드 플로우 설정 */}
      {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>
      )}

      {/* FROM/TO 섹션 가로 배치 (매핑 타입일 때만) */}
      {executionType === "mapping" && (
      <>
      <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
        {/* FROM 설정 */}
        <div className="space-y-3 rounded-lg 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-medium">FROM (소스)</span>
          </div>
            {batchType === "db-to-db" && (
              <>
                <div>
                  <Label>연결</Label>
                  <Select
                    value={
                      fromConnection?.type === "internal"
                        ? "internal"
                        : fromConnection?.id?.toString() || ""
                    }
                    onValueChange={handleFromConnectionChange}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="소스 연결을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="internal">내부 DB</SelectItem>
                      {connections
                        .filter((conn) => conn.id)
                        .map((conn) => (
                          <SelectItem key={conn.id} value={conn.id!.toString()}>
                            {conn.name}
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                </div>

                <div>
                  <Label>테이블</Label>
                  <Select value={fromTable} onValueChange={handleFromTableChange}>
                    <SelectTrigger>
                      <SelectValue placeholder="소스 테이블을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      {fromTables.map((table) => (
                        <SelectItem key={table} value={table}>
                          {table}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}

            {batchType === "restapi-to-db" && mappings.length > 0 && (
              <>
                {/* API 서버 URL */}
                <div>
                  <Label>API 서버 URL *</Label>
                  <Input
                    value={mappings[0]?.from_api_url || ""}
                    onChange={(e) => {
                      const value = e.target.value;
                      setMappings((prev) => {
                        if (prev.length === 0) return prev;
                        const updated = [...prev];
                        updated[0] = { ...updated[0], from_api_url: value } as any;
                        return updated;
                      });
                    }}
                    placeholder="https://api.example.com"
                  />
                </div>

                {/* 인증 토큰 (Authorization) */}
                <div>
                  <Label>인증 토큰 (Authorization)</Label>
                  <div className="mt-2 flex items-center gap-4">
                    <label className="flex cursor-pointer items-center gap-1.5">
                      <input
                        type="radio"
                        name="authTokenMode"
                        value="direct"
                        checked={authTokenMode === "direct"}
                        onChange={() => setAuthTokenMode("direct")}
                        className="h-3.5 w-3.5"
                      />
                      <span className="text-xs">직접 입력</span>
                    </label>
                    <label className="flex cursor-pointer items-center gap-1.5">
                      <input
                        type="radio"
                        name="authTokenMode"
                        value="db"
                        checked={authTokenMode === "db"}
                        onChange={() => setAuthTokenMode("db")}
                        className="h-3.5 w-3.5"
                      />
                      <span className="text-xs">DB에서 선택</span>
                    </label>
                  </div>
                  {/* 직접 입력 모드 */}
                  {authTokenMode === "direct" && (
                    <Input
                      value={fromApiKey}
                      onChange={(e) => setFromApiKey(e.target.value)}
                      placeholder="Bearer eyJhbGciOiJIUzI1NiIs..."
                      className="mt-2"
                    />
                  )}
                  {/* DB 선택 모드 */}
                  {authTokenMode === "db" && (
                    <Select
                      value={authServiceName || "none"}
                      onValueChange={(value) => setAuthServiceName(value === "none" ? "" : value)}
                    >
                      <SelectTrigger className="mt-2">
                        <SelectValue placeholder="서비스명 선택" />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="none">선택 안 함</SelectItem>
                        {authServiceNames.map((name) => (
                          <SelectItem key={name} value={name}>
                            {name}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  )}
                  <p className="mt-1 text-xs text-muted-foreground">
                    {authTokenMode === "direct"
                      ? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요."
                      : "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."}
                  </p>
                </div>

                {/* 엔드포인트 */}
                <div>
                  <Label>엔드포인트 *</Label>
                  <Input
                    value={mappings[0]?.from_table_name || ""}
                    onChange={(e) => {
                      const value = e.target.value;
                      setMappings((prev) => {
                        if (prev.length === 0) return prev;
                        const updated = [...prev];
                        updated[0] = { ...updated[0], from_table_name: value } as any;
                        return updated;
                      });
                    }}
                    placeholder="/api/users"
                  />
                </div>

                {/* HTTP 메서드 */}
                <div>
                  <Label>HTTP 메서드</Label>
                  <Select
                    value={mappings[0]?.from_api_method || "GET"}
                    onValueChange={(value) => {
                      setMappings((prev) => {
                        if (prev.length === 0) return prev;
                        const updated = [...prev];
                        updated[0] = { ...updated[0], from_api_method: value } as any;
                        return updated;
                      });
                    }}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="HTTP 메서드 선택" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="GET">GET (데이터 조회)</SelectItem>
                      <SelectItem value="POST">POST (데이터 조회/전송)</SelectItem>
                      <SelectItem value="PUT">PUT</SelectItem>
                      <SelectItem value="DELETE">DELETE</SelectItem>
                    </SelectContent>
                  </Select>
                </div>

                {/* 데이터 배열 경로 */}
                <div>
                  <Label>데이터 배열 경로</Label>
                  <Input
                    value={dataArrayPath}
                    onChange={(e) => setDataArrayPath(e.target.value)}
                    placeholder="response (예: data.items, results)"
                  />
                  <p className="mt-1 text-xs text-muted-foreground">
                    API 응답에서 배열 데이터가 있는 경로를 입력하세요. 비워두면 응답 전체를 사용합니다.
                    <br />
                    예시: response, data.items, result.list
                  </p>
                </div>

                {/* Request Body (POST/PUT/DELETE용) */}
                {(mappings[0]?.from_api_method === "POST" || 
                  mappings[0]?.from_api_method === "PUT" || 
                  mappings[0]?.from_api_method === "DELETE") && (
                  <div>
                    <Label>Request Body (JSON)</Label>
                    <Textarea
                      value={mappings[0]?.from_api_body || ""}
                      onChange={(e) => {
                        const value = e.target.value;
                        setMappings((prev) => {
                          if (prev.length === 0) return prev;
                          const updated = [...prev];
                          updated[0] = { ...updated[0], from_api_body: value } as any;
                          return updated;
                        });
                      }}
                      placeholder='{"username": "myuser", "token": "abc"}'
                      className="min-h-[100px]"
                      rows={5}
                    />
                    <p className="mt-1 text-xs text-muted-foreground">API 호출 시 함께 전송할 JSON 데이터를 입력하세요.</p>
                  </div>
                )}

                {/* API 파라미터 설정 */}
                <div className="space-y-4">
                  <div className="border-t pt-4">
                    <Label className="text-base font-medium">API 파라미터 설정</Label>
                    <p className="mt-1 text-sm text-muted-foreground">특정 사용자나 조건으로 데이터를 조회할 때 사용합니다.</p>
                  </div>

                  <div>
                    <Label>파라미터 타입</Label>
                    <Select value={apiParamType} onValueChange={(value: any) => setApiParamType(value)}>
                      <SelectTrigger>
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="none">파라미터 없음</SelectItem>
                        <SelectItem value="url">URL 파라미터 (/api/users/{"{userId}"})</SelectItem>
                        <SelectItem value="query">쿼리 파라미터 (/api/users?userId=123)</SelectItem>
                      </SelectContent>
                    </Select>
                  </div>

                  {apiParamType !== "none" && (
                    <>
                      {/* 파라미터명 */}
                      <div>
                        <Label>파라미터명 *</Label>
                        <Input
                          value={apiParamName}
                          onChange={(e) => setApiParamName(e.target.value)}
                          placeholder="userId, id, email 등"
                        />
                      </div>

                      {/* 파라미터 소스 */}
                      <div>
                        <Label>파라미터 소스</Label>
                        <Select value={apiParamSource} onValueChange={(value: any) => setApiParamSource(value)}>
                          <SelectTrigger>
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value="static">고정값</SelectItem>
                            <SelectItem value="dynamic">동적값 (실행 시 결정)</SelectItem>
                          </SelectContent>
                        </Select>
                      </div>

                      <div>
                        <Label>
                          {apiParamSource === "static" ? "파라미터 값" : "파라미터 템플릿"} *
                        </Label>
                        <Input
                          value={apiParamValue}
                          onChange={(e) => setApiParamValue(e.target.value)}
                          placeholder={
                            apiParamSource === "static"
                              ? "123, john@example.com 등"
                              : "{{user_id}}, {{email}} 등 (실행 시 치환됨)"
                          }
                        />
                        {apiParamSource === "dynamic" && (
                          <p className="mt-1 text-xs text-muted-foreground">
                            동적값은 배치 실행 시 설정된 값으로 치환됩니다. 예: {"{{user_id}}"} → 실제 사용자 ID
                          </p>
                        )}
                      </div>

                      {apiParamType === "url" && (
                        <div className="rounded-lg bg-primary/10 p-3">
                          <div className="text-sm font-medium text-primary">URL 파라미터 예시</div>
                          <div className="mt-1 text-sm text-primary">
                            엔드포인트: /api/users/{`{${apiParamName || "userId"}}`}
                          </div>
                          <div className="text-sm text-primary">실제 호출: /api/users/{apiParamValue || "123"}</div>
                        </div>
                      )}

                      {apiParamType === "query" && (
                        <div className="rounded-lg bg-emerald-50 p-3">
                          <div className="text-sm font-medium text-emerald-800">쿼리 파라미터 예시</div>
                          <div className="mt-1 text-sm text-emerald-700">
                            실제 호출: {mappings[0]?.from_table_name || "/api/users"}?{apiParamName || "userId"}=
                            {apiParamValue || "123"}
                          </div>
                        </div>
                      )}
                    </>
                  )}
                </div>
              </>
            )}

            {batchType === "db-to-restapi" && mappings.length > 0 && (
              <>
                <div className="space-y-1.5">
                  <Label className="text-xs">소스 테이블</Label>
                  <Input value={mappings[0]?.from_table_name || ""} readOnly className="h-9 text-sm" />
                </div>
              </>
            )}
        </div>

        {/* TO 설정 */}
        <div className="space-y-3 rounded-lg 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-medium">TO (대상)</span>
          </div>
            {batchType === "db-to-db" && (
              <>
                <div>
                  <Label>연결</Label>
                  <Select
                    value={
                      toConnection?.type === "internal"
                        ? "internal"
                        : toConnection?.id?.toString() || ""
                    }
                    onValueChange={handleToConnectionChange}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="대상 연결을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="internal">내부 DB</SelectItem>
                      {connections
                        .filter((conn) => conn.id)
                        .map((conn) => (
                          <SelectItem key={conn.id} value={conn.id!.toString()}>
                            {conn.name}
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                </div>

                <div>
                  <Label>테이블</Label>
                  <Select
                    value={toTable}
                    onValueChange={handleToTableChange}
                    disabled={!toConnection}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="대상 테이블을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      {toTables.map((table) => (
                        <SelectItem key={table} value={table}>
                          {table}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}

            {batchType === "restapi-to-db" && (
              <>
                <div>
                  <Label>데이터베이스 연결 *</Label>
                  <Select
                    value={
                      toConnection?.type === "internal"
                        ? "internal"
                        : toConnection?.id?.toString() || ""
                    }
                    onValueChange={handleToConnectionChange}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="대상 연결을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="internal">내부 DB</SelectItem>
                      {connections
                        .filter((conn) => conn.id)
                        .map((conn) => (
                          <SelectItem key={conn.id} value={conn.id!.toString()}>
                            {conn.name}
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                </div>

                <div>
                  <Label>대상 테이블 *</Label>
                  <Select
                    value={toTable}
                    onValueChange={handleToTableChange}
                    disabled={!toConnection}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder={toConnection ? "테이블을 선택하세요" : "먼저 연결을 선택하세요"} />
                    </SelectTrigger>
                    <SelectContent>
                      {toTables.map((table) => (
                        <SelectItem key={table} value={table}>
                          {table}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>
              </>
            )}

            {batchType === "db-to-restapi" && mappings.length > 0 && (
              <>
                <div>
                  <Label>API URL</Label>
                  <Input value={mappings[0]?.to_api_url || ""} readOnly />
                </div>
                <div>
                  <Label>API 엔드포인트</Label>
                  <Input value={mappings[0]?.to_table_name || ""} readOnly />
                </div>
                <div>
                  <Label>HTTP 메서드</Label>
                  <Input value={mappings[0]?.to_api_method || "POST"} readOnly />
                </div>
              </>
            )}

            {/* 저장 모드 설정 - 항상 표시 (단계적 활성화) */}
            <div className="border-t pt-4">
              <Label>저장 모드</Label>
              <Select
                value={saveMode}
                onValueChange={(value: "INSERT" | "UPSERT") => {
                  setSaveMode(value);
                  // INSERT로 변경 시 충돌 기준 컬럼 초기화
                  if (value === "INSERT") {
                    setConflictKey("");
                  }
                }}
                disabled={!toTable}
              >
                <SelectTrigger>
                  <SelectValue placeholder="저장 모드 선택" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="INSERT">INSERT (항상 새로 추가)</SelectItem>
                  <SelectItem value="UPSERT">
                    UPSERT (있으면 업데이트, 없으면 추가)
                  </SelectItem>
                </SelectContent>
              </Select>
              <p className="text-muted-foreground mt-1.5 text-xs">
                UPSERT: 동일한 키가 있으면 업데이트, 없으면 새로 추가합니다.
              </p>
            </div>

            {/* 충돌 기준 컬럼 - UPSERT일 때만 활성화 */}
            <div>
              <Label>충돌 기준 컬럼</Label>
              <Select
                value={conflictKey || "none"}
                onValueChange={(value) => setConflictKey(value === "none" ? "" : value)}
                disabled={saveMode !== "UPSERT" || toColumns.length === 0}
              >
                <SelectTrigger>
                  <SelectValue placeholder={saveMode !== "UPSERT" ? "UPSERT 모드를 선택하세요" : "컬럼을 선택하세요"} />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="none">선택 안함</SelectItem>
                  {toColumns.map((col) => (
                    <SelectItem key={col.column_name} value={col.column_name}>
                      <div className="flex items-center gap-2">
                        <span>{col.column_name}</span>
                        <span className="text-muted-foreground text-xs">({col.data_type})</span>
                      </div>
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <p className="text-muted-foreground mt-1.5 text-xs">
                UPSERT 시 중복 여부를 판단할 컬럼을 선택하세요.
              </p>
            </div>
        </div>
      </div>

      {/* API 데이터 미리보기 버튼 */}
      {batchType === "restapi-to-db" && (
        <div className="flex justify-center">
          <Button
            variant="outline"
            onClick={previewRestApiData}
            disabled={mappings.length === 0}
          >
            <RefreshCw className="mr-2 h-4 w-4" />
            API 데이터 미리보기
          </Button>
        </div>
      )}

      {/* 컬럼 매핑 섹션 */}
      <div className="space-y-3 rounded-lg border p-4 sm:p-5">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2 text-sm font-medium">
            <Link className="h-4 w-4 text-muted-foreground" />
            {batchType === "db-to-db" && "컬럼 매핑"}
            {batchType === "restapi-to-db" && "컬럼 매핑 설정"}
            {batchType === "db-to-restapi" && "DB → API 필드 매핑"}
          </div>
          {batchType === "restapi-to-db" && (
            <p className="text-xs text-muted-foreground">DB 컬럼에 API 필드 또는 고정값을 매핑</p>
          )}
        </div>
          <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
            {/* 왼쪽: 샘플 데이터 */}
            <div className="flex flex-col">
              <div className="mb-3 flex h-8 items-center">
                <h4 className="text-sm font-semibold">샘플 데이터 (최대 3개)</h4>
              </div>
              {apiPreviewData.length > 0 ? (
                <div className="bg-muted/30 h-[360px] overflow-y-auto rounded-lg border p-3">
                  <div className="space-y-2">
                    {apiPreviewData.slice(0, 3).map((item, index) => (
                      <div key={index} className="bg-background rounded border p-2">
                        <pre className="whitespace-pre-wrap font-mono text-xs">
                          {JSON.stringify(item, null, 2)}
                        </pre>
                      </div>
                    ))}
                  </div>
                </div>
              ) : (
                <div className="flex h-[360px] items-center justify-center rounded-lg border border-dashed">
                  <p className="text-muted-foreground text-sm">
                    API 데이터 미리보기를 실행하면 샘플 데이터가 표시됩니다.
                  </p>
                </div>
              )}
            </div>

            {/* 오른쪽: 매핑 설정 */}
            <div className="flex flex-col">
              <div className="mb-3 flex h-8 items-center justify-between">
                <h4 className="text-sm font-semibold">매핑 설정</h4>
                {(batchType === "db-to-db" || batchType === "restapi-to-db") && (
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={batchType === "db-to-db" ? addMapping : addMappingListItem}
                    className="h-8 gap-1"
                  >
                    <Plus className="h-4 w-4" />
                    매핑 추가
                  </Button>
                )}
              </div>

              {/* restapi-to-db 새로운 매핑 UI */}
              {batchType === "restapi-to-db" && (
                <>
                  {mappingList.length === 0 ? (
                    <div className="flex h-[360px] flex-col items-center justify-center rounded-lg border border-dashed text-center">
                      <p className="text-muted-foreground text-sm">매핑이 없습니다.</p>
                      <Button variant="link" onClick={addMappingListItem} className="mt-2">
                        매핑 추가하기
                      </Button>
                    </div>
                  ) : (
                    <div className="bg-muted/30 h-[360px] space-y-3 overflow-y-auto rounded-lg border p-3">
                      {mappingList.map((mapping, index) => (
                        <div key={mapping.id} className="bg-background flex items-center gap-2 rounded-lg border p-3">
                          {/* 순서 표시 */}
                          <div className="bg-primary/10 text-primary flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-medium">
                            {index + 1}
                          </div>

                          {/* DB 컬럼 선택 (좌측 - TO) */}
                          <div className="w-36 shrink-0">
                            <Select
                              value={mapping.dbColumn || "none"}
                              onValueChange={(value) =>
                                updateMappingListItem(mapping.id, { dbColumn: value === "none" ? "" : value })
                              }
                            >
                              <SelectTrigger className="h-9">
                                <SelectValue placeholder="선택 안함" />
                              </SelectTrigger>
                              <SelectContent>
                                <SelectItem value="none">선택 안함</SelectItem>
                                {toColumns.map((col) => {
                                  const isUsed =
                                    mappingList.some((m) => m.dbColumn === col.column_name) &&
                                    mapping.dbColumn !== col.column_name;
                                  return (
                                    <SelectItem key={col.column_name} value={col.column_name} disabled={isUsed}>
                                      <div className="flex items-center gap-2">
                                        <span className={isUsed ? "text-muted-foreground" : ""}>{col.column_name}</span>
                                        <span className="text-muted-foreground text-xs">({col.data_type})</span>
                                      </div>
                                    </SelectItem>
                                  );
                                })}
                              </SelectContent>
                            </Select>
                          </div>

                          {/* 화살표 */}
                          <ArrowLeft className="text-muted-foreground h-4 w-4 shrink-0" />

                          {/* 소스 타입 선택 */}
                          <div className="w-24 shrink-0">
                            <Select
                              value={mapping.sourceType}
                              onValueChange={(value: "api" | "fixed") =>
                                updateMappingListItem(mapping.id, {
                                  sourceType: value,
                                  apiField: value === "fixed" ? "" : mapping.apiField,
                                  fixedValue: value === "api" ? "" : mapping.fixedValue,
                                })
                              }
                            >
                              <SelectTrigger className="h-9">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                <SelectItem value="api">API 필드</SelectItem>
                                <SelectItem value="fixed">고정값</SelectItem>
                              </SelectContent>
                            </Select>
                          </div>

                          {/* API 필드 선택 또는 고정값 입력 (우측 - FROM) */}
                          <div className="min-w-0 flex-1">
                            {mapping.sourceType === "api" ? (
                              <Select
                                value={mapping.apiField || "none"}
                                onValueChange={(value) =>
                                  updateMappingListItem(mapping.id, { apiField: value === "none" ? "" : value })
                                }
                              >
                                <SelectTrigger className="h-9">
                                  <SelectValue placeholder="선택 안함" />
                                </SelectTrigger>
                                <SelectContent>
                                  <SelectItem value="none">선택 안함</SelectItem>
                                  {fromApiFields.map((field) => (
                                    <SelectItem key={field} value={field}>
                                      <div className="flex items-center gap-2">
                                        <span>{field}</span>
                                        {apiPreviewData[0] && apiPreviewData[0][field] !== undefined && (
                                          <span className="text-muted-foreground text-xs">
                                            (예: {String(apiPreviewData[0][field]).substring(0, 15)}
                                            {String(apiPreviewData[0][field]).length > 15 ? "..." : ""})
                                          </span>
                                        )}
                                      </div>
                                    </SelectItem>
                                  ))}
                                </SelectContent>
                              </Select>
                            ) : (
                              <Input
                                value={mapping.fixedValue}
                                onChange={(e) => updateMappingListItem(mapping.id, { fixedValue: e.target.value })}
                                placeholder="고정값 입력"
                                className="h-9"
                              />
                            )}
                          </div>

                          {/* 삭제 버튼 */}
                          <Button
                            variant="ghost"
                            size="icon"
                            onClick={() => removeMappingListItem(mapping.id)}
                            className="text-muted-foreground hover:text-destructive h-8 w-8 shrink-0"
                          >
                            <Trash2 className="h-4 w-4" />
                          </Button>
                        </div>
                      ))}
                    </div>
                  )}
                </>
              )}

              {/* db-to-db 기존 매핑 UI */}
              {batchType === "db-to-db" && (
                <>
                  {mappings.length === 0 ? (
                    <div className="flex h-[360px] flex-col items-center justify-center rounded-lg border border-dashed text-center">
                      <p className="text-muted-foreground text-sm">매핑이 없습니다.</p>
                      <Button variant="link" onClick={addMapping} className="mt-2">
                        매핑 추가하기
                      </Button>
                    </div>
                  ) : (
                    <div className="bg-muted/30 h-[360px] space-y-3 overflow-y-auto rounded-lg border p-3">
                      {mappings.map((mapping, index) => (
                        <div
                          key={index}
                          className="bg-background flex items-center gap-2 rounded-lg border p-3"
                        >
                          <div className="flex-1">
                            <Select
                              value={mapping.from_column_name || ""}
                              onValueChange={(value) => {
                                updateMapping(index, "from_column_name", value);
                                const selectedColumn = fromColumns.find(
                                  (col) => col.column_name === value
                                );
                                if (selectedColumn) {
                                  updateMapping(
                                    index,
                                    "from_column_type",
                                    selectedColumn.data_type
                                  );
                                }
                              }}
                            >
                              <SelectTrigger className="h-9 text-xs">
                                <SelectValue placeholder="FROM 컬럼" />
                              </SelectTrigger>
                              <SelectContent>
                                {fromColumns.map((column) => (
                                  <SelectItem
                                    key={column.column_name}
                                    value={column.column_name}
                                  >
                                    {column.column_name}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <span className="text-muted-foreground text-xs">-&gt;</span>
                          <div className="flex-1">
                            <Select
                              value={mapping.to_column_name || ""}
                              onValueChange={(value) => {
                                updateMapping(index, "to_column_name", value);
                                const selectedColumn = toColumns.find(
                                  (col) => col.column_name === value
                                );
                                if (selectedColumn) {
                                  updateMapping(
                                    index,
                                    "to_column_type",
                                    selectedColumn.data_type
                                  );
                                }
                              }}
                            >
                              <SelectTrigger className="h-9 text-xs">
                                <SelectValue placeholder="TO 컬럼" />
                              </SelectTrigger>
                              <SelectContent>
                                {toColumns.map((column) => (
                                  <SelectItem
                                    key={column.column_name}
                                    value={column.column_name}
                                  >
                                    {column.column_name}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <Button
                            variant="ghost"
                            size="icon"
                            className="h-8 w-8"
                            onClick={() => removeMapping(index)}
                          >
                            <Trash2 className="h-4 w-4" />
                          </Button>
                        </div>
                      ))}
                    </div>
                  )}
                </>
              )}

              {/* db-to-restapi 기존 매핑 UI */}
              {batchType === "db-to-restapi" && (
                <>
                  {mappings.length === 0 ? (
                    <div className="flex h-[360px] flex-col items-center justify-center rounded-lg border border-dashed text-center">
                      <p className="text-muted-foreground text-sm">매핑이 없습니다.</p>
                    </div>
                  ) : (
                    <div className="bg-muted/30 h-[360px] space-y-3 overflow-y-auto rounded-lg border p-3">
                      {mappings.map((mapping, index) => (
                        <div
                          key={index}
                          className="bg-background flex items-center gap-2 rounded-lg border p-3"
                        >
                          <div className="flex-1">
                            <Input
                              value={mapping.from_column_name || ""}
                              readOnly
                              className="h-9 text-xs"
                            />
                          </div>
                          <span className="text-muted-foreground text-xs">-&gt;</span>
                          <div className="flex-1">
                            <Input
                              value={mapping.to_column_name || ""}
                              readOnly
                              className="h-9 text-xs"
                            />
                          </div>
                        </div>
                      ))}
                    </div>
                  )}
                </>
              )}
            </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 === "node_flow" ? !selectedFlowId : (batchType === "restapi-to-db" ? mappingList.length === 0 : mappings.length === 0))}
          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>
  );
}
