"use client";

import * as React from "react";
import dynamic from "next/dynamic";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { GenericData } from "./export-excel-form";
import * as GC from "@mescius/spread-sheets";
import { toast } from "sonner";
import { updateFormDataInDB } from "@/lib/forms/services";
import { Loader, Save, AlertTriangle } from "lucide-react";
import '@mescius/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css';
import { DataTableColumnJSON, ColumnType } from "./form-data-table-columns";
import { setupSpreadJSLicense } from "@/lib/spread-js/license-utils";

const SpreadSheets = dynamic(
  () => import("@mescius/spread-sheets-react").then(mod => mod.SpreadSheets),
  {
    ssr: false,
    loading: () => (
      <div className="flex items-center justify-center h-full">
        <Loader className="mr-2 h-4 w-4 animate-spin" />
        Loading SpreadSheets...
      </div>
    )
  }
);

// 도메인별 라이선스 설정
if (typeof window !== 'undefined') {
  setupSpreadJSLicense(GC);
}

interface TemplateItem {
  TMPL_ID: string;
  NAME: string;
  TMPL_TYPE: string;
  SPR_LST_SETUP: {
    ACT_SHEET: string;
    HIDN_SHEETS: Array<string>;
    CONTENT?: string;
    DATA_SHEETS: Array<{
      SHEET_NAME: string;
      REG_TYPE_ID: string;
      MAP_CELL_ATT: Array<{
        ATT_ID: string;
        IN: string;
      }>;
    }>;
  };
  GRD_LST_SETUP: {
    REG_TYPE_ID: string;
    SPR_ITM_IDS: Array<string>;
    ATTS: Array<{}>;
  };
  SPR_ITM_LST_SETUP: {
    ACT_SHEET: string;
    HIDN_SHEETS: Array<string>;
    CONTENT?: string;
    DATA_SHEETS: Array<{
      SHEET_NAME: string;
      REG_TYPE_ID: string;
      MAP_CELL_ATT: Array<{
        ATT_ID: string;
        IN: string;
      }>;
    }>;
  };
}

interface ValidationError {
  cellAddress: string;
  attId: string;
  value: any;
  expectedType: ColumnType;
  message: string;
}

interface CellMapping {
  attId: string;
  cellAddress: string;
  isEditable: boolean;
  dataRowIndex?: number;
}

interface TemplateViewDialogProps {
  isOpen: boolean;
  onClose: () => void;
  templateData: TemplateItem[] | any;
  selectedRow?: GenericData;
  tableData?: GenericData[];
  formCode: string;
  columnsJSON: DataTableColumnJSON[]
  contractItemId: number;
  editableFieldsMap?: Map<string, string[]>;
  onUpdateSuccess?: (updatedValues: Record<string, any> | GenericData[]) => void;
}

// 🚀 로딩 프로그레스 컴포넌트
interface LoadingProgressProps {
  phase: string;
  progress: number;
  total: number;
  isVisible: boolean;
}

const LoadingProgress: React.FC<LoadingProgressProps> = ({ phase, progress, total, isVisible }) => {
  const percentage = total > 0 ? Math.round((progress / total) * 100) : 0;

  if (!isVisible) return null;

  return (
    <div className="absolute inset-0 bg-white/90 flex items-center justify-center z-50">
      <div className="bg-white rounded-lg shadow-lg border p-6 min-w-[300px]">
        <div className="flex items-center space-x-3 mb-4">
          <Loader className="h-5 w-5 animate-spin text-blue-600" />
          <span className="font-medium text-gray-900">Loading Template</span>
        </div>

        <div className="space-y-2">
          <div className="text-sm text-gray-600">{phase}</div>
          <div className="w-full bg-gray-200 rounded-full h-2">
            <div
              className="bg-blue-600 h-2 rounded-full transition-all duration-300 ease-out"
              style={{ width: `${percentage}%` }}
            />
          </div>
          <div className="text-xs text-gray-500 text-right">
            {progress.toLocaleString()} / {total.toLocaleString()} ({percentage}%)
          </div>
        </div>
      </div>
    </div>
  );
};

