import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
import { toast } from "sonner";
import { DataTableColumnJSON } from "./form-data-table-columns";
import { updateFormDataBatchInDB } from "@/lib/forms/services";
import { decryptWithServerAction } from "../drm/drmUtils";

// Define error structure for import
export interface ImportError {
  tagNo: string;
  rowIndex: number;
  columnKey: string;
  columnLabel: string;
  errorType: string;
  errorMessage: string;
  currentValue?: any;
  expectedFormat?: string;
}

// Updated options interface with editableFieldsMap
export interface ImportExcelOptions {
  file: File;
  tableData: GenericData[];
  columnsJSON: DataTableColumnJSON[];
  formCode?: string;
  contractItemId?: number;
  editableFieldsMap?: Map<string, string[]>; // 새로 추가
  onPendingChange?: (isPending: boolean) => void;
  onDataUpdate?: (updater: ((prev: GenericData[]) => GenericData[]) | GenericData[]) => void;
}

export interface ImportExcelResult {
  success: boolean;
  importedCount?: number;
  error?: any;
  message?: string;
  skippedFields?: { tagNo: string, fields: string[] }[]; // 건너뛴 필드 정보
  errorCount?: number;
  hasErrors?: boolean;
  notFoundTags?: string[];
}

export interface ExportExcelOptions {
  tableData: GenericData[];
  columnsJSON: DataTableColumnJSON[];
  formCode: string;
  editableFieldsMap?: Map<string, string[]>; // 새로 추가
  onPendingChange?: (isPending: boolean) => void;
}

interface GenericData {
  [key: string]: any;
}

/**
 * Check if a field is editable for a specific TAG_NO
 */
function isFieldEditable(
  column: DataTableColumnJSON,
  tagNo: string,
  editableFieldsMap: Map<string, string[]>
): boolean {
  // SHI-only fields (shi === "OUT" or shi === null) are never editable
  if (column.shi === "OUT" || column.shi === null) return false;

  // System fields are never editable
  if (column.key === "TAG_NO" || column.key === "TAG_DESC" || column.key === "status") return false;

  // If no editableFieldsMap provided, assume all non-SHI fields are editable
  if (!editableFieldsMap || editableFieldsMap.size === 0) return true;

  // If TAG_NO not in map, no fields are editable
  if (!editableFieldsMap.has(tagNo)) return false;

  // Check if this field is in the editable fields list for this TAG_NO
  const editableFields = editableFieldsMap.get(tagNo) || [];
  return editableFields.includes(column.key);
}

/**
 * Create error sheet with import validation results
 */
