"use client";

import React, { useState, useEffect, useMemo, memo } from "react";
import { useRouter } from "next/navigation";
import { useTabStore } from "@/stores/tabStore";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
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 { Trash2, Plus, ArrowLeft, Save, RefreshCw, Globe, Database, Eye } from "lucide-react";
import { toast } from "sonner";
import { showErrorToast } from "@/lib/utils/toastUtils";
import { BatchManagementAPI } from "@/lib/api/batchManagement";

// 타입 정의
type BatchType = "db-to-restapi" | "restapi-to-db" | "restapi-to-restapi";

interface BatchTypeOption {
  value: BatchType;
  label: string;
  description: string;
}

interface BatchConnectionInfo {
  id: number;
  name: string;
  type: string;
}

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

// 통합 매핑 아이템 타입
interface MappingItem {
  id: string;
  dbColumn: string;
  sourceType: "api" | "fixed";
  apiField: string;
  fixedValue: string;
}

interface RestApiToDbMappingCardProps {
  fromApiFields: string[];
  toColumns: BatchColumnInfo[];
  fromApiData: any[];
  mappingList: MappingItem[];
  setMappingList: React.Dispatch<React.SetStateAction<MappingItem[]>>;
}

interface DbToRestApiMappingCardProps {
  fromColumns: BatchColumnInfo[];
  selectedColumns: string[];
  toApiFields: string[];
  dbToApiFieldMapping: Record<string, string>;
  setDbToApiFieldMapping: React.Dispatch<React.SetStateAction<Record<string, string>>>;
  setToApiBody: (body: string) => void;
}