export function TemplateViewDialog({
  isOpen,
  onClose,
  templateData,
  selectedRow,
  tableData = [],
  formCode,
  contractItemId,
  columnsJSON,
  editableFieldsMap = new Map(),
  onUpdateSuccess
}: TemplateViewDialogProps) {
  const [hostStyle, setHostStyle] = React.useState({
    width: '100%',
    height: '100%'
  });

  const [isPending, setIsPending] = React.useState(false);
  const [hasChanges, setHasChanges] = React.useState(false);
  const [currentSpread, setCurrentSpread] = React.useState<any>(null);
  const [cellMappings, setCellMappings] = React.useState<CellMapping[]>([]);
  const [isClient, setIsClient] = React.useState(false);
  const [templateType, setTemplateType] = React.useState<'SPREAD_LIST' | 'SPREAD_ITEM' | 'GRD_LIST' | null>(null);
  const [validationErrors, setValidationErrors] = React.useState<ValidationError[]>([]);
  const [selectedTemplateId, setSelectedTemplateId] = React.useState<string>("");
  const [availableTemplates, setAvailableTemplates] = React.useState<TemplateItem[]>([]);

  // 🆕 로딩 상태 추가
  const [loadingProgress, setLoadingProgress] = React.useState<{
    phase: string;
    progress: number;
    total: number;
  } | null>(null);
  const [isInitializing, setIsInitializing] = React.useState(false);

  // 🔄 진행상황 업데이트 함수
  const updateProgress = React.useCallback((phase: string, progress: number, total: number) => {
    setLoadingProgress({ phase, progress, total });
  }, []);

  const determineTemplateType = React.useCallback((template: TemplateItem): 'SPREAD_LIST' | 'SPREAD_ITEM' | 'GRD_LIST' | null => {
    if (template?.TMPL_TYPE === "SPREAD_LIST" && (template?.SPR_LST_SETUP?.CONTENT || template?.SPR_ITM_LST_SETUP?.CONTENT)) {
      return 'SPREAD_LIST';
    }
    if (template?.TMPL_TYPE === "SPREAD_ITEM" && (template?.SPR_LST_SETUP?.CONTENT || template?.SPR_ITM_LST_SETUP?.CONTENT)) {
      return 'SPREAD_ITEM';
    }
    if (template?.GRD_LST_SETUP && columnsJSON.length > 0) {
      return 'GRD_LIST';
    }
    return null;
  }, [columnsJSON]);

  const isValidTemplate = React.useCallback((template: TemplateItem): boolean => {
    // 🔍 TMPL_ID 필수 검증 추가
    if (!template || !template.TMPL_ID || typeof template.TMPL_ID !== 'string') {
      console.warn('⚠️ Invalid template: missing or invalid TMPL_ID', template);
      return false;
    }
    return determineTemplateType(template) !== null;
  }, [determineTemplateType]);

  React.useEffect(() => {
    setIsClient(true);
  }, []);

  React.useEffect(() => {
    // 템플릿 데이터가 없거나 빈 배열인 경우 기본 GRD_LIST 템플릿 생성
    if (!templateData || (Array.isArray(templateData) && templateData.length === 0)) {
      // columnsJSON이 있으면 기본 GRD_LIST 템플릿 생성
      if (columnsJSON && columnsJSON.length > 0) {
        const defaultGrdTemplate: TemplateItem = {
          TMPL_ID: 'DEFAULT_GRD_LIST',
          NAME: 'Default Grid View',
          TMPL_TYPE: 'GRD_LIST',
          SPR_LST_SETUP: {
            ACT_SHEET: '',
            HIDN_SHEETS: [],
            DATA_SHEETS: []
          },
          GRD_LST_SETUP: {
            REG_TYPE_ID: 'DEFAULT',
            SPR_ITM_IDS: [],
            ATTS: []
          },
          SPR_ITM_LST_SETUP: {
            ACT_SHEET: '',
            HIDN_SHEETS: [],
            DATA_SHEETS: []
          }
        };

        setAvailableTemplates([defaultGrdTemplate]);
        // setSelectedTemplateId('DEFAULT_GRD_LIST');
        // setTemplateType('GRD_LIST');
        console.log('📋 Created default GRD_LIST template');
      }
      return;
    }

    let templates: TemplateItem[];
    if (Array.isArray(templateData)) {
      templates = templateData as TemplateItem[];
    } else {
      templates = [templateData as TemplateItem];
    }

    const validTemplates = templates.filter(isValidTemplate);

    // 유효한 템플릿이 없지만 columnsJSON이 있으면 기본 GRD_LIST 추가
    if (validTemplates.length === 0 && columnsJSON && columnsJSON.length > 0) {
      const defaultGrdTemplate: TemplateItem = {
        TMPL_ID: 'DEFAULT_GRD_LIST',
        NAME: 'Default Grid View',
        TMPL_TYPE: 'GRD_LIST',
        SPR_LST_SETUP: {
          ACT_SHEET: '',
          HIDN_SHEETS: [],
          DATA_SHEETS: []
        },
        GRD_LST_SETUP: {
          REG_TYPE_ID: 'DEFAULT',
          SPR_ITM_IDS: [],
          ATTS: []
        },
        SPR_ITM_LST_SETUP: {
          ACT_SHEET: '',
          HIDN_SHEETS: [],
          DATA_SHEETS: []
        }
      };

      validTemplates.push(defaultGrdTemplate);
      console.log('📋 Added default GRD_LIST template to empty template list');
    }

    setAvailableTemplates(validTemplates);

    if (validTemplates.length > 0 && !selectedTemplateId) {
      const firstTemplate = validTemplates[0];
      // 🔍 TMPL_ID 검증 (isValidTemplate로 필터링했으므로 존재해야 하지만 안전장치)
      if (firstTemplate?.TMPL_ID) {
        const templateTypeToSet = determineTemplateType(firstTemplate);
        setSelectedTemplateId(firstTemplate.TMPL_ID);
        setTemplateType(templateTypeToSet);
      } else {
        console.error('❌ First valid template has no TMPL_ID:', firstTemplate);
      }
    }
  }, [templateData, selectedTemplateId, isValidTemplate, determineTemplateType, columnsJSON]);

  const handleTemplateChange = (templateId: string) => {
    const template = availableTemplates.find(t => t?.TMPL_ID === templateId);
    
    // 🔍 템플릿과 TMPL_ID 검증
    if (!template || !template.TMPL_ID) {
      console.error('❌ Template not found or invalid TMPL_ID:', templateId);
      return;
    }
    
    const templateTypeToSet = determineTemplateType(template);
    setSelectedTemplateId(templateId);
    setTemplateType(templateTypeToSet);
    setHasChanges(false);
    setValidationErrors([]);

    if (currentSpread) {
      initSpread(currentSpread, template);
    }
  };

  const selectedTemplate = React.useMemo(() => {
    const found = availableTemplates.find(t => t?.TMPL_ID === selectedTemplateId);
    if (!found && selectedTemplateId) {
      console.warn('⚠️ Selected template not found:', selectedTemplateId);
    }
    return found;
  }, [availableTemplates, selectedTemplateId]);

  const editableFields = React.useMemo(() => {
    // SPREAD_ITEM의 경우에만 전역 editableFields 사용
    if (templateType === 'SPREAD_ITEM' && selectedRow?.TAG_NO) {
      if (!editableFieldsMap.has(selectedRow.TAG_NO)) {
        return [];
      }
      return editableFieldsMap.get(selectedRow.TAG_NO) || [];
    }

    // SPREAD_LIST나 GRD_LIST의 경우 전역 editableFields는 사용하지 않음
    return [];
  }, [templateType, selectedRow?.TAG_NO, editableFieldsMap]);


  const isFieldEditable = React.useCallback((attId: string, rowData?: GenericData) => {
    const columnConfig = columnsJSON.find(col => col.key === attId);
    if (columnConfig?.shi === "OUT" || columnConfig?.shi === null) {
      return false;
    }

    if (attId === "TAG_NO" || attId === "TAG_DESC" || attId === "status") {
      return false;
    }

    if (templateType === 'SPREAD_LIST' || templateType === 'GRD_LIST') {
      // 각 행의 TAG_NO를 기준으로 편집 가능 여부 판단
      if (!rowData?.TAG_NO || !editableFieldsMap.has(rowData.TAG_NO)) {
        return false;
      }

      const rowEditableFields = editableFieldsMap.get(rowData.TAG_NO) || [];
      if (!rowEditableFields.includes(attId)) {
        return false;
      }

      if (rowData && (rowData.shi === "OUT" || rowData.shi === null)) {
        return false;
      }
      return true;
    }

    // SPREAD_ITEM의 경우 기존 로직 유지
    if (templateType === 'SPREAD_ITEM') {
      return editableFields.includes(attId);
    }

    return true;
  }, [templateType, columnsJSON, editableFieldsMap]); // editableFields 의존성 제거

  const editableFieldsCount = React.useMemo(() => {
    if (templateType === 'SPREAD_ITEM') {
      // SPREAD_ITEM의 경우 기존 로직 유지
      return cellMappings.filter(m => m.isEditable).length;
    }

    if (templateType === 'SPREAD_LIST' || templateType === 'GRD_LIST') {
      // 각 행별로 편집 가능한 필드 수를 계산
      let totalEditableCount = 0;

      tableData.forEach((rowData, rowIndex) => {
        cellMappings.forEach(mapping => {
          if (mapping.dataRowIndex === rowIndex) {
            if (isFieldEditable(mapping.attId, rowData)) {
              totalEditableCount++;
            }
          }
        });
      });

      return totalEditableCount;
    }

    return cellMappings.filter(m => m.isEditable).length;
  }, [cellMappings, templateType, tableData, isFieldEditable]);

  // 🚀 배치 처리 함수들
  const setBatchValues = React.useCallback((
    activeSheet: any,
    valuesToSet: Array<{ row: number, col: number, value: any }>
  ) => {
    console.log(`🚀 Setting ${valuesToSet.length} values in batch`);

    const columnGroups = new Map<number, Array<{ row: number, value: any }>>();

    valuesToSet.forEach(({ row, col, value }) => {
      if (!columnGroups.has(col)) {
        columnGroups.set(col, []);
      }
      columnGroups.get(col)!.push({ row, value });
    });

    columnGroups.forEach((values, col) => {
      values.sort((a, b) => a.row - b.row);

      let start = 0;
      while (start < values.length) {
        let end = start;
        while (end + 1 < values.length && values[end + 1].row === values[end].row + 1) {
          end++;
        }

        const rangeValues = values.slice(start, end + 1).map(v => v.value);
        const startRow = values[start].row;

        try {
          if (rangeValues.length === 1) {
            activeSheet.setValue(startRow, col, rangeValues[0]);
          } else {
            const dataArray = rangeValues.map(v => [v]);
            activeSheet.setArray(startRow, col, dataArray);
          }
        } catch (error) {
          for (let i = start; i <= end; i++) {
            try {
              activeSheet.setValue(values[i].row, col, values[i].value);
            } catch (cellError) {
              console.warn(`⚠️ Individual value setting failed [${values[i].row}, ${col}]:`, cellError);
            }
          }
        }

        start = end + 1;
      }
    });
  }, []);

  const createCellStyle = React.useCallback((activeSheet: any, row: number, col: number, isEditable: boolean) => {
    // 기존 스타일 가져오기 (없으면 새로 생성)
    const existingStyle = activeSheet.getStyle(row, col) || new GC.Spread.Sheets.Style();

    // backColor만 수정
    if (isEditable) {
      existingStyle.backColor = "#bbf7d0";
    } else {
      existingStyle.backColor = "#e5e7eb";
      // 읽기 전용일 때만 텍스트 색상 변경 (선택사항)
      existingStyle.foreColor = "#4b5563";
    }

    return existingStyle;
  }, []);

  const setBatchStyles = React.useCallback((
    activeSheet: any,
    stylesToSet: Array<{ row: number, col: number, isEditable: boolean }>
  ) => {
    console.log(`🎨 Setting ${stylesToSet.length} styles in batch`);

    // 🔧 개별 셀별로 스타일과 잠금 상태 설정 (편집 권한 보장)
    stylesToSet.forEach(({ row, col, isEditable }) => {
      try {
        const cell = activeSheet.getCell(row, col);
        const style = createCellStyle(activeSheet, row, col, isEditable);

        activeSheet.setStyle(row, col, style);
        cell.locked(!isEditable); // 편집 가능하면 잠금 해제

        // 🆕 편집 가능한 셀에 기본 텍스트 에디터 설정
        if (isEditable) {
          const textCellType = new GC.Spread.Sheets.CellTypes.Text();
          activeSheet.setCellType(row, col, textCellType);
        }
      } catch (error) {
        console.warn(`⚠️ Failed to set style for cell [${row}, ${col}]:`, error);
      }
    });
  }, [createCellStyle]);

  const parseCellAddress = (address: string): { row: number, col: number } | null => {
    if (!address || address.trim() === "") return null;

    const match = address.match(/^([A-Z]+)(\d+)$/);
    if (!match) return null;

    const [, colStr, rowStr] = match;

    let col = 0;
    for (let i = 0; i < colStr.length; i++) {
      col = col * 26 + (colStr.charCodeAt(i) - 65 + 1);
    }
    col -= 1;

    const row = parseInt(rowStr) - 1;
    return { row, col };
  };

  const getCellAddress = (row: number, col: number): string => {
    let colStr = '';
    let colNum = col;
    while (colNum >= 0) {
      colStr = String.fromCharCode((colNum % 26) + 65) + colStr;
      colNum = Math.floor(colNum / 26) - 1;
    }
    return colStr + (row + 1);
  };

  const validateCellValue = (value: any, columnType: ColumnType, options?: string[]): string | null => {
    if (value === undefined || value === null || value === "") {
      return null;
    }

    switch (columnType) {
      case "NUMBER":
        if (isNaN(Number(value))) {
          return "Value must be a valid number";
        }
        break;
      case "LIST":
        if (options && !options.includes(String(value))) {
          return `Value must be one of: ${options.join(", ")}`;
        }
        break;
      case "STRING":
        break;
      default:
        break;
    }

    return null;
  };

  const validateAllData = React.useCallback(() => {
    if (!currentSpread || !selectedTemplate) return [];

    const activeSheet = currentSpread.getActiveSheet();
    const errors: ValidationError[] = [];

    cellMappings.forEach(mapping => {
      const columnConfig = columnsJSON.find(col => col.key === mapping.attId);
      if (!columnConfig) return;

      const cellPos = parseCellAddress(mapping.cellAddress);
      if (!cellPos) return;

      const cellValue = activeSheet.getValue(cellPos.row, cellPos.col);
      const errorMessage = validateCellValue(cellValue, columnConfig.type, columnConfig.options);

      if (errorMessage) {
        errors.push({
          cellAddress: mapping.cellAddress,
          attId: mapping.attId,
          value: cellValue,
          expectedType: columnConfig.type,
          message: errorMessage
        });
      }
    });

    setValidationErrors(errors);
    return errors;
  }, [currentSpread, selectedTemplate, cellMappings, columnsJSON]);



  const setupOptimizedListValidation = React.useCallback((activeSheet: any, cellPos: { row: number, col: number }, options: string[], rowCount: number) => {
    try {
      console.log(`🎯 Setting up dropdown for ${rowCount} rows with options:`, options);

      const safeOptions = options
        .filter(opt => opt !== null && opt !== undefined && opt !== '')
        .map(opt => String(opt).trim())
        .filter(opt => opt.length > 0)
        .filter((opt, index, arr) => arr.indexOf(opt) === index)
        .slice(0, 20);

      if (safeOptions.length === 0) {
        console.warn(`⚠️ No valid options found, skipping`);
        return;
      }

      const optionsString = safeOptions.join(',');

      for (let i = 0; i < rowCount; i++) {
        try {
          const targetRow = cellPos.row + i;

          // 🔧 각 셀마다 새로운 ComboBox 인스턴스 생성
          const comboBoxCellType = new GC.Spread.Sheets.CellTypes.ComboBox();
          comboBoxCellType.items(safeOptions);
          comboBoxCellType.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);

          // 🔧 DataValidation 설정
          const cellValidator = GC.Spread.Sheets.DataValidation.createListValidator(optionsString);
          cellValidator.showInputMessage(false);
          cellValidator.showErrorMessage(false);

          // ComboBox와 Validator 적용
          activeSheet.setCellType(targetRow, cellPos.col, comboBoxCellType);
          activeSheet.setDataValidator(targetRow, cellPos.col, cellValidator);

          // 🚀 중요: 셀 잠금 해제 및 편집 가능 설정
          const cell = activeSheet.getCell(targetRow, cellPos.col);
          cell.locked(false);

          console.log(`✅ Dropdown applied to [${targetRow}, ${cellPos.col}] with ${safeOptions.length} options`);

        } catch (cellError) {
          console.warn(`⚠️ Failed to apply dropdown to row ${cellPos.row + i}:`, cellError);
        }
      }

      console.log(`✅ Dropdown setup completed for ${rowCount} cells`);

    } catch (error) {
      console.error('❌ Dropdown setup failed:', error);
    }
  }, []);

  const getSafeActiveSheet = React.useCallback((spread: any, functionName: string = 'unknown') => {
    if (!spread) return null;

    try {
      let activeSheet = spread.getActiveSheet();
      if (!activeSheet) {
        const sheetCount = spread.getSheetCount();
        if (sheetCount > 0) {
          activeSheet = spread.getSheet(0);
          if (activeSheet) {
            spread.setActiveSheetIndex(0);
          }
        }
      }
      return activeSheet;
    } catch (error) {
      console.error(`❌ Error getting activeSheet in ${functionName}:`, error);
      return null;
    }
  }, []);

  const ensureRowCapacity = React.useCallback((activeSheet: any, requiredRowCount: number) => {
    try {
      if (!activeSheet) return false;

      const currentRowCount = activeSheet.getRowCount();
      if (requiredRowCount > currentRowCount) {
        const newRowCount = requiredRowCount + 10;
        activeSheet.setRowCount(newRowCount);
      }
      return true;
    } catch (error) {
      console.error('❌ Error in ensureRowCapacity:', error);
      return false;
    }
  }, []);

  const ensureColumnCapacity = React.useCallback((activeSheet: any, requiredColumnCount: number) => {
    try {
      if (!activeSheet) return false;

      const currentColumnCount = activeSheet.getColumnCount();
      if (requiredColumnCount > currentColumnCount) {
        const newColumnCount = requiredColumnCount + 10;
        activeSheet.setColumnCount(newColumnCount);
      }
      return true;
    } catch (error) {
      console.error('❌ Error in ensureColumnCapacity:', error);
      return false;
    }
  }, []);

  const setOptimalColumnWidths = React.useCallback((activeSheet: any, columns: any[], startCol: number, tableData: any[]) => {
    columns.forEach((column, colIndex) => {
      const targetCol = startCol + colIndex;
      const optimalWidth = column.type === 'NUMBER' ? 100 : column.type === 'STRING' ? 150 : 120;
      activeSheet.setColumnWidth(targetCol, optimalWidth);
    });
  }, []);

  // 🚀 최적화된 GRD_LIST 생성
  // 🚀 최적화된 GRD_LIST 생성 (TAG_DESC 컬럼 틀고정 포함)
  const createGrdListTableOptimized = React.useCallback((activeSheet: any, template: TemplateItem) => {
    console.log('🚀 Creating optimized GRD_LIST table with TAG_DESC freeze');

    const visibleColumns = columnsJSON
      .filter(col => col.hidden !== true)
      .sort((a, b) => (a.seq ?? 999999) - (b.seq ?? 999999));

    if (visibleColumns.length === 0) return [];

    const startCol = 1;
    const dataStartRow = 1;
    const mappings: CellMapping[] = [];

    ensureColumnCapacity(activeSheet, startCol + visibleColumns.length);
    ensureRowCapacity(activeSheet, dataStartRow + tableData.length);

    // 🧊 TAG_DESC 컬럼 위치 찾기 (틀고정용)
    const tagDescColumnIndex = visibleColumns.findIndex(col => col.key === 'TAG_DESC');
    let freezeColumnCount = 0;

    if (tagDescColumnIndex !== -1) {
      // TAG_DESC 컬럼까지 포함해서 고정 (startCol + tagDescColumnIndex + 1)
      freezeColumnCount = startCol + tagDescColumnIndex + 1;
      console.log(`🧊 TAG_DESC found at column index ${tagDescColumnIndex}, freezing ${freezeColumnCount} columns`);
    } else {
      // TAG_DESC가 없으면 TAG_NO까지만 고정 (일반적으로 첫 번째 컬럼)
      const tagNoColumnIndex = visibleColumns.findIndex(col => col.key === 'TAG_NO');
      if (tagNoColumnIndex !== -1) {
        freezeColumnCount = startCol + tagNoColumnIndex + 1;
        console.log(`🧊 TAG_NO found at column index ${tagNoColumnIndex}, freezing ${freezeColumnCount} columns`);
      }
    }

    // 헤더 생성
    const headerStyle = new GC.Spread.Sheets.Style();
    headerStyle.backColor = "#3b82f6";
    headerStyle.foreColor = "#ffffff";
    headerStyle.font = "bold 12px Arial";
    headerStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center;

    visibleColumns.forEach((column, colIndex) => {
      const targetCol = startCol + colIndex;
      const cell = activeSheet.getCell(0, targetCol);
      cell.value(column.label);
      cell.locked(true);
      activeSheet.setStyle(0, targetCol, headerStyle);
    });

    // 🚀 데이터 배치 처리 준비
    const allValues: Array<{ row: number, col: number, value: any }> = [];
    const allStyles: Array<{ row: number, col: number, isEditable: boolean }> = [];

    // 🔧 편집 가능한 셀 정보 수집 (드롭다운용)
    const dropdownConfigs: Array<{
      startRow: number;
      col: number;
      rowCount: number;
      options: string[];
      editableRows: number[]; // 편집 가능한 행만 추적
    }> = [];

    visibleColumns.forEach((column, colIndex) => {
      const targetCol = startCol + colIndex;

      // 드롭다운 설정을 위한 편집 가능한 행 찾기
      if (column.type === "LIST" && column.options) {
        const editableRows: number[] = [];
        tableData.forEach((rowData, rowIndex) => {
          if (isFieldEditable(column.key, rowData)) { // rowData 전달
            editableRows.push(dataStartRow + rowIndex);
          }
        });

        if (editableRows.length > 0) {
          dropdownConfigs.push({
            startRow: dataStartRow,
            col: targetCol,
            rowCount: tableData.length,
            options: column.options,
            editableRows: editableRows
          });
        }
      }

      tableData.forEach((rowData, rowIndex) => {
        const targetRow = dataStartRow + rowIndex;
        const cellEditable = isFieldEditable(column.key, rowData); // rowData 전달
        const value = rowData[column.key];

        mappings.push({
          attId: column.key,
          cellAddress: getCellAddress(targetRow, targetCol),
          isEditable: cellEditable,
          dataRowIndex: rowIndex
        });

        allValues.push({
          row: targetRow,
          col: targetCol,
          value: value ?? null
        });

        allStyles.push({
          row: targetRow,
          col: targetCol,
          isEditable: cellEditable
        });
      });
    });

    // 🚀 배치로 값과 스타일 설정
    setBatchValues(activeSheet, allValues);
    setBatchStyles(activeSheet, allStyles);

    // 🎯 개선된 드롭다운 설정 (편집 가능한 셀에만)
    dropdownConfigs.forEach(({ col, options, editableRows }) => {
      try {
        console.log(`🎯 Setting dropdown for column ${col}, editable rows: ${editableRows.length}`);

        const safeOptions = options
          .filter(opt => opt !== null && opt !== undefined && opt !== '')
          .map(opt => String(opt).trim())
          .filter(opt => opt.length > 0)
          .slice(0, 20);

        if (safeOptions.length === 0) return;

        // 편집 가능한 행에만 드롭다운 적용
        editableRows.forEach(targetRow => {
          try {
            const comboBoxCellType = new GC.Spread.Sheets.CellTypes.ComboBox();
            comboBoxCellType.items(safeOptions);
            comboBoxCellType.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);

            const cellValidator = GC.Spread.Sheets.DataValidation.createListValidator(safeOptions.join(','));
            cellValidator.showInputMessage(false);
            cellValidator.showErrorMessage(false);

            activeSheet.setCellType(targetRow, col, comboBoxCellType);
            activeSheet.setDataValidator(targetRow, col, cellValidator);

            // 🚀 편집 권한 명시적 설정
            const cell = activeSheet.getCell(targetRow, col);
            cell.locked(false);

            console.log(`✅ Dropdown applied to editable cell [${targetRow}, ${col}]`);
          } catch (cellError) {
            console.warn(`⚠️ Failed to apply dropdown to [${targetRow}, ${col}]:`, cellError);
          }
        });
      } catch (error) {
        console.error(`❌ Dropdown config failed for column ${col}:`, error);
      }
    });

    // 🧊 틀고정 설정
    if (freezeColumnCount > 0) {
      try {
        activeSheet.frozenColumnCount(freezeColumnCount);
        activeSheet.frozenRowCount(1); // 헤더 행도 고정

        console.log(`🧊 Freeze applied: ${freezeColumnCount} columns, 1 row (header)`);

        // 🎨 고정된 컬럼에 특별한 스타일 추가 (선택사항)
        for (let col = 0; col < freezeColumnCount; col++) {
          for (let row = 0; row <= tableData.length; row++) {
            try {
              const currentStyle = activeSheet.getStyle(row, col) || new GC.Spread.Sheets.Style();

              if (row === 0) {
                // 헤더는 기존 스타일 유지
                continue;
              } else {
                // 데이터 셀에 고정 구분선 추가
                if (col === freezeColumnCount - 1) {
                  currentStyle.borderRight = new GC.Spread.Sheets.LineBorder("#2563eb", GC.Spread.Sheets.LineStyle.medium);
                  activeSheet.setStyle(row, col, currentStyle);
                }
              }
            } catch (styleError) {
              console.warn(`⚠️ Failed to apply freeze border style to [${row}, ${col}]:`, styleError);
            }
          }
        }
      } catch (freezeError) {
        console.error('❌ Failed to apply freeze:', freezeError);
      }
    }

    setOptimalColumnWidths(activeSheet, visibleColumns, startCol, tableData);

    console.log(`✅ Optimized GRD_LIST created with freeze:`);
    console.log(`   - Total mappings: ${mappings.length}`);
    console.log(`   - Editable cells: ${mappings.filter(m => m.isEditable).length}`);
    console.log(`   - Dropdown configs: ${dropdownConfigs.length}`);
    console.log(`   - Frozen columns: ${freezeColumnCount}`);

    return mappings;
  }, [tableData, columnsJSON, isFieldEditable, setBatchValues, setBatchStyles, ensureColumnCapacity, ensureRowCapacity, getCellAddress, setOptimalColumnWidths]);

  const setupSheetProtectionAndEvents = React.useCallback((activeSheet: any, mappings: CellMapping[]) => {
    console.log(`🛡️ Setting up protection and events for ${mappings.length} mappings`);

    // 🔧 시트 보호 완전 해제 후 편집 권한 설정
    activeSheet.options.isProtected = false;

    // 🔧 편집 가능한 셀들을 위한 강화된 설정
    mappings.forEach((mapping) => {
      const cellPos = parseCellAddress(mapping.cellAddress);
      if (!cellPos) return;

      try {
        const cell = activeSheet.getCell(cellPos.row, cellPos.col);
        const columnConfig = columnsJSON.find(col => col.key === mapping.attId);

        if (mapping.isEditable) {
          // 🚀 편집 가능한 셀 설정 강화
          cell.locked(false);

          if (columnConfig?.type === "LIST" && columnConfig.options) {
            // LIST 타입: 새 ComboBox 인스턴스 생성
            const comboBox = new GC.Spread.Sheets.CellTypes.ComboBox();
            comboBox.items(columnConfig.options);
            comboBox.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);
            activeSheet.setCellType(cellPos.row, cellPos.col, comboBox);

            // DataValidation도 추가
            const validator = GC.Spread.Sheets.DataValidation.createListValidator(columnConfig.options.join(','));
            activeSheet.setDataValidator(cellPos.row, cellPos.col, validator);
          } else if (columnConfig?.type === "NUMBER") {
            // NUMBER 타입: 숫자 입력 허용
            const textCellType = new GC.Spread.Sheets.CellTypes.Text();
            activeSheet.setCellType(cellPos.row, cellPos.col, textCellType);

            // 숫자 validation 추가 (에러 메시지 없이)
            const numberValidator = GC.Spread.Sheets.DataValidation.createNumberValidator(
              GC.Spread.Sheets.ConditionalFormatting.ComparisonOperators.between,
              -999999999, 999999999, true
            );
            numberValidator.showInputMessage(false);
            numberValidator.showErrorMessage(false);
            activeSheet.setDataValidator(cellPos.row, cellPos.col, numberValidator);
          } else {
            // 기본 TEXT 타입: 자유 텍스트 입력
            const textCellType = new GC.Spread.Sheets.CellTypes.Text();
            activeSheet.setCellType(cellPos.row, cellPos.col, textCellType);
          }

          // 편집 가능 스타일 재적용
          const editableStyle = createCellStyle(activeSheet, cellPos.row, cellPos.col, true);
          activeSheet.setStyle(cellPos.row, cellPos.col, editableStyle);

          console.log(`🔓 Cell [${cellPos.row}, ${cellPos.col}] ${mapping.attId} set as EDITABLE`);
        } else {
          // 읽기 전용 셀
          cell.locked(true);
          const readonlyStyle = createCellStyle(activeSheet, cellPos.row, cellPos.col, false);
          activeSheet.setStyle(cellPos.row, cellPos.col, readonlyStyle);
        }
      } catch (error) {
        console.error(`❌ Error processing cell ${mapping.cellAddress}:`, error);
      }
    });

    // 🛡️ 시트 보호 재설정 (편집 허용 모드로)
    activeSheet.options.isProtected = false;
    activeSheet.options.protectionOptions = {
      allowSelectLockedCells: true,
      allowSelectUnlockedCells: true,
      allowSort: false,
      allowFilter: false,
      allowEditObjects: true,        // ✅ 편집 객체 허용
      allowResizeRows: false,
      allowResizeColumns: false,
      allowFormatCells: false,
      allowInsertRows: false,
      allowInsertColumns: false,
      allowDeleteRows: false,
      allowDeleteColumns: false
    };

    // 🎯 변경 감지 이벤트
    const changeEvents = [
      GC.Spread.Sheets.Events.CellChanged,
      GC.Spread.Sheets.Events.ValueChanged,
      GC.Spread.Sheets.Events.ClipboardPasted
    ];

    changeEvents.forEach(eventType => {
      activeSheet.bind(eventType, () => {
        console.log(`📝 ${eventType} detected`);
        setHasChanges(true);
      });
    });

    // 🚫 편집 시작 권한 확인
    activeSheet.bind(GC.Spread.Sheets.Events.EditStarting, (event: any, info: any) => {
      console.log(`🎯 EditStarting: Row ${info.row}, Col ${info.col}`);

      const exactMapping = mappings.find(m => {
        const cellPos = parseCellAddress(m.cellAddress);
        return cellPos && cellPos.row === info.row && cellPos.col === info.col;
      });

      if (!exactMapping) {
        console.log(`ℹ️ No mapping found for [${info.row}, ${info.col}] - allowing edit`);
        return; // 매핑이 없으면 허용
      }

      console.log(`📋 Found mapping: ${exactMapping.attId}, isEditable: ${exactMapping.isEditable}`);

      if (!exactMapping.isEditable) {
        console.log(`🚫 Field ${exactMapping.attId} is not editable`);
        toast.warning(`${exactMapping.attId} field is read-only`);
        info.cancel = true;
        return;
      }

      // SPREAD_LIST/GRD_LIST 개별 행 SHI 확인
      if ((templateType === 'SPREAD_LIST' || templateType === 'GRD_LIST') && exactMapping.dataRowIndex !== undefined) {
        const dataRowIndex = exactMapping.dataRowIndex;
        if (dataRowIndex >= 0 && dataRowIndex < tableData.length) {
          const rowData = tableData[dataRowIndex];
          if (rowData?.shi === "OUT" || rowData?.shi === null) {
            console.log(`🚫 Row ${dataRowIndex} is in SHI mode`);
            toast.warning(`Row ${dataRowIndex + 1}: ${exactMapping.attId} field is read-only (SHI mode)`);
            info.cancel = true;
            return;
          }
        }
      }

      console.log(`✅ Edit allowed for ${exactMapping.attId}`);
    });

    // ✅ 편집 완료 검증
    activeSheet.bind(GC.Spread.Sheets.Events.EditEnded, (event: any, info: any) => {
      console.log(`🏁 EditEnded: Row ${info.row}, Col ${info.col}, New value: ${activeSheet.getValue(info.row, info.col)}`);

      const exactMapping = mappings.find(m => {
        const cellPos = parseCellAddress(m.cellAddress);
        return cellPos && cellPos.row === info.row && cellPos.col === info.col;
      });

      if (!exactMapping) return;

      const columnConfig = columnsJSON.find(col => col.key === exactMapping.attId);
      if (columnConfig) {
        const cellValue = activeSheet.getValue(info.row, info.col);
        const errorMessage = validateCellValue(cellValue, columnConfig.type, columnConfig.options);
        const cell = activeSheet.getCell(info.row, info.col);

        if (errorMessage) {
          // 🚨 에러 스타일 적용
          const errorStyle = new GC.Spread.Sheets.Style();
          errorStyle.backColor = "#fef2f2";
          errorStyle.foreColor = "#dc2626";
          errorStyle.borderLeft = new GC.Spread.Sheets.LineBorder("#dc2626", GC.Spread.Sheets.LineStyle.thick);
          errorStyle.borderRight = new GC.Spread.Sheets.LineBorder("#dc2626", GC.Spread.Sheets.LineStyle.thick);
          errorStyle.borderTop = new GC.Spread.Sheets.LineBorder("#dc2626", GC.Spread.Sheets.LineStyle.thick);
          errorStyle.borderBottom = new GC.Spread.Sheets.LineBorder("#dc2626", GC.Spread.Sheets.LineStyle.thick);

          activeSheet.setStyle(info.row, info.col, errorStyle);
          cell.locked(!exactMapping.isEditable); // 편집 가능 상태 유지
          toast.warning(`Invalid value in ${exactMapping.attId}: ${errorMessage}`, { duration: 5000 });
        } else {
          // ✅ 정상 스타일 복원
          const normalStyle = createCellStyle(activeSheet, info.row, info.col, exactMapping.isEditable);
          activeSheet.setStyle(info.row, info.col, normalStyle);
          cell.locked(!exactMapping.isEditable);
        }
      }

      setHasChanges(true);
    });

    console.log(`🛡️ Protection configured. Editable cells: ${mappings.filter(m => m.isEditable).length}`);
  }, [templateType, tableData, createCellStyle, validateCellValue, columnsJSON]);

  // 🚀 최적화된 initSpread
  const initSpread = React.useCallback(async (spread: any, template?: TemplateItem) => {
    const workingTemplate = template || selectedTemplate;
    if (!spread || !workingTemplate) {
      console.error('❌ Invalid spread or template');
      return;
    }

    try {
      console.log('🚀 Starting optimized spread initialization...');
      setIsInitializing(true);
      updateProgress('Initializing...', 0, 100);

      setCurrentSpread(spread);
      setHasChanges(false);
      setValidationErrors([]);

      // 🚀 핵심 최적화: 모든 렌더링과 이벤트 중단
      spread.suspendPaint();
      spread.suspendEvent();
      spread.suspendCalcService();

      updateProgress('Setting up workspace...', 10, 100);

      try {
        let activeSheet = getSafeActiveSheet(spread, 'initSpread');
        if (!activeSheet) {
          throw new Error('Failed to get initial activeSheet');
        }

        activeSheet.options.isProtected = false;
        let mappings: CellMapping[] = [];

        if (templateType === 'GRD_LIST') {
          updateProgress('Creating dynamic table...', 20, 100);

          spread.clearSheets();
          spread.addSheet(0);
          const sheet = spread.getSheet(0);
          sheet.name('Data');
          spread.setActiveSheet('Data');

          updateProgress('Processing table data...', 50, 100);
          mappings = createGrdListTableOptimized(sheet, workingTemplate);

        } else {
          updateProgress('Loading template structure...', 20, 100);

          let contentJson = workingTemplate.SPR_LST_SETUP?.CONTENT || workingTemplate.SPR_ITM_LST_SETUP?.CONTENT;
          let dataSheets = workingTemplate.SPR_LST_SETUP?.DATA_SHEETS || workingTemplate.SPR_ITM_LST_SETUP?.DATA_SHEETS;

          if (!contentJson || !dataSheets) {
            throw new Error(`No template content found for ${workingTemplate.NAME}`);
          }

          const jsonData = typeof contentJson === 'string' ? JSON.parse(contentJson) : contentJson;

          updateProgress('Loading template layout...', 40, 100);
          spread.fromJSON(jsonData);

          activeSheet = getSafeActiveSheet(spread, 'after-fromJSON');
          if (!activeSheet) {
            throw new Error('ActiveSheet became null after loading template');
          }

          activeSheet.options.isProtected = false;

          if (templateType === 'SPREAD_LIST' && tableData.length > 0) {
            updateProgress('Processing data rows...', 60, 100);

            const activeSheetName = workingTemplate.SPR_LST_SETUP?.ACT_SHEET;


            // 🔧 각 DATA_SHEET별로 처리
            dataSheets.forEach(dataSheet => {
              const sheetName = dataSheet.SHEET_NAME;

              // 해당 시트가 존재하는지 확인
              const targetSheet = spread.getSheetFromName(sheetName);
              if (!targetSheet) {
                console.warn(`⚠️ Sheet '${sheetName}' not found in template`);
                return;
              }

              console.log(`📋 Processing sheet: ${sheetName}`);

              // 해당 시트로 전환
              spread.setActiveSheet(sheetName);
              const currentSheet = spread.getActiveSheet();

              if (dataSheet.MAP_CELL_ATT && dataSheet.MAP_CELL_ATT.length > 0) {
                dataSheet.MAP_CELL_ATT.forEach((mapping: any) => {
                  const { ATT_ID, IN } = mapping;
                  if (!ATT_ID || !IN || IN.trim() === "") return;

                  const cellPos = parseCellAddress(IN);
                  if (!cellPos) return;

                  const requiredRows = cellPos.row + tableData.length;
                  if (!ensureRowCapacity(currentSheet, requiredRows)) return;

                  // 🚀 배치 데이터 준비
                  const valuesToSet: Array<{ row: number, col: number, value: any }> = [];
                  const stylesToSet: Array<{ row: number, col: number, isEditable: boolean }> = [];

                  tableData.forEach((rowData, index) => {
                    const targetRow = cellPos.row + index;
                    const cellEditable = isFieldEditable(ATT_ID, rowData);
                    const value = rowData[ATT_ID];

                    mappings.push({
                      attId: ATT_ID,
                      cellAddress: getCellAddress(targetRow, cellPos.col),
                      isEditable: cellEditable,
                      dataRowIndex: index
                    });

                    valuesToSet.push({
                      row: targetRow,
                      col: cellPos.col,
                      value: value ?? null
                    });

                    stylesToSet.push({
                      row: targetRow,
                      col: cellPos.col,
                      isEditable: cellEditable
                    });
                  });

                  // 🚀 배치 처리
                  setBatchValues(currentSheet, valuesToSet);
                  setBatchStyles(currentSheet, stylesToSet);

                  // 드롭다운 설정
                  const columnConfig = columnsJSON.find(col => col.key === ATT_ID);
                  if (columnConfig?.type === "LIST" && columnConfig.options) {
                    const hasEditableRows = tableData.some((rowData) => isFieldEditable(ATT_ID, rowData));
                    if (hasEditableRows) {
                      setupOptimizedListValidation(currentSheet, cellPos, columnConfig.options, tableData.length);
                    }
                  }
                });
              }
            });

            // 🔧 마지막에 activeSheetName으로 다시 전환
            if (activeSheetName && spread.getSheetFromName(activeSheetName)) {
              spread.setActiveSheet(activeSheetName);
              activeSheet = spread.getActiveSheet();
            }

          } else if (templateType === 'SPREAD_ITEM' && selectedRow) {
            updateProgress('Setting up form fields...', 60, 100);
            const activeSheetName = workingTemplate.SPR_ITM_LST_SETUP?.ACT_SHEET;

            if (activeSheetName && spread.getSheetFromName(activeSheetName)) {
              spread.setActiveSheet(activeSheetName);
            }

            dataSheets.forEach(dataSheet => {

              const sheetName = dataSheet.SHEET_NAME;
              // 해당 시트가 존재하는지 확인
              const targetSheet = spread.getSheetFromName(sheetName);
              if (!targetSheet) {
                console.warn(`⚠️ Sheet '${sheetName}' not found in template`);
                return;
              }

              console.log(`📋 Processing sheet: ${sheetName}`);

              // 해당 시트로 전환
              spread.setActiveSheet(sheetName);
              const currentSheet = spread.getActiveSheet();


              dataSheet.MAP_CELL_ATT?.forEach((mapping: any) => {
                const { ATT_ID, IN } = mapping;
                const cellPos = parseCellAddress(IN);


                if (cellPos) {
                  const isEditable = isFieldEditable(ATT_ID);
                  const value = selectedRow[ATT_ID];

                  mappings.push({
                    attId: ATT_ID,
                    cellAddress: IN,
                    isEditable: isEditable,
                    dataRowIndex: 0
                  });

                  const cell = currentSheet.getCell(cellPos.row, cellPos.col);
                  cell.value(value ?? null);

                  const style = createCellStyle(currentSheet, cellPos.row, cellPos.col, isEditable);
                  currentSheet.setStyle(cellPos.row, cellPos.col, style);

                  const columnConfig = columnsJSON.find(col => col.key === ATT_ID);
                  if (columnConfig?.type === "LIST" && columnConfig.options && isEditable) {
                    setupOptimizedListValidation(currentSheet, cellPos, columnConfig.options, 1);
                  }
                }
              });

              // 🔧 마지막에 activeSheetName으로 다시 전환
              if (activeSheetName && spread.getSheetFromName(activeSheetName)) {
                spread.setActiveSheet(activeSheetName);
                activeSheet = spread.getActiveSheet();
              }


            });
          }
        }

        updateProgress('Configuring interactions...', 90, 100);
        setCellMappings(mappings);

        const finalActiveSheet = getSafeActiveSheet(spread, 'setupEvents');
        if (finalActiveSheet) {
          setupSheetProtectionAndEvents(finalActiveSheet, mappings);
        }

        updateProgress('Finalizing...', 100, 100);
        console.log(`✅ Optimized initialization completed with ${mappings.length} mappings`);

      } finally {
        // 🚀 올바른 순서로 재개
        spread.resumeCalcService();
        spread.resumeEvent();
        spread.resumePaint();
      }

    } catch (error) {
      console.error('❌ Error in optimized spread initialization:', error);
      if (spread?.resumeCalcService) spread.resumeCalcService();
      if (spread?.resumeEvent) spread.resumeEvent();
      if (spread?.resumePaint) spread.resumePaint();
      toast.error(`Template loading failed: ${error.message}`);
    } finally {
      setIsInitializing(false);
      setLoadingProgress(null);
    }
  }, [selectedTemplate, templateType, selectedRow, tableData, updateProgress, getSafeActiveSheet, createGrdListTableOptimized, setBatchValues, setBatchStyles, setupSheetProtectionAndEvents, setCellMappings, createCellStyle, isFieldEditable, columnsJSON, setupOptimizedListValidation, parseCellAddress, ensureRowCapacity, getCellAddress]);

  React.useEffect(() => {
    // 🔍 안전성 검증: availableTemplates가 있고, selectedTemplateId가 없을 때만 실행
    if (!selectedTemplateId && availableTemplates.length > 0) {
      const only = availableTemplates[0];
      
      // 🔍 TMPL_ID 검증
      if (!only || !only.TMPL_ID) {
        console.error('❌ First template has no TMPL_ID:', only);
        return;
      }

      const type = determineTemplateType(only);
      
      // 🔍 type이 null이 아닐 때만 진행
      if (!type) {
        console.warn('⚠️ Could not determine template type for:', only);
        return;
      }
  
      // 선택되어 있지 않다면 자동 선택
      setSelectedTemplateId(only.TMPL_ID);
      setTemplateType(type);
  
      // 이미 스프레드가 마운트되어 있다면 즉시 초기화
      if (currentSpread) {
        initSpread(currentSpread, only);
      }
    }
  }, [
    availableTemplates,
    selectedTemplateId,
    currentSpread,
    determineTemplateType,
    initSpread
  ]);

  const handleSaveChanges = React.useCallback(async () => {
    if (!currentSpread || !hasChanges) {
      toast.info("No changes to save");
      return;
    }

    const errors = validateAllData();
    if (errors.length > 0) {
      toast.error(`Cannot save: ${errors.length} validation errors found. Please fix them first.`);
      return;
    }

    try {
      setIsPending(true);
      const activeSheet = currentSpread.getActiveSheet();

      if (templateType === 'SPREAD_ITEM' && selectedRow) {
        const dataToSave = { ...selectedRow };

        cellMappings.forEach(mapping => {
          if (mapping.isEditable) {
            const cellPos = parseCellAddress(mapping.cellAddress);
            if (cellPos) {
              const cellValue = activeSheet.getValue(cellPos.row, cellPos.col);
              dataToSave[mapping.attId] = cellValue;
            }
          }
        });

        dataToSave.TAG_NO = selectedRow.TAG_NO;

        const { success, message } = await updateFormDataInDB(
          formCode,
          contractItemId,
          dataToSave
        );

        if (!success) {
          toast.error(message);
          return;
        }

        toast.success("Changes saved successfully!");
        onUpdateSuccess?.(dataToSave);

      } else if ((templateType === 'SPREAD_LIST' || templateType === 'GRD_LIST') && tableData.length > 0) {
        console.log('🔍 Starting batch save process...');

        const updatedRows: GenericData[] = [];
        let saveCount = 0;
        let checkedCount = 0;

        for (let i = 0; i < tableData.length; i++) {
          const originalRow = tableData[i];
          const dataToSave = { ...originalRow };
          let hasRowChanges = false;

          console.log(`🔍 Processing row ${i} (TAG_NO: ${originalRow.TAG_NO})`);

          cellMappings.forEach(mapping => {
            if (mapping.dataRowIndex === i && mapping.isEditable) {
              checkedCount++;

              // 🔧 isFieldEditable과 동일한 로직 사용
              const rowData = tableData[i];
              const fieldEditable = isFieldEditable(mapping.attId, rowData);

              console.log(`  📝 Field ${mapping.attId}: fieldEditable=${fieldEditable}, mapping.isEditable=${mapping.isEditable}`);

              if (fieldEditable) {
                const cellPos = parseCellAddress(mapping.cellAddress);
                if (cellPos) {
                  const cellValue = activeSheet.getValue(cellPos.row, cellPos.col);
                  const originalValue = originalRow[mapping.attId];

                  // 🔧 개선된 값 비교 (타입 변환 및 null/undefined 처리)
                  const normalizedCellValue = cellValue === null || cellValue === undefined ? "" : String(cellValue).trim();
                  const normalizedOriginalValue = originalValue === null || originalValue === undefined ? "" : String(originalValue).trim();

                  console.log(`    🔍 ${mapping.attId}: "${normalizedOriginalValue}" -> "${normalizedCellValue}"`);

                  if (normalizedCellValue !== normalizedOriginalValue) {
                    dataToSave[mapping.attId] = cellValue;
                    hasRowChanges = true;
                    console.log(`    ✅ Change detected for ${mapping.attId}`);
                  }
                }
              }
            }
          });

          if (hasRowChanges) {
            console.log(`💾 Saving row ${i} with changes`);
            dataToSave.TAG_NO = originalRow.TAG_NO;

            try {
              const { success, message } = await updateFormDataInDB(
                formCode,
                contractItemId,
                dataToSave
              );

              if (success) {
                updatedRows.push(dataToSave);
                saveCount++;
                console.log(`✅ Row ${i} saved successfully`);
              } else {
                console.error(`❌ Failed to save row ${i}: ${message}`);
                toast.error(`Failed to save row ${i + 1}: ${message}`);
                updatedRows.push(originalRow); // 원본 데이터 유지
              }
            } catch (error) {
              console.error(`❌ Error saving row ${i}:`, error);
              toast.error(`Error saving row ${i + 1}`);
              updatedRows.push(originalRow); // 원본 데이터 유지
            }
          } else {
            updatedRows.push(originalRow);
            console.log(`ℹ️ No changes in row ${i}`);
          }
        }

        console.log(`📊 Save summary: ${saveCount} saved, ${checkedCount} fields checked`);

        if (saveCount > 0) {
          toast.success(`${saveCount} rows saved successfully!`);
          onUpdateSuccess?.(updatedRows);
        } else {
          console.warn(`⚠️ No changes detected despite hasChanges=${hasChanges}`);
          toast.warning("No actual changes were found to save. Please check if the values were properly edited.");
        }
      }

      setHasChanges(false);
      setValidationErrors([]);

    } catch (error) {
      console.error("Error saving changes:", error);
      toast.error("An unexpected error occurred while saving");
    } finally {
      setIsPending(false);
    }
  }, [
    currentSpread,
    hasChanges,
    templateType,
    selectedRow,
    tableData,
    formCode,
    contractItemId,
    onUpdateSuccess,
    cellMappings,
    columnsJSON,
    validateAllData,
    isFieldEditable  // 🔧 의존성 추가
  ]);

  if (!isOpen) return null;

  const isDataValid = templateType === 'SPREAD_ITEM' ? !!selectedRow : tableData.length > 0;
  const dataCount = templateType === 'SPREAD_ITEM' ? 1 : tableData.length;



  return (
    <Dialog open={isOpen} onOpenChange={onClose}>
      <DialogContent
        className="w-[95vw] max-w-[95vw] h-[90vh] flex flex-col fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-50"
      >
        <DialogHeader className="flex-shrink-0">
          <DialogTitle>SEDP Template - {formCode}</DialogTitle>
          <DialogDescription>
            <div className="space-y-3">
              {availableTemplates.length > 1 ? (
                // 🔍 템플릿이 2개 이상일 때: Select 박스 표시
                <div className="flex items-center gap-4">
                  <span className="text-sm font-medium">Template:</span>
                  <Select value={selectedTemplateId} onValueChange={handleTemplateChange}>
                    <SelectTrigger className="w-64">
                      <SelectValue placeholder="Select a template" />
                    </SelectTrigger>
                    <SelectContent>
                      {availableTemplates
                        .filter(template => template?.TMPL_ID) // 🔍 TMPL_ID가 있는 것만 표시
                        .map(template => (
                          <SelectItem key={template.TMPL_ID} value={template.TMPL_ID}>
                            {template.NAME || 'Unnamed'} ({template.TMPL_TYPE || 'Unknown'})
                          </SelectItem>
                        ))}
                    </SelectContent>
                  </Select>
                </div>
              ) : availableTemplates.length === 1 ? (
                // 🔍 템플릿이 정확히 1개일 때: 템플릿 이름을 텍스트로 표시
                <div className="flex items-center gap-4">
                  <span className="text-sm font-medium">Template:</span>
                  <span className="text-sm text-blue-600 font-medium">
                    {availableTemplates[0]?.NAME || 'Unnamed'} ({availableTemplates[0]?.TMPL_TYPE || 'Unknown'})
                  </span>
                </div>
              ) : null}  

              {selectedTemplate && (
                <div className="flex items-center gap-4 text-sm">
                  <span className="font-medium text-blue-600">
                    Template Type: {
                      templateType === 'SPREAD_LIST' ? 'List View (SPREAD_LIST)' :
                        templateType === 'SPREAD_ITEM' ? 'Item View (SPREAD_ITEM)' :
                          'Grid List View (GRD_LIST)'
                    }
                  </span>
                  {templateType === 'SPREAD_ITEM' && selectedRow && (
                    <span>• Selected TAG_NO: {selectedRow.TAG_NO || 'N/A'}</span>
                  )}
                  {(templateType === 'SPREAD_LIST' || templateType === 'GRD_LIST') && (
                    <span>• {dataCount} rows</span>
                  )}
                  {hasChanges && (
                    <span className="text-orange-600 font-medium">
                      • Unsaved changes
                    </span>
                  )}
                  {validationErrors.length > 0 && (
                    <span className="text-red-600 font-medium flex items-center">
                      <AlertTriangle className="w-4 h-4 mr-1" />
                      {validationErrors.length} validation errors
                    </span>
                  )}
                </div>
              )}

              <div className="flex items-center gap-4 text-xs">
                <span className="text-muted-foreground">
                  <span className="inline-block w-3 h-3 bg-green-100 border border-green-400 mr-1"></span>
                  Editable fields
                </span>
                <span className="text-muted-foreground">
                  <span className="inline-block w-3 h-3 bg-gray-100 border border-gray-300 mr-1"></span>
                  Read-only fields
                </span>
                <span className="text-muted-foreground">
                  <span className="inline-block w-3 h-3 bg-red-100 border border-red-300 mr-1"></span>
                  Validation errors
                </span>
                {cellMappings.length > 0 && (
                  <span className="text-blue-600">
                    {editableFieldsCount} of {cellMappings.length} fields editable
                  </span>
                )}
              </div>
            </div>
          </DialogDescription>
        </DialogHeader>

        <div className="flex-1 overflow-hidden relative">
          {/* 🆕 로딩 프로그레스 오버레이 */}
          <LoadingProgress
            phase={loadingProgress?.phase || ''}
            progress={loadingProgress?.progress || 0}
            total={loadingProgress?.total || 100}
            isVisible={isInitializing && !!loadingProgress}
          />

          {selectedTemplate && isClient && isDataValid ? (
            <SpreadSheets
              key={`${templateType}-${selectedTemplate?.TMPL_ID || 'unknown'}-${selectedTemplateId}`}
              workbookInitialized={initSpread}
              hostStyle={hostStyle}
            />
          ) : (
            <div className="flex items-center justify-center h-full text-muted-foreground">
              {!isClient ? (
                <>
                  <Loader className="mr-2 h-4 w-4 animate-spin" />
                  Loading...
                </>
              ) : !selectedTemplate ? (
                "No template available"
              ) : !isDataValid ? (
                `No ${templateType === 'SPREAD_ITEM' ? 'selected row' : 'data'} available`
              ) : (
                "Template not ready"
              )}
            </div>
          )}
        </div>

        <DialogFooter className="flex-shrink-0">
          <div className="flex items-center gap-2">
            <Button variant="outline" onClick={onClose}>
              Close
            </Button>

            {hasChanges && (
              <Button
                variant="default"
                onClick={handleSaveChanges}
                disabled={isPending || validationErrors.length > 0}
              >
                {isPending ? (
                  <>
                    <Loader className="mr-2 h-4 w-4 animate-spin" />
                    Saving...
                  </>
                ) : (
                  <>
                    <Save className="mr-2 h-4 w-4" />
                    Save Changes
                  </>
                )}
              </Button>
            )}

            {validationErrors.length > 0 && (
              <Button
                variant="outline"
                onClick={validateAllData}
                className="text-red-600 border-red-300 hover:bg-red-50"
              >
                <AlertTriangle className="mr-2 h-4 w-4" />
                Check Errors ({validationErrors.length})
              </Button>
            )}
          </div>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}