function createImportErrorSheet(workbook: ExcelJS.Workbook, errors: ImportError[], headerErrors?: string[]) {

  const existingErrorSheet = workbook.getWorksheet("Import_Errors");
  if (existingErrorSheet) {
    workbook.removeWorksheet("Import_Errors");
  }

  const errorSheet = workbook.addWorksheet("Import_Errors");

  // Add header error section if exists
  if (headerErrors && headerErrors.length > 0) {
    errorSheet.addRow(["HEADER VALIDATION ERRORS"]);
    const headerErrorTitleRow = errorSheet.getRow(1);
    headerErrorTitleRow.font = { bold: true, size: 14, color: { argb: "FFFFFFFF" } };
    headerErrorTitleRow.getCell(1).fill = {
      type: "pattern",
      pattern: "solid",
      fgColor: { argb: "FFDC143C" },
    };

    headerErrors.forEach((error, index) => {
      const errorRow = errorSheet.addRow([`${index + 1}. ${error}`]);
      errorRow.getCell(1).fill = {
        type: "pattern",
        pattern: "solid",
        fgColor: { argb: "FFFFCCCC" },
      };
    });

    errorSheet.addRow([]); // Empty row for separation
  }

  // Data validation errors section
  const startRow = errorSheet.rowCount + 1;

  // Summary row
  errorSheet.addRow([`DATA VALIDATION ERRORS: ${errors.length} errors found`]);
  const summaryRow = errorSheet.getRow(startRow);
  summaryRow.font = { bold: true, size: 12 };
  if (errors.length > 0) {
    summaryRow.getCell(1).fill = {
      type: "pattern",
      pattern: "solid",
      fgColor: { argb: "FFFFC0C0" },
    };
  }

  if (errors.length > 0) {
    // Error data headers
    const errorHeaders = [
      "TAG NO",
      "Row Number",
      "Column",
      "Error Type",
      "Error Message",
      "Current Value",
      "Expected Format",
    ];

    errorSheet.addRow(errorHeaders);
    const headerRow = errorSheet.getRow(errorSheet.rowCount);
    headerRow.font = { bold: true, color: { argb: "FFFFFFFF" } };
    headerRow.alignment = { horizontal: "center" };

    headerRow.eachCell((cell) => {
      cell.fill = {
        type: "pattern",
        pattern: "solid",
        fgColor: { argb: "FFDC143C" },
      };
    });

    // Add error data
    errors.forEach((error) => {
      const errorRow = errorSheet.addRow([
        error.tagNo,
        error.rowIndex,
        error.columnLabel,
        error.errorType,
        error.errorMessage,
        error.currentValue || "",
        error.expectedFormat || "",
      ]);

      // Color code by error type
      errorRow.eachCell((cell) => {
        let bgColor = "FFFFFFFF"; // Default white

        switch (error.errorType) {
          case "MISSING_TAG_NO":
            bgColor = "FFFFCCCC"; // Light red
            break;
          case "TAG_NOT_FOUND":
            bgColor = "FFFFDDDD"; // Very light red
            break;
          case "TYPE_MISMATCH":
            bgColor = "FFFFEECC"; // Light orange
            break;
          case "INVALID_OPTION":
            bgColor = "FFFFFFE0"; // Light yellow
            break;
          case "HEADER_MISMATCH":
            bgColor = "FFFFE0E0"; // Very light red
            break;
          case "READ_ONLY_FIELD":
            bgColor = "FFF0F0F0"; // Light gray
            break;
        }

        cell.fill = {
          type: "pattern",
          pattern: "solid",
          fgColor: { argb: bgColor },
        };
      });
    });
  }

  // Auto-fit columns
  errorSheet.columns.forEach((column) => {
    let maxLength = 0;
    column.eachCell({ includeEmpty: false }, (cell) => {
      const columnLength = String(cell.value).length;
      if (columnLength > maxLength) {
        maxLength = columnLength;
      }
    });
    column.width = Math.min(Math.max(maxLength + 2, 10), 50);
  });

  return errorSheet;
}

export async function importExcelData({
  file,
  tableData,
  columnsJSON,
  formCode,
  contractItemId,
  editableFieldsMap = new Map(), // 새로 추가
  onPendingChange,
  onDataUpdate
}: ImportExcelOptions): Promise<ImportExcelResult> {
  if (!file) return { success: false, error: "No file provided" };

  try {
    if (onPendingChange) onPendingChange(true);

    // Get existing tag numbers and create a map for quick lookup
    const existingTagNumbers = new Set(tableData.map((d) => d.TAG_NO));
    const existingDataMap = new Map<string, GenericData>();
    tableData.forEach(item => {
      if (item.TAG_NO) {
        existingDataMap.set(item.TAG_NO, item);
      }
    });

    const workbook = new ExcelJS.Workbook();
    const arrayBuffer = await decryptWithServerAction(file);
    await workbook.xlsx.load(arrayBuffer);

    const worksheet = workbook.worksheets[0];

    // Parse headers
    const headerRow = worksheet.getRow(1);
    const headerRowValues = headerRow.values as ExcelJS.CellValue[];

    console.log("Original headers:", headerRowValues);

    // Create mappings between Excel headers and column definitions
    const headerToIndexMap = new Map<string, number>();
    for (let i = 1; i < headerRowValues.length; i++) {
      const headerValue = String(headerRowValues[i] || "").trim();
      if (headerValue) {
        headerToIndexMap.set(headerValue, i);
      }
    }

    // Validate headers
    const headerErrors: string[] = [];

    // Check for missing required columns
    columnsJSON.forEach((col) => {
      const label = col.label;
      if (!headerToIndexMap.has(label)) {
        headerErrors.push(`Column "${label}" is missing from Excel file`);
      }
    });

    // Check for unexpected columns
    headerToIndexMap.forEach((index, headerLabel) => {
      const found = columnsJSON.some((col) => col.label === headerLabel);
      if (!found) {
        headerErrors.push(`Unexpected column "${headerLabel}" found in Excel file`);
      }
    });

    // If header validation fails, create error report and exit
    if (headerErrors.length > 0) {
      createImportErrorSheet(workbook, [], headerErrors);

      const outBuffer = await workbook.xlsx.writeBuffer();
      saveAs(new Blob([outBuffer]), `import-error-report_${Date.now()}.xlsx`);

      toast.error(`Header validation failed. ${headerErrors.length} errors found. Check downloaded error report.`);
      return {
        success: false,
        error: "Header validation errors",
        errorCount: headerErrors.length,
        hasErrors: true
      };
    }

    // Create column key to Excel index mapping
    const keyToIndexMap = new Map<string, number>();
    columnsJSON.forEach((col) => {
      const index = headerToIndexMap.get(col.label);
      if (index !== undefined) {
        keyToIndexMap.set(col.key, index);
      }
    });

    // Parse and validate data rows
    const importedData: GenericData[] = [];
    const validationErrors: ImportError[] = [];
    const lastRowNumber = worksheet.lastRow?.number || 1;
    const skippedFieldsLog: { tagNo: string, fields: string[] }[] = []; // 건너뛴 필드 로그

    // Process each data row
    for (let rowNum = 2; rowNum <= lastRowNumber; rowNum++) {
      const row = worksheet.getRow(rowNum);
      const rowValues = row.values as ExcelJS.CellValue[];
      // 실제 값이 있는지 확인 (빈 문자열이 아닌 실제 내용)
      const hasAnyValue = rowValues && rowValues.slice(1).some(val =>
        val !== undefined &&
        val !== null &&
        String(val).trim() !== ""
      );

      if (!hasAnyValue) {
        console.log(`Row ${rowNum} is empty, skipping...`);
        continue; // 완전히 빈 행은 건너뛰기
      }

      const rowObj: Record<string, any> = {};
      const skippedFields: string[] = []; // 현재 행에서 건너뛴 필드들
      let hasErrors = false;

      // Get the TAG_NO first to identify existing data
      const tagNoColIndex = keyToIndexMap.get("TAG_NO");
      const tagNo = tagNoColIndex ? String(rowValues[tagNoColIndex] ?? "").trim() : "";
      const existingRowData = existingDataMap.get(tagNo);

      if (!existingTagNumbers.has(tagNo)) {
        validationErrors.push({
          tagNo: tagNo,
          rowIndex: rowNum,
          columnKey: "TAG_NO",
          columnLabel: "TAG NO",
          errorType: "TAG_NOT_FOUND",
          errorMessage: "TAG_NO not found in current data",
          currentValue: tagNo,
        });
        hasErrors = true;
      }

      // Process each column
      columnsJSON.forEach((col) => {
        const colIndex = keyToIndexMap.get(col.key);
        if (colIndex === undefined) return;

        // Check if this field is editable for this TAG_NO
        const fieldEditable = isFieldEditable(col, tagNo, editableFieldsMap);

        if (!fieldEditable) {
          // If field is not editable, preserve existing value
          if (existingRowData && existingRowData[col.key] !== undefined) {
            rowObj[col.key] = existingRowData[col.key];
          } else {
            // If no existing data, use appropriate default
            switch (col.type) {
              case "NUMBER":
                rowObj[col.key] = null;
                break;
              case "STRING":
              case "LIST":
              default:
                rowObj[col.key] = "";
                break;
            }
          }

          // Determine skip reason
          let skipReason = "";
          if (col.shi === "OUT" || col.shi === null) {
            skipReason = "SHI-only field";
          } else if (col.key === "TAG_NO" || col.key === "TAG_DESC" || col.key === "status") {
            skipReason = "System field";
          } else {
            skipReason = "Not editable for this TAG";
          }

          // Log skipped field
          skippedFields.push(`${col.label} (${skipReason})`);

          // Check if Excel contains a value for a read-only field and warn
          const cellValue = rowValues[colIndex] ?? "";
          const stringVal = String(cellValue).trim();
          if (stringVal && existingRowData && String(existingRowData[col.key] || "").trim() !== stringVal) {
            validationErrors.push({
              tagNo: tagNo || `Row-${rowNum}`,
              rowIndex: rowNum,
              columnKey: col.key,
              columnLabel: col.label,
              errorType: "READ_ONLY_FIELD",
              errorMessage: `Attempting to modify read-only field. ${skipReason}.`,
              currentValue: stringVal,
              expectedFormat: `Field is read-only. Current value: ${existingRowData[col.key] || "empty"}`,
            });
            hasErrors = true;
          }

          return; // Skip processing Excel value for this column
        }

        // Process Excel value for editable fields
        const cellValue = rowValues[colIndex] ?? "";
        let stringVal = String(cellValue).trim();

        // Type-specific validation
        switch (col.type) {
          case "STRING":
            rowObj[col.key] = stringVal;
            break;

          case "NUMBER":
            if (stringVal) {
              const num = parseFloat(stringVal);
              if (isNaN(num)) {
                validationErrors.push({
                  tagNo: tagNo || `Row-${rowNum}`,
                  rowIndex: rowNum,
                  columnKey: col.key,
                  columnLabel: col.label,
                  errorType: "TYPE_MISMATCH",
                  errorMessage: "Value is not a valid number",
                  currentValue: stringVal,
                  expectedFormat: "Number",
                });
                hasErrors = true;
              } else {
                rowObj[col.key] = num;
              }
            } else {
              rowObj[col.key] = null;
            }
            break;

          case "LIST":
            if (stringVal && col.options && !col.options.includes(stringVal)) {
              validationErrors.push({
                tagNo: tagNo || `Row-${rowNum}`,
                rowIndex: rowNum,
                columnKey: col.key,
                columnLabel: col.label,
                errorType: "INVALID_OPTION",
                errorMessage: "Value is not in the allowed options list",
                currentValue: stringVal,
                expectedFormat: col.options.join(", "),
              });
              hasErrors = true;
            }
            rowObj[col.key] = stringVal;
            break;

          default:
            rowObj[col.key] = stringVal;
            break;
        }
      });

      // Log skipped fields for this TAG
      if (skippedFields.length > 0) {
        skippedFieldsLog.push({
          tagNo: tagNo,
          fields: skippedFields
        });
      }

      // Add to valid data only if no errors
      if (!hasErrors) {
        importedData.push(rowObj);
      }
    }

    // Show summary of skipped fields
    if (skippedFieldsLog.length > 0) {
      const totalSkippedFields = skippedFieldsLog.reduce((sum, log) => sum + log.fields.length, 0);
      console.log("Skipped fields summary:", skippedFieldsLog);
      toast.info(
        `${totalSkippedFields} read-only fields were skipped across ${skippedFieldsLog.length} rows. Check console for details.`
      );
    }

    // If there are validation errors, create error report and exit
    if (validationErrors.length > 0) {
      createImportErrorSheet(workbook, validationErrors);

      const outBuffer = await workbook.xlsx.writeBuffer();
      saveAs(new Blob([outBuffer]), `import-error-report_${Date.now()}.xlsx`);

      toast.error(
        `Data validation failed. ${validationErrors.length} errors found across ${new Set(validationErrors.map(e => e.tagNo)).size} TAG(s). Check downloaded error report.`
      );

      return {
        success: false,
        error: "Data validation errors",
        errorCount: validationErrors.length,
        hasErrors: true,
        skippedFields: skippedFieldsLog
      };
    }

    // If we reached here, all data is valid
    // Create locally merged data for UI update
    const mergedData = [...tableData];
    const dataMap = new Map<string, GenericData>();

    // Map existing data by TAG_NO
    mergedData.forEach(item => {
      if (item.TAG_NO) {
        dataMap.set(item.TAG_NO, item);
      }
    });

    // Update with imported data
    importedData.forEach(item => {
      if (item.TAG_NO) {
        const existingItem = dataMap.get(item.TAG_NO);
        if (existingItem) {
          // Update existing item with imported values
          Object.assign(existingItem, item);
        }
      }
    });

    // If formCode and contractItemId are provided, save directly to DB
    // importExcelData 함수에서 DB 저장 부분
    if (formCode && contractItemId) {
      try {
        // 배치 업데이트 함수 호출
        const result = await updateFormDataBatchInDB(
          formCode,
          contractItemId,
          importedData // 모든 imported rows를 한번에 전달
        );

        if (result.success) {
          // 로컬 상태 업데이트
          if (onDataUpdate) {
            onDataUpdate(() => mergedData);
          }

          // 성공 메시지 구성
          const { updatedCount, notFoundTags } = result.data || {};

          let message = `Successfully updated ${updatedCount || importedData.length} rows`;

          // 건너뛴 필드가 있는 경우
          if (skippedFieldsLog.length > 0) {
            const totalSkippedFields = skippedFieldsLog.reduce((sum, log) => sum + log.fields.length, 0);
            message += ` (${totalSkippedFields} read-only fields preserved)`;
          }

          // 찾을 수 없는 TAG가 있는 경우
          if (notFoundTags && notFoundTags.length > 0) {
            console.warn("Tags not found in database:", notFoundTags);
            message += `. Warning: ${notFoundTags.length} tags not found in database`;
          }

          toast.success(message);

          return {
            success: true,
            importedCount: updatedCount || importedData.length,
            message: message,
            errorCount: 0,
            hasErrors: false,
            skippedFields: skippedFieldsLog,
            notFoundTags: notFoundTags
          };

        } else {
          // 배치 업데이트 실패
          console.error("Batch update failed:", result.message);

          // 부분 성공인 경우
          if (result.data?.updatedCount > 0) {
            // 부분적으로라도 업데이트된 경우 로컬 상태 업데이트
            if (onDataUpdate) {
              onDataUpdate(() => mergedData);
            }

            toast.warning(
              `Partially updated: ${result.data.updatedCount} of ${importedData.length} rows updated. ` +
              `${result.data.failedCount || 0} failed.`
            );

            return {
              success: true, // 부분 성공도 success로 처리
              importedCount: result.data.updatedCount,
              message: result.message,
              errorCount: result.data.failedCount || 0,
              hasErrors: true,
              skippedFields: skippedFieldsLog
            };

          } else {
            // 완전 실패
            toast.error(result.message || "Failed to update data to database");

            return {
              success: false,
              error: result.message,
              errorCount: importedData.length,
              hasErrors: true,
              skippedFields: skippedFieldsLog
            };
          }
        }

      } catch (saveError) {
        // 예외 발생 처리
        console.error("Failed to save imported data:", saveError);

        const errorMessage = saveError instanceof Error
          ? saveError.message
          : "Unknown error occurred";

        toast.error(`Database update failed: ${errorMessage}`);

        return {
          success: false,
          error: saveError,
          message: errorMessage,
          errorCount: importedData.length,
          hasErrors: true,
          skippedFields: skippedFieldsLog
        };
      }

    } else {
      // formCode나 contractItemId가 없는 경우 - 로컬 업데이트만
      if (onDataUpdate) {
        onDataUpdate(() => mergedData);
      }

      const successMessage = skippedFieldsLog.length > 0
        ? `Imported ${importedData.length} rows successfully (read-only fields preserved)`
        : `Imported ${importedData.length} rows successfully`;

      toast.success(`${successMessage} (local only - no database connection)`);

      return {
        success: true,
        importedCount: importedData.length,
        message: "Data imported locally only",
        errorCount: 0,
        hasErrors: false,
        skippedFields: skippedFieldsLog
      };
    }

  } catch (err) {
    console.error("Excel import error:", err);
    toast.error("Excel import failed.");
    return {
      success: false,
      error: err,
      errorCount: 1,
      hasErrors: true
    };
  } finally {
    if (onPendingChange) onPendingChange(false);
  }
}