export default function BatchManagementNewPage() {
  const router = useRouter();
  const { openTab } = useTabStore();

  // 기본 상태
  const [batchName, setBatchName] = useState("");
  const [cronSchedule, setCronSchedule] = useState("0 12 * * *");
  const [description, setDescription] = useState("");

  // 인증 토큰 설정
  const [authTokenMode, setAuthTokenMode] = useState<"direct" | "db">("direct"); // 직접입력 / DB에서 선택
  const [authServiceName, setAuthServiceName] = useState("");
  const [authServiceNames, setAuthServiceNames] = useState<string[]>([]);

  // 연결 정보
  const [connections, setConnections] = useState<BatchConnectionInfo[]>([]);
  const [toConnection, setToConnection] = useState<BatchConnectionInfo | null>(null);
  const [toTables, setToTables] = useState<string[]>([]);
  const [toTable, setToTable] = useState("");
  const [toColumns, setToColumns] = useState<BatchColumnInfo[]>([]);

  // REST API 설정 (REST API → DB용)
  const [fromApiUrl, setFromApiUrl] = useState("");
  const [fromApiKey, setFromApiKey] = useState("");
  const [fromEndpoint, setFromEndpoint] = useState("");
  const [fromApiMethod, setFromApiMethod] = useState<"GET" | "POST" | "PUT" | "DELETE">("GET");
  const [fromApiBody, setFromApiBody] = useState(""); // Request Body (JSON)
  const [dataArrayPath, setDataArrayPath] = useState(""); // 데이터 배열 경로 (예: response, data.items)

  // REST API 파라미터 설정
  const [apiParamType, setApiParamType] = useState<"none" | "url" | "query">("none");
  const [apiParamName, setApiParamName] = useState(""); // 파라미터명 (예: userId, id)
  const [apiParamValue, setApiParamValue] = useState(""); // 파라미터 값 또는 템플릿
  const [apiParamSource, setApiParamSource] = useState<"static" | "dynamic">("static"); // 정적 값 또는 동적 값

  // DB → REST API용 상태
  const [fromConnection, setFromConnection] = useState<BatchConnectionInfo | null>(null);
  const [fromTables, setFromTables] = useState<string[]>([]);
  const [fromTable, setFromTable] = useState("");
  const [fromColumns, setFromColumns] = useState<BatchColumnInfo[]>([]);
  const [selectedColumns, setSelectedColumns] = useState<string[]>([]); // 선택된 컬럼들
  const [dbToApiFieldMapping, setDbToApiFieldMapping] = useState<Record<string, string>>({}); // DB 컬럼 → API 필드 매핑

  // REST API 대상 설정 (DB → REST API용)
  const [toApiUrl, setToApiUrl] = useState("");
  const [toApiKey, setToApiKey] = useState("");
  const [toEndpoint, setToEndpoint] = useState("");
  const [toApiMethod, setToApiMethod] = useState<"POST" | "PUT" | "DELETE">("POST");
  const [toApiBody, setToApiBody] = useState<string>(""); // Request Body 템플릿
  const [toApiFields, setToApiFields] = useState<string[]>([]); // TO API 필드 목록
  const [urlPathColumn, setUrlPathColumn] = useState(""); // URL 경로에 사용할 컬럼 (PUT/DELETE용)

  // API 데이터 미리보기
  const [fromApiData, setFromApiData] = useState<any[]>([]);
  const [fromApiFields, setFromApiFields] = useState<string[]>([]);

  // 통합 매핑 리스트
  const [mappingList, setMappingList] = useState<MappingItem[]>([]);

  // INSERT/UPSERT 설정
  const [saveMode, setSaveMode] = useState<"INSERT" | "UPSERT">("INSERT");
  const [conflictKey, setConflictKey] = useState("");

  // 배치 타입 상태
  const [batchType, setBatchType] = useState<BatchType>("restapi-to-db");

  // 배치 타입 옵션
  const batchTypeOptions: BatchTypeOption[] = [
    {
      value: "restapi-to-db",
      label: "REST API → DB",
      description: "REST API에서 데이터베이스로 데이터 수집",
    },
    {
      value: "db-to-restapi",
      label: "DB → REST API",
      description: "데이터베이스에서 REST API로 데이터 전송",
    },
  ];

  // 초기 데이터 로드
  useEffect(() => {
    loadConnections();
    loadAuthServiceNames();
  }, []);

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

  // 배치 타입 변경 시 상태 초기화
  useEffect(() => {
    // 공통 초기화
    setMappingList([]);

    // REST API → DB 관련 초기화
    setToConnection(null);
    setToTables([]);
    setToTable("");
    setToColumns([]);
    setFromApiUrl("");
    setFromApiKey("");
    setFromEndpoint("");
    setFromApiData([]);
    setFromApiFields([]);

    // DB → REST API 관련 초기화
    setFromConnection(null);
    setFromTables([]);
    setFromTable("");
    setFromColumns([]);
    setSelectedColumns([]);
    setDbToApiFieldMapping({});
    setToApiUrl("");
    setToApiKey("");
    setToEndpoint("");
    setToApiBody("");
    setToApiFields([]);
  }, [batchType]);

  // 연결 목록 로드
  const loadConnections = async () => {
    try {
      const result = await BatchManagementAPI.getAvailableConnections();
      setConnections(result || []);
    } catch (error) {
      console.error("연결 목록 로드 오류:", error);
      toast.error("연결 목록을 불러오는데 실패했습니다.");
    }
  };

  // TO 연결 변경 핸들러
  const handleToConnectionChange = async (connectionValue: string) => {
    let connection: BatchConnectionInfo | null = null;

    if (connectionValue === "internal") {
      // 내부 데이터베이스 선택
      connection = connections.find((conn) => conn.type === "internal") || null;
    } else {
      // 외부 데이터베이스 선택
      const connectionId = parseInt(connectionValue);
      connection = connections.find((conn) => conn.id === connectionId) || null;
    }

    setToConnection(connection);
    setToTable("");
    setToColumns([]);

    if (connection) {
      try {
        const connectionType = connection.type === "internal" ? "internal" : "external";
        const result = await BatchManagementAPI.getTablesFromConnection(connectionType, connection.id);
        const tableNames = Array.isArray(result)
          ? result.map((table: any) => (typeof table === "string" ? table : table.table_name || String(table)))
          : [];
        setToTables(tableNames);
      } catch (error) {
        console.error("테이블 목록 로드 오류:", error);
        toast.error("테이블 목록을 불러오는데 실패했습니다.");
      }
    }
  };

  // TO 테이블 변경 핸들러
  const handleToTableChange = async (tableName: string) => {
    setToTable(tableName);
    setToColumns([]);

    if (toConnection && tableName) {
      try {
        const connectionType = toConnection.type === "internal" ? "internal" : "external";
        const result = await BatchManagementAPI.getTableColumns(connectionType, tableName, toConnection.id);
        if (result && result.length > 0) {
          setToColumns(result);
        } else {
          setToColumns([]);
        }
      } catch (error) {
        console.error("❌ 컬럼 목록 로드 오류:", error);
        toast.error("컬럼 목록을 불러오는데 실패했습니다.");
        setToColumns([]);
      }
    }
  };

  // FROM 연결 변경 핸들러 (DB → REST API용)
  const handleFromConnectionChange = async (connectionValue: string) => {
    let connection: BatchConnectionInfo | null = null;
    if (connectionValue === "internal") {
      connection = connections.find((conn) => conn.type === "internal") || null;
    } else {
      const connectionId = parseInt(connectionValue);
      connection = connections.find((conn) => conn.id === connectionId) || null;
    }
    setFromConnection(connection);
    setFromTable("");
    setFromColumns([]);

    if (connection) {
      try {
        const connectionType = connection.type === "internal" ? "internal" : "external";
        const result = await BatchManagementAPI.getTablesFromConnection(connectionType, connection.id);
        const tableNames = Array.isArray(result)
          ? result.map((table: any) => (typeof table === "string" ? table : table.table_name || String(table)))
          : [];
        setFromTables(tableNames);
      } catch (error) {
        console.error("테이블 목록 로드 오류:", error);
        toast.error("테이블 목록을 불러오는데 실패했습니다.");
      }
    }
  };

  // FROM 테이블 변경 핸들러 (DB → REST API용)
  const handleFromTableChange = async (tableName: string) => {
    setFromTable(tableName);
    setFromColumns([]);
    setSelectedColumns([]); // 선택된 컬럼도 초기화
    setDbToApiFieldMapping({}); // 매핑도 초기화

    if (fromConnection && tableName) {
      try {
        const connectionType = fromConnection.type === "internal" ? "internal" : "external";
        const result = await BatchManagementAPI.getTableColumns(connectionType, tableName, fromConnection.id);
        if (result && result.length > 0) {
          setFromColumns(result);
        } else {
          setFromColumns([]);
        }
      } catch (error) {
        console.error("❌ FROM 컬럼 목록 로드 오류:", error);
        toast.error("컬럼 목록을 불러오는데 실패했습니다.");
        setFromColumns([]);
      }
    }
  };

  // TO API 미리보기 (DB → REST API용)
  const previewToApiData = async () => {
    if (!toApiUrl || !toApiKey || !toEndpoint) {
      toast.error("API URL, API Key, 엔드포인트를 모두 입력해주세요.");
      return;
    }

    try {
      const result = await BatchManagementAPI.previewRestApiData(
        toApiUrl,
        toApiKey,
        toEndpoint,
        "GET", // 미리보기는 항상 GET으로
      );

      if (result.fields && result.fields.length > 0) {
        setToApiFields(result.fields);
        toast.success(`TO API 필드 ${result.fields.length}개를 조회했습니다.`);
      } else {
        setToApiFields([]);
        toast.warning("TO API에서 필드를 찾을 수 없습니다.");
      }
    } catch (error) {
      console.error("❌ TO API 미리보기 오류:", error);
      toast.error("TO API 미리보기에 실패했습니다.");
      setToApiFields([]);
    }
  };

  // REST API 데이터 미리보기
  const previewRestApiData = async () => {
    // API URL, 엔드포인트는 항상 필수
    if (!fromApiUrl || !fromEndpoint) {
      toast.error("API URL과 엔드포인트를 모두 입력해주세요.");
      return;
    }

    // 직접 입력 모드일 때만 토큰 검증
    if (authTokenMode === "direct" && !fromApiKey) {
      toast.error("인증 토큰을 입력해주세요.");
      return;
    }

    // DB 선택 모드일 때 서비스명 검증
    if (authTokenMode === "db" && !authServiceName) {
      toast.error("인증 토큰 서비스를 선택해주세요.");
      return;
    }

    try {
      const result = await BatchManagementAPI.previewRestApiData(
        fromApiUrl,
        authTokenMode === "direct" ? fromApiKey : "", // 직접 입력일 때만 API 키 전달
        fromEndpoint,
        fromApiMethod,
        // 파라미터 정보 추가
        apiParamType !== "none"
          ? {
              paramType: apiParamType,
              paramName: apiParamName,
              paramValue: apiParamValue,
              paramSource: apiParamSource,
            }
          : undefined,
        // Request Body 추가 (POST/PUT/DELETE)
        fromApiMethod === "POST" || fromApiMethod === "PUT" || fromApiMethod === "DELETE" ? fromApiBody : undefined,
        // DB 선택 모드일 때 서비스명 전달
        authTokenMode === "db" ? authServiceName : undefined,
        // 데이터 배열 경로 전달
        dataArrayPath || undefined,
      );

      if (result.fields && result.fields.length > 0) {
        setFromApiFields(result.fields);
        setFromApiData(result.samples);
        toast.success(`API 데이터 미리보기 완료! ${result.fields.length}개 필드, ${result.totalCount}개 레코드`);
      } else if (result.samples && result.samples.length > 0) {
        // 백엔드에서 fields를 제대로 보내지 않은 경우, 프론트엔드에서 직접 추출
        const extractedFields = Object.keys(result.samples[0]);
        setFromApiFields(extractedFields);
        setFromApiData(result.samples);

        toast.success(`API 데이터 미리보기 완료! ${extractedFields.length}개 필드, ${result.samples.length}개 레코드`);
      } else {
        setFromApiFields([]);
        setFromApiData([]);
        toast.warning("API에서 데이터를 가져올 수 없습니다.");
      }
    } catch (error) {
      console.error("REST API 미리보기 오류:", error);
      toast.error("API 데이터 미리보기에 실패했습니다.");
      setFromApiFields([]);
      setFromApiData([]);
    }
  };

  // 배치 설정 저장
  const handleSave = async () => {
    if (!batchName.trim()) {
      toast.error("배치명을 입력해주세요.");
      return;
    }

    // 배치 타입별 검증 및 저장
    if (batchType === "restapi-to-db") {
      // 유효한 매핑만 필터링 (DB 컬럼이 선택되고, API 필드 또는 고정값이 있는 것)
      const validMappings = mappingList.filter(
        (m) => m.dbColumn && (m.sourceType === "api" ? m.apiField : m.fixedValue),
      );

      if (validMappings.length === 0) {
        toast.error("최소 하나의 매핑을 설정해주세요.");
        return;
      }

      // UPSERT 모드일 때 conflict key 검증
      if (saveMode === "UPSERT" && !conflictKey) {
        toast.error("UPSERT 모드에서는 충돌 기준 컬럼을 선택해주세요.");
        return;
      }

      // 통합 매핑 리스트를 배치 매핑 형태로 변환
      // 고정값 매핑도 동일한 from_connection_type을 사용해야 같은 그룹으로 처리됨
      const apiMappings = validMappings.map((mapping) => ({
        from_connection_type: "restapi" as const, // 고정값도 동일한 소스 타입 사용
        from_table_name: fromEndpoint,
        from_column_name: mapping.sourceType === "api" ? mapping.apiField : mapping.fixedValue,
        from_api_url: fromApiUrl,
        from_api_key: authTokenMode === "direct" ? fromApiKey : "",
        from_api_method: fromApiMethod,
        from_api_body:
          fromApiMethod === "POST" || fromApiMethod === "PUT" || fromApiMethod === "DELETE" ? fromApiBody : undefined,
        from_api_param_type: apiParamType !== "none" ? apiParamType : undefined,
        from_api_param_name: apiParamType !== "none" ? apiParamName : undefined,
        from_api_param_value: apiParamType !== "none" ? apiParamValue : undefined,
        from_api_param_source: apiParamType !== "none" ? apiParamSource : undefined,
        to_connection_type: toConnection?.type === "internal" ? "internal" : "external",
        to_connection_id: toConnection?.type === "internal" ? undefined : toConnection?.id,
        to_table_name: toTable,
        to_column_name: mapping.dbColumn,
        mapping_type: mapping.sourceType === "fixed" ? ("fixed" as const) : ("direct" as const),
        fixed_value: mapping.sourceType === "fixed" ? mapping.fixedValue : undefined,
      }));

      // 실제 API 호출
      try {
        const result = await BatchManagementAPI.saveRestApiBatch({
          batchName,
          batchType,
          cronSchedule,
          description,
          apiMappings,
          authServiceName: authTokenMode === "db" ? authServiceName : undefined,
          dataArrayPath: dataArrayPath || undefined,
          saveMode,
          conflictKey: saveMode === "UPSERT" ? conflictKey : undefined,
        });

        if (result.success) {
          toast.success(result.message || "REST API 배치 설정이 저장되었습니다.");
          setTimeout(() => {
            openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });
          }, 1000);
        } else {
          toast.error(result.message || "배치 저장에 실패했습니다.");
        }
      } catch (error) {
        console.error("배치 저장 오류:", error);
        showErrorToast("배치 설정 저장에 실패했습니다", error, {
          guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
        });
      }
      return;
    } else if (batchType === "db-to-restapi") {
      // DB → REST API 배치 검증
      if (!fromConnection || !fromTable || selectedColumns.length === 0) {
        toast.error("소스 데이터베이스, 테이블, 컬럼을 선택해주세요.");
        return;
      }

      if (!toApiUrl || !toApiKey || !toEndpoint) {
        toast.error("대상 API URL, API Key, 엔드포인트를 입력해주세요.");
        return;
      }

      if ((toApiMethod === "POST" || toApiMethod === "PUT") && !toApiBody) {
        toast.error("POST/PUT 메서드의 경우 Request Body 템플릿을 입력해주세요.");
        return;
      }

      // DELETE의 경우 빈 Request Body라도 템플릿 로직을 위해 "{}" 설정
      let finalToApiBody = toApiBody;
      if (toApiMethod === "DELETE" && !finalToApiBody.trim()) {
        finalToApiBody = "{}";
      }

      // DB → REST API 매핑 생성 (선택된 컬럼만)
      const selectedColumnObjects = fromColumns.filter((column) => selectedColumns.includes(column.column_name));
      const dbMappings = selectedColumnObjects.map((column, index) => ({
        from_connection_type: fromConnection.type === "internal" ? "internal" : "external",
        from_connection_id: fromConnection.type === "internal" ? undefined : fromConnection.id,
        from_table_name: fromTable,
        from_column_name: column.column_name,
        from_column_type: column.data_type,
        to_connection_type: "restapi" as const,
        to_table_name: toEndpoint, // API 엔드포인트
        to_column_name: dbToApiFieldMapping[column.column_name] || column.column_name, // 매핑된 API 필드명
        to_api_url: toApiUrl,
        to_api_key: toApiKey,
        to_api_method: toApiMethod,
        to_api_body: finalToApiBody, // Request Body 템플릿
        mapping_type: "template" as const,
        mapping_order: index + 1,
      }));

      // URL 경로 파라미터 매핑 추가 (PUT/DELETE용)
      if ((toApiMethod === "PUT" || toApiMethod === "DELETE") && urlPathColumn) {
        const urlPathColumnObject = fromColumns.find((col) => col.column_name === urlPathColumn);
        if (urlPathColumnObject) {
          dbMappings.push({
            from_connection_type: fromConnection.type === "internal" ? "internal" : "external",
            from_connection_id: fromConnection.type === "internal" ? undefined : fromConnection.id,
            from_table_name: fromTable,
            from_column_name: urlPathColumn,
            from_column_type: urlPathColumnObject.data_type,
            to_connection_type: "restapi" as const,
            to_table_name: toEndpoint,
            to_column_name: "URL_PATH_PARAM", // 특별한 식별자
            to_api_url: toApiUrl,
            to_api_key: toApiKey,
            to_api_method: toApiMethod,
            to_api_body: finalToApiBody,
            mapping_type: "url_path" as const,
            mapping_order: 999, // 마지막 순서
          });
        }
      }

      // 실제 API 호출 (기존 saveRestApiBatch 재사용)
      try {
        const result = await BatchManagementAPI.saveRestApiBatch({
          batchName,
          batchType,
          cronSchedule,
          description,
          apiMappings: dbMappings,
          authServiceName: authServiceName || undefined,
        });

        if (result.success) {
          toast.success(result.message || "DB → REST API 배치 설정이 저장되었습니다.");
          setTimeout(() => {
            openTab({ type: "admin", title: "배치 관리", adminUrl: "/admin/automaticMng/batchmngList" });
          }, 1000);
        } else {
          toast.error(result.message || "배치 저장에 실패했습니다.");
        }
      } catch (error) {
        console.error("배치 저장 오류:", error);
        showErrorToast("배치 설정 저장에 실패했습니다", error, {
          guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
        });
      }
      return;
    }

    toast.error("지원하지 않는 배치 타입입니다.");
  };

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

  return (
    <div className="mx-auto max-w-5xl space-y-6 p-4 sm:p-6">
      {/* 헤더 */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-3">
          <button onClick={goBack} className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground">
            <ArrowLeft className="h-4 w-4" />
          </button>
          <div>
            <h1 className="text-lg font-semibold sm:text-xl">고급 배치 생성</h1>
            <p className="text-xs text-muted-foreground">REST API / DB 간 데이터 동기화 배치를 설정합니다</p>
          </div>
        </div>
      </div>

      {/* 배치 타입 선택 */}
      <div className="grid grid-cols-2 gap-3">
        {batchTypeOptions.map((option) => (
          <button
            key={option.value}
            onClick={() => setBatchType(option.value)}
            className={`group relative flex items-center gap-3 rounded-lg border p-4 text-left transition-all ${
              batchType === option.value
                ? "border-primary bg-primary/5 ring-1 ring-primary/30"
                : "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 ${batchType === option.value ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`}>
              {option.value === "restapi-to-db" ? <Globe className="h-5 w-5" /> : <Database className="h-5 w-5" />}
            </div>
            <div className="min-w-0">
              <div className="text-sm font-medium">{option.label}</div>
              <div className="text-[11px] text-muted-foreground">{option.description}</div>
            </div>
            {batchType === option.value && <div className="absolute right-3 top-3 h-2 w-2 rounded-full bg-primary" />}
          </button>
        ))}
      </div>

      {/* 기본 정보 */}
      <div className="space-y-4 rounded-lg border p-4 sm:p-5">
        <div className="flex items-center gap-2 text-sm font-medium">
          <Eye className="h-4 w-4 text-muted-foreground" />
          기본 정보
        </div>
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
          <div className="space-y-1.5">
            <Label htmlFor="batchName" className="text-xs">배치명 <span className="text-destructive">*</span></Label>
            <Input id="batchName" value={batchName} onChange={e => setBatchName(e.target.value)} placeholder="배치명을 입력하세요" className="h-9 text-sm" />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="cronSchedule" className="text-xs">실행 스케줄 <span className="text-destructive">*</span></Label>
            <Input id="cronSchedule" value={cronSchedule} onChange={e => setCronSchedule(e.target.value)} placeholder="0 12 * * *" className="h-9 font-mono text-sm" />
          </div>
        </div>
        <div className="space-y-1.5">
          <Label htmlFor="description" className="text-xs">설명</Label>
          <Textarea id="description" value={description} onChange={e => setDescription(e.target.value)} placeholder="배치에 대한 설명을 입력하세요" rows={2} className="resize-none text-sm" />
        </div>
      </div>

      {/* FROM/TO 설정 - 가로 배치 */}
      <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
        {/* FROM 설정 */}
        <Card>
          <CardHeader>
            <CardTitle className="flex items-center">
              {batchType === "restapi-to-db" ? (
                <>
                  <Globe className="mr-2 h-5 w-5" />
                  FROM: REST API (소스)
                </>
              ) : (
                <>
                  <Database className="mr-2 h-5 w-5" />
                  FROM: 데이터베이스 (소스)
                </>
              )}
            </CardTitle>
          </CardHeader>
          <CardContent className="space-y-4">
            {/* REST API 설정 (REST API → DB) */}
            {batchType === "restapi-to-db" && (
              <div className="space-y-4">
                {/* API 서버 URL */}
                <div>
                  <Label htmlFor="fromApiUrl">API 서버 URL *</Label>
                  <Input
                    id="fromApiUrl"
                    value={fromApiUrl}
                    onChange={(e) => setFromApiUrl(e.target.value)}
                    placeholder="https://api.example.com"
                  />
                </div>

                {/* 인증 토큰 설정 */}
                <div>
                  <Label>인증 토큰 (Authorization)</Label>
                  {/* 토큰 설정 방식 선택 */}
                  <div className="mt-2 flex 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");
                          setAuthServiceName("");
                        }}
                        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
                      id="fromApiKey"
                      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 htmlFor="fromEndpoint">엔드포인트 *</Label>
                  <Input
                    id="fromEndpoint"
                    value={fromEndpoint}
                    onChange={(e) => setFromEndpoint(e.target.value)}
                    placeholder="/api/users"
                  />
                </div>

                {/* HTTP 메서드 */}
                <div>
                  <Label>HTTP 메서드</Label>
                  <Select value={fromApiMethod} onValueChange={(value: any) => setFromApiMethod(value)}>
                    <SelectTrigger>
                      <SelectValue />
                    </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 htmlFor="dataArrayPath">데이터 배열 경로</Label>
                  <Input
                    id="dataArrayPath"
                    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용) */}
                {(fromApiMethod === "POST" || fromApiMethod === "PUT" || fromApiMethod === "DELETE") && (
                  <div>
                    <Label htmlFor="fromApiBody">Request Body (JSON)</Label>
                    <Textarea
                      id="fromApiBody"
                      value={fromApiBody}
                      onChange={(e) => setFromApiBody(e.target.value)}
                      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 htmlFor="apiParamName">파라미터명 *</Label>
                        <Input
                          id="apiParamName"
                          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 htmlFor="apiParamValue">
                          {apiParamSource === "static" ? "파라미터 값" : "파라미터 템플릿"} *
                        </Label>
                        <Input
                          id="apiParamValue"
                          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">
                            실제 호출: {fromEndpoint || "/api/users"}?{apiParamName || "userId"}=
                            {apiParamValue || "123"}
                          </div>
                        </div>
                      )}
                    </>
                  )}
                </div>

                {/* API 호출 미리보기 정보 */}
                {fromApiUrl && fromEndpoint && (
                  <div className="rounded-lg bg-muted p-3">
                    <div className="text-sm font-medium text-foreground">API 호출 정보</div>
                    <div className="mt-1 text-sm text-muted-foreground">
                      {fromApiMethod} {fromApiUrl}
                      {apiParamType === "url" && apiParamName && apiParamValue
                        ? fromEndpoint.replace(`{${apiParamName}}`, apiParamValue) || fromEndpoint + `/${apiParamValue}`
                        : fromEndpoint}
                      {apiParamType === "query" && apiParamName && apiParamValue
                        ? `?${apiParamName}=${apiParamValue}`
                        : ""}
                    </div>
                    {((authTokenMode === "direct" && fromApiKey) || (authTokenMode === "db" && authServiceName)) && (
                      <div className="mt-1 text-xs text-muted-foreground">
                        {authTokenMode === "direct"
                          ? `Authorization: Bearer ${fromApiKey.substring(0, 15)}...`
                          : `Authorization: DB 토큰 (${authServiceName})`}
                      </div>
                    )}
                    {apiParamType !== "none" && apiParamName && apiParamValue && (
                      <div className="mt-1 text-xs text-primary">
                        파라미터: {apiParamName} = {apiParamValue} ({apiParamSource === "static" ? "고정값" : "동적값"})
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}

            {/* DB 설정 (DB → REST API) */}
            {batchType === "db-to-restapi" && (
              <div className="space-y-4">
                <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
                  <div>
                    <Label>소스 연결 *</Label>
                    <Select
                      value={fromConnection?.id?.toString() || fromConnection?.type || ""}
                      onValueChange={handleFromConnectionChange}
                    >
                      <SelectTrigger>
                        <SelectValue placeholder="연결을 선택하세요" />
                      </SelectTrigger>
                      <SelectContent>
                        {connections.map((connection) => (
                          <SelectItem
                            key={connection.id || "internal"}
                            value={connection.id ? connection.id.toString() : "internal"}
                          >
                            {connection.name} ({connection.type === "internal" ? "내부 DB" : connection.db_type})
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                  <div>
                    <Label>소스 테이블 *</Label>
                    <Select value={fromTable} onValueChange={handleFromTableChange}>
                      <SelectTrigger>
                        <SelectValue placeholder="테이블을 선택하세요" />
                      </SelectTrigger>
                      <SelectContent>
                        {Array.isArray(fromTables) &&
                          fromTables.map((table: string) => (
                            <SelectItem key={table} value={table}>
                              {table.toUpperCase()}
                            </SelectItem>
                          ))}
                      </SelectContent>
                    </Select>
                  </div>
                </div>

                {/* 컬럼 선택 UI */}
                {fromColumns.length > 0 && (
                  <div>
                    <Label>컬럼 선택 (선택된 컬럼이 API로 전송됩니다)</Label>
                    <div className="mt-2 grid max-h-60 grid-cols-2 gap-2 overflow-y-auto rounded-lg border p-3 md:grid-cols-3 lg:grid-cols-4">
                      {fromColumns.map((column) => (
                        <div key={column.column_name} className="flex items-center space-x-2">
                          <input
                            type="checkbox"
                            id={`col-${column.column_name}`}
                            checked={selectedColumns.includes(column.column_name)}
                            onChange={(e) => {
                              if (e.target.checked) {
                                setSelectedColumns([...selectedColumns, column.column_name]);
                              } else {
                                setSelectedColumns(selectedColumns.filter((col) => col !== column.column_name));
                              }
                            }}
                            className="rounded border-input"
                          />
                          <label
                            htmlFor={`col-${column.column_name}`}
                            className="flex-1 cursor-pointer text-sm"
                            title={`타입: ${column.data_type} | NULL: ${column.is_nullable ? "Y" : "N"}`}
                          >
                            {column.column_name}
                          </label>
                        </div>
                      ))}
                    </div>

                    {/* 선택된 컬럼 개수 표시 */}
                    <div className="mt-2 text-xs text-muted-foreground">
                      선택된 컬럼: {selectedColumns.length}개 / 전체: {fromColumns.length}개
                    </div>

                    {/* 빠른 매핑 버튼들 */}
                    {selectedColumns.length > 0 && toApiFields.length > 0 && (
                      <div className="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3">
                        <div className="mb-2 text-sm font-medium text-emerald-800">빠른 매핑</div>
                        <div className="flex flex-wrap gap-2">
                          <button
                            type="button"
                            onClick={() => {
                              const mapping: Record<string, string> = {};
                              selectedColumns.forEach((col) => {
                                // 스마트 매핑 로직
                                const matchingApiField = toApiFields.find((apiField) => {
                                  const colLower = col.toLowerCase();
                                  const apiLower = apiField.toLowerCase();

                                  // 정확한 매치
                                  if (colLower === apiLower) return true;

                                  // 언더스코어 무시 매치
                                  if (colLower.replace(/_/g, "") === apiLower.replace(/_/g, "")) return true;
                                  // 의미적 매핑
                                  if (
                                    (colLower.includes("created") || colLower.includes("reg")) &&
                                    (apiLower.includes("date") || apiLower.includes("time"))
                                  )
                                    return true;
                                  if (
                                    (colLower.includes("updated") || colLower.includes("mod")) &&
                                    (apiLower.includes("date") || apiLower.includes("time"))
                                  )
                                    return true;
                                  if (colLower.includes("id") && apiLower.includes("id")) return true;
                                  if (colLower.includes("name") && apiLower.includes("name")) return true;
                                  if (colLower.includes("code") && apiLower.includes("code")) return true;

                                  return false;
                                });

                                if (matchingApiField) {
                                  mapping[col] = matchingApiField;
                                }
                              });
                              setDbToApiFieldMapping(mapping);
                              toast.success(`${Object.keys(mapping).length}개 컬럼이 자동 매핑되었습니다.`);
                            }}
                            className="rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
                          >
                            스마트 자동 매핑
                          </button>
                          <button
                            type="button"
                            onClick={() => {
                              setDbToApiFieldMapping({});
                              toast.success("매핑이 초기화되었습니다.");
                            }}
                            className="rounded bg-foreground/80 px-3 py-1 text-xs text-white hover:bg-foreground/90"
                          >
                            매핑 초기화
                          </button>
                        </div>
                      </div>
                    )}

                    {/* 자동 생성된 JSON 미리보기 */}
                    {selectedColumns.length > 0 && (
                      <div className="mt-3 rounded-lg border border-primary/20 bg-primary/10 p-3">
                        <div className="mb-2 text-sm font-medium text-primary">자동 생성된 JSON 구조</div>
                        <pre className="overflow-x-auto font-mono text-xs text-primary">
                          {JSON.stringify(
                            selectedColumns.reduce(
                              (obj, col) => {
                                const apiField = dbToApiFieldMapping[col] || col; // 매핑된 API 필드명 또는 원본 컬럼명
                                obj[apiField] = `{{${col}}}`;
                                return obj;
                              },
                              {} as Record<string, string>,
                            ),
                            null,
                            2,
                          )}
                        </pre>
                        <button
                          type="button"
                          onClick={() => {
                            const autoJson = JSON.stringify(
                              selectedColumns.reduce(
                                (obj, col) => {
                                  const apiField = dbToApiFieldMapping[col] || col; // 매핑된 API 필드명 또는 원본 컬럼명
                                  obj[apiField] = `{{${col}}}`;
                                  return obj;
                                },
                                {} as Record<string, string>,
                              ),
                              null,
                              2,
                            );
                            setToApiBody(autoJson);
                            toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다.");
                          }}
                          className="mt-2 rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
                        >
                          Request Body에 적용
                        </button>
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}
          </CardContent>
        </Card>

        {/* TO 설정 */}
        <Card>
          <CardHeader>
            <CardTitle className="flex items-center">
              {batchType === "restapi-to-db" ? (
                <>
                  <Database className="mr-2 h-5 w-5" />
                  TO: 데이터베이스 (대상)
                </>
              ) : (
                <>
                  <Globe className="mr-2 h-5 w-5" />
                  TO: REST API (대상)
                </>
              )}
            </CardTitle>
          </CardHeader>
          <CardContent className="space-y-4">
            {/* DB 설정 (REST API → DB) - 단계적 활성화 */}
            {batchType === "restapi-to-db" && (
              <div className="space-y-4">
                {/* 1. 커넥션 선택 - 항상 활성화 */}
                <div>
                  <Label>데이터베이스 커넥션 선택 *</Label>
                  <Select onValueChange={handleToConnectionChange}>
                    <SelectTrigger>
                      <SelectValue placeholder="커넥션을 선택하세요" />
                    </SelectTrigger>
                    <SelectContent>
                      {connections.map((connection, index) => (
                        <SelectItem
                          key={connection.id || `internal-${index}`}
                          value={connection.id ? connection.id.toString() : "internal"}
                        >
                          {connection.name}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>

                {/* 2. 테이블 선택 - 커넥션 선택 후 활성화 */}
                <div className={toTables.length === 0 ? "pointer-events-none opacity-50" : ""}>
                  <Label>테이블 선택 *</Label>
                  <Select onValueChange={handleToTableChange} disabled={toTables.length === 0}>
                    <SelectTrigger>
                      <SelectValue
                        placeholder={toTables.length === 0 ? "먼저 커넥션을 선택하세요" : "테이블을 선택하세요"}
                      />
                    </SelectTrigger>
                    <SelectContent>
                      {toTables.map((table: string) => (
                        <SelectItem key={table} value={table}>
                          {table.toUpperCase()}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                </div>

                {/* 3. 저장 모드 - 테이블 선택 후 활성화 */}
                <div className={!toTable ? "pointer-events-none opacity-50" : ""}>
                  <Label>저장 모드</Label>
                  <div className="mt-2 flex gap-4">
                    <label className="flex cursor-pointer items-center gap-2">
                      <input
                        type="radio"
                        name="saveMode"
                        value="INSERT"
                        checked={saveMode === "INSERT"}
                        onChange={() => {
                          setSaveMode("INSERT");
                          setConflictKey("");
                        }}
                        className="h-4 w-4"
                        disabled={!toTable}
                      />
                      <span className="text-sm">INSERT (새 데이터 추가)</span>
                    </label>
                    <label className="flex cursor-pointer items-center gap-2">
                      <input
                        type="radio"
                        name="saveMode"
                        value="UPSERT"
                        checked={saveMode === "UPSERT"}
                        onChange={() => setSaveMode("UPSERT")}
                        className="h-4 w-4"
                        disabled={!toTable}
                      />
                      <span className="text-sm">UPSERT (있으면 업데이트, 없으면 추가)</span>
                    </label>
                  </div>
                </div>

                {/* 4. 충돌 기준 컬럼 - UPSERT 선택 후 활성화 */}
                <div className={saveMode !== "UPSERT" ? "pointer-events-none opacity-50" : ""}>
                  <Label htmlFor="conflictKey">충돌 기준 컬럼 (Conflict Key) *</Label>
                  <Select value={conflictKey} onValueChange={setConflictKey} disabled={saveMode !== "UPSERT"}>
                    <SelectTrigger className="mt-2">
                      <SelectValue
                        placeholder={
                          saveMode !== "UPSERT" ? "UPSERT 모드를 선택하세요" : "UPSERT 기준 컬럼을 선택하세요"
                        }
                      />
                    </SelectTrigger>
                    <SelectContent>
                      {toColumns.map((col) => (
                        <SelectItem key={col.column_name} value={col.column_name}>
                          {col.column_name}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                  <p className="mt-1 text-xs text-muted-foreground">
                    이 컬럼 값이 같으면 UPDATE, 없으면 INSERT 합니다. (예: device_serial_number)
                  </p>
                </div>
              </div>
            )}

            {/* REST API 설정 (DB → REST API) */}
            {batchType === "db-to-restapi" && (
              <div className="space-y-4">
                {/* API 서버 URL */}
                <div>
                  <Label htmlFor="toApiUrl">API 서버 URL *</Label>
                  <Input
                    id="toApiUrl"
                    value={toApiUrl}
                    onChange={(e) => setToApiUrl(e.target.value)}
                    placeholder="https://api.example.com"
                  />
                </div>

                {/* API 키 */}
                <div>
                  <Label htmlFor="toApiKey">API 키 *</Label>
                  <Input
                    id="toApiKey"
                    value={toApiKey}
                    onChange={(e) => setToApiKey(e.target.value)}
                    placeholder="ak_your_api_key_here"
                  />
                </div>

                {/* 엔드포인트 */}
                <div>
                  <Label htmlFor="toEndpoint">엔드포인트 *</Label>
                  <Input
                    id="toEndpoint"
                    value={toEndpoint}
                    onChange={(e) => setToEndpoint(e.target.value)}
                    placeholder="/api/users"
                  />
                  {(toApiMethod === "PUT" || toApiMethod === "DELETE") && (
                    <p className="mt-1 text-xs text-muted-foreground">
                      실제 URL: {toEndpoint}/{urlPathColumn ? `{${urlPathColumn}}` : "{ID}"}
                    </p>
                  )}
                </div>

                {/* HTTP 메서드 */}
                <div>
                  <Label>HTTP 메서드</Label>
                  <Select value={toApiMethod} onValueChange={(value: any) => setToApiMethod(value)}>
                    <SelectTrigger>
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="POST">POST (데이터 생성)</SelectItem>
                      <SelectItem value="PUT">PUT (데이터 업데이트)</SelectItem>
                      <SelectItem value="DELETE">DELETE (데이터 삭제)</SelectItem>
                    </SelectContent>
                  </Select>
                </div>

                {/* URL 경로 파라미터 설정 (PUT/DELETE용) */}
                {(toApiMethod === "PUT" || toApiMethod === "DELETE") && (
                  <div>
                    <Label>URL 경로 파라미터 컬럼 *</Label>
                    <Select value={urlPathColumn} onValueChange={setUrlPathColumn}>
                      <SelectTrigger>
                        <SelectValue placeholder="URL 경로에 사용할 컬럼을 선택하세요" />
                      </SelectTrigger>
                      <SelectContent>
                        {selectedColumns.map((column) => (
                          <SelectItem key={column} value={column}>
                            {column} (예: /api/users/{`{${column}}`})
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <p className="mt-1 text-xs text-muted-foreground">
                      PUT/DELETE 요청 시 URL 경로에 포함될 컬럼을 선택하세요. (예: USER_ID → /api/users/user123)
                    </p>
                  </div>
                )}

                {/* TO API 미리보기 버튼 */}
                <div className="flex justify-center">
                  <button
                    type="button"
                    onClick={previewToApiData}
                    className="flex items-center space-x-2 rounded-md bg-emerald-600 px-4 py-2 text-white hover:bg-green-700"
                  >
                    <Eye className="h-4 w-4" />
                    <span>API 필드 미리보기</span>
                  </button>
                </div>

                {/* TO API 필드 표시 */}
                {toApiFields.length > 0 && (
                  <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3">
                    <div className="mb-2 text-sm font-medium text-emerald-800">
                      API 필드 목록 ({toApiFields.length}개)
                    </div>
                    <div className="flex flex-wrap gap-2">
                      {toApiFields.map((field) => (
                        <span key={field} className="rounded bg-emerald-100 px-2 py-1 text-xs text-emerald-700">
                          {field}
                        </span>
                      ))}
                    </div>
                  </div>
                )}

                {/* Request Body 템플릿 */}
                {(toApiMethod === "POST" || toApiMethod === "PUT") && (
                  <div>
                    <Label htmlFor="toApiBody">Request Body 템플릿 (JSON)</Label>
                    <textarea
                      id="toApiBody"
                      value={toApiBody}
                      onChange={(e) => setToApiBody(e.target.value)}
                      placeholder='{"id": "{{id}}", "name": "{{name}}", "email": "{{email}}"}'
                      className="h-24 w-full rounded-md border p-2 font-mono text-sm"
                    />
                    <div className="mt-1 text-xs text-muted-foreground">
                      DB 컬럼 값을 {"{{컬럼명}}"} 형태로 매핑하세요. 예: {"{{user_id}}, {{user_name}}"}
                    </div>
                  </div>
                )}

                {/* API 호출 정보 */}
                {toApiUrl && toApiKey && toEndpoint && (
                  <div className="rounded-lg bg-muted p-3">
                    <div className="text-sm font-medium text-foreground">API 호출 정보</div>
                    <div className="mt-1 text-sm text-muted-foreground">
                      {toApiMethod} {toApiUrl}
                      {toEndpoint}
                    </div>
                    <div className="mt-1 text-xs text-muted-foreground">Headers: X-API-Key: {toApiKey.substring(0, 10)}...</div>
                    {toApiBody && (
                      <div className="mt-1 text-xs text-primary">Body: {toApiBody.substring(0, 50)}...</div>
                    )}
                  </div>
                )}
              </div>
            )}
          </CardContent>
        </Card>
      </div>

      {/* API 데이터 미리보기 버튼 - FROM/TO 섹션 아래 */}
      {batchType === "restapi-to-db" && (
        <div className="flex justify-center">
          <Button
            onClick={previewRestApiData}
            variant="outline"
            disabled={!fromApiUrl || !fromEndpoint || !toTable}
            className="gap-2"
          >
            <RefreshCw className="h-4 w-4" />
            데이터 불러오고 매핑하기
          </Button>
          {(!fromApiUrl || !fromEndpoint || !toTable) && (
            <p className="ml-4 flex items-center text-xs text-muted-foreground">
              FROM 섹션과 TO 섹션의 필수 값을 모두 입력해야 합니다.
            </p>
          )}
        </div>
      )}

      {/* 매핑 UI - 배치 타입별 동적 렌더링 */}
      {/* REST API → DB 매핑 */}
      {batchType === "restapi-to-db" && fromApiFields.length > 0 && toColumns.length > 0 && (
        <RestApiToDbMappingCard
          fromApiFields={fromApiFields}
          toColumns={toColumns}
          fromApiData={fromApiData}
          mappingList={mappingList}
          setMappingList={setMappingList}
        />
      )}

      {/* DB → REST API 매핑 */}
      {batchType === "db-to-restapi" && selectedColumns.length > 0 && toApiFields.length > 0 && (
        <DbToRestApiMappingCard
          fromColumns={fromColumns}
          selectedColumns={selectedColumns}
          toApiFields={toApiFields}
          dbToApiFieldMapping={dbToApiFieldMapping}
          setDbToApiFieldMapping={setDbToApiFieldMapping}
          setToApiBody={setToApiBody}
        />
      )}

      {/* 하단 액션 버튼 */}
      <div className="flex items-center justify-end gap-2 border-t pt-4">
        <Button onClick={goBack} variant="outline" size="sm" className="h-8 gap-1 text-xs">취소</Button>
        <Button onClick={loadConnections} variant="outline" size="sm" className="h-8 gap-1 text-xs">
          <RefreshCw className="h-3.5 w-3.5" />
          새로고침
        </Button>
        <Button onClick={handleSave} size="sm" className="h-8 gap-1 text-xs">
          <Save className="h-3.5 w-3.5" />
          저장
        </Button>
      </div>
    </div>
  );
}

const RestApiToDbMappingCard = memo(function RestApiToDbMappingCard({
  fromApiFields,
  toColumns,
  fromApiData,
  mappingList,
  setMappingList,
}: RestApiToDbMappingCardProps) {
  // 샘플 JSON 문자열
  const sampleJsonList = useMemo(
    () => fromApiData.slice(0, 3).map((item) => JSON.stringify(item, null, 2)),
    [fromApiData],
  );

  const firstSample = fromApiData[0] || null;

  // 이미 매핑된 DB 컬럼들
  const mappedDbColumns = useMemo(() => mappingList.map((m) => m.dbColumn).filter(Boolean), [mappingList]);

  // 매핑 추가
  const addMapping = () => {
    const newId = `mapping-${Date.now()}`;
    setMappingList((prev) => [
      ...prev,
      {
        id: newId,
        dbColumn: "",
        sourceType: "api",
        apiField: "",
        fixedValue: "",
      },
    ]);
  };

  // 매핑 삭제
  const removeMapping = (id: string) => {
    setMappingList((prev) => prev.filter((m) => m.id !== id));
  };

  // 매핑 업데이트
  const updateMapping = (id: string, updates: Partial<MappingItem>) => {
    setMappingList((prev) => prev.map((m) => (m.id === id ? { ...m, ...updates } : m)));
  };

  return (
    <Card>
      <CardHeader>
        <CardTitle>컬럼 매핑 설정</CardTitle>
        <CardDescription>DB 컬럼에 API 필드 또는 고정값을 매핑합니다.</CardDescription>
      </CardHeader>
      <CardContent>
        <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>
            {sampleJsonList.length > 0 ? (
              <div className="bg-muted/30 h-[360px] overflow-y-auto rounded-lg border p-3">
                <div className="space-y-2">
                  {sampleJsonList.map((json, index) => (
                    <div key={index} className="bg-background rounded border p-2">
                      <pre className="font-mono text-xs whitespace-pre-wrap">{json}</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>
              <Button variant="outline" size="sm" onClick={addMapping} className="h-8 gap-1">
                <Plus className="h-4 w-4" />
                매핑 추가
              </Button>
            </div>

            {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={addMapping} 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) =>
                          updateMapping(mapping.id, { dbColumn: value === "none" ? "" : value })
                        }
                      >
                        <SelectTrigger className="h-9">
                          <SelectValue placeholder="DB 컬럼" />
                        </SelectTrigger>
                        <SelectContent>
                          <SelectItem value="none">선택 안함</SelectItem>
                          {toColumns.map((col) => {
                            const isUsed =
                              mappedDbColumns.includes(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") =>
                          updateMapping(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) =>
                            updateMapping(mapping.id, { apiField: value === "none" ? "" : value })
                          }
                        >
                          <SelectTrigger className="h-9">
                            <SelectValue placeholder="API 필드" />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value="none">선택 안함</SelectItem>
                            {fromApiFields.map((field) => (
                              <SelectItem key={field} value={field}>
                                <div className="flex items-center gap-2">
                                  <span>{field}</span>
                                  {firstSample && firstSample[field] !== undefined && (
                                    <span className="text-muted-foreground text-xs">
                                      (예: {String(firstSample[field]).substring(0, 15)}
                                      {String(firstSample[field]).length > 15 ? "..." : ""})
                                    </span>
                                  )}
                                </div>
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      ) : (
                        <Input
                          value={mapping.fixedValue}
                          onChange={(e) => updateMapping(mapping.id, { fixedValue: e.target.value })}
                          placeholder="고정값 입력"
                          className="h-9"
                        />
                      )}
                    </div>

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

const DbToRestApiMappingCard = memo(function DbToRestApiMappingCard({
  fromColumns,
  selectedColumns,
  toApiFields,
  dbToApiFieldMapping,
  setDbToApiFieldMapping,
  setToApiBody,
}: DbToRestApiMappingCardProps) {
  const selectedColumnObjects = useMemo(
    () => fromColumns.filter((column) => selectedColumns.includes(column.column_name)),
    [fromColumns, selectedColumns],
  );

  const autoJsonPreview = useMemo(() => {
    if (selectedColumns.length === 0) {
      return "";
    }
    const obj = selectedColumns.reduce(
      (acc, col) => {
        const apiField = dbToApiFieldMapping[col] || col;
        acc[apiField] = `{{${col}}}`;
        return acc;
      },
      {} as Record<string, string>,
    );
    return JSON.stringify(obj, null, 2);
  }, [selectedColumns, dbToApiFieldMapping]);

  return (
    <Card>
      <CardHeader>
        <CardTitle>DB 컬럼 → API 필드 매핑</CardTitle>
        <CardDescription>
          DB 컬럼 값을 REST API Request Body에 매핑하세요. Request Body 템플릿에서 {"{{컬럼명}}"} 형태로 사용됩니다.
        </CardDescription>
      </CardHeader>
      <CardContent>
        <div className="max-h-96 space-y-3 overflow-y-auto rounded-lg border p-4">
          {selectedColumnObjects.map((column) => (
            <div key={column.column_name} className="flex items-center space-x-4 rounded-lg bg-muted p-3">
              {/* DB 컬럼 정보 */}
              <div className="flex-1">
                <div className="text-sm font-medium">{column.column_name}</div>
                <div className="text-xs text-muted-foreground">
                  타입: {column.data_type} | NULL: {column.is_nullable ? "Y" : "N"}
                </div>
              </div>

              {/* 화살표 */}
              <div className="text-muted-foreground/70">→</div>

              {/* API 필드 선택 드롭다운 */}
              <div className="flex-1">
                <Select
                  value={dbToApiFieldMapping[column.column_name] || ""}
                  onValueChange={(value) => {
                    setDbToApiFieldMapping((prev) => ({
                      ...prev,
                      [column.column_name]: value === "none" ? "" : value,
                    }));
                  }}
                >
                  <SelectTrigger className="w-full">
                    <SelectValue placeholder="API 필드 선택" />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="none">선택 안함</SelectItem>
                    {toApiFields.map((apiField) => (
                      <SelectItem key={apiField} value={apiField}>
                        {apiField}
                      </SelectItem>
                    ))}
                    <SelectItem value="custom">직접 입력...</SelectItem>
                  </SelectContent>
                </Select>

                {/* 직접 입력 모드 */}
                {dbToApiFieldMapping[column.column_name] === "custom" && (
                  <input
                    type="text"
                    placeholder="API 필드명을 직접 입력하세요"
                    className="mt-2 w-full rounded-md border border-input px-3 py-2 text-sm focus:ring-2 focus:ring-ring focus:outline-none"
                    onChange={(e) => {
                      setDbToApiFieldMapping((prev) => ({
                        ...prev,
                        [column.column_name]: e.target.value,
                      }));
                    }}
                  />
                )}

                <div className="mt-1 text-xs text-muted-foreground">
                  {dbToApiFieldMapping[column.column_name]
                    ? `매핑: ${column.column_name} → ${dbToApiFieldMapping[column.column_name]}`
                    : `기본값: ${column.column_name} (DB 컬럼명 사용)`}
                </div>
              </div>

              {/* 템플릿 미리보기 */}
              <div className="flex-1">
                <div className="rounded border bg-white p-2 font-mono text-sm">{`{{${column.column_name}}}`}</div>
                <div className="mt-1 text-xs text-muted-foreground">실제 DB 값으로 치환됩니다</div>
              </div>
            </div>
          ))}
        </div>

        {selectedColumns.length > 0 && (
          <div className="mt-4 rounded-lg border border-primary/20 bg-primary/10 p-3">
            <div className="text-sm font-medium text-primary">자동 생성된 JSON 구조</div>
            <pre className="mt-1 overflow-x-auto font-mono text-xs text-primary">{autoJsonPreview}</pre>
            <button
              type="button"
              onClick={() => {
                setToApiBody(autoJsonPreview);
                toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다.");
              }}
              className="mt-2 rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
            >
              Request Body에 적용
            </button>
          </div>
        )}

        <div className="mt-4 rounded-lg border border-primary/20 bg-primary/10 p-3">
          <div className="text-sm font-medium text-primary">매핑 사용 예시</div>
          <div className="mt-1 font-mono text-xs text-primary">
            {'{"id": "{{id}}", "name": "{{user_name}}", "email": "{{email}}"}'}
          </div>
        </div>
      </CardContent>
    </Card>
  );
});
