/**
 * 화면관리 폼 데이터 검증 유틸리티
 * 클라이언트 측에서 사전 검증을 수행하여 사용자 경험 향상
 */

import { WebType, DynamicWebType, isValidWebType, normalizeWebType } from "@/types/v2-web-types";
import { ColumnInfo, ComponentData, WidgetComponent } from "@/types/screen";
import { isColumnRequiredByMeta } from "@/lib/registry/DynamicComponentRenderer";

// 검증 결과 타입
export interface ValidationResult {
  isValid: boolean;
  errors: ValidationError[];
  warnings: ValidationWarning[];
}

export interface ValidationError {
  field: string;
  code: string;
  message: string;
  severity: "error" | "warning";
  value?: any;
}

export interface ValidationWarning {
  field: string;
  code: string;
  message: string;
  suggestion?: string;
}

// 필드 검증 결과
export interface FieldValidationResult {
  isValid: boolean;
  error?: ValidationError;
  transformedValue?: any;
}

// 스키마 검증 결과
export interface SchemaValidationResult {
  isValid: boolean;
  missingColumns: string[];
  invalidTypes: { field: string; expected: WebType; actual: string }[];
  suggestions: string[];
}

/**
 * 폼 데이터 전체 검증
 */
export const validateFormData = async (
  formData: Record<string, any>,
  components: ComponentData[],
  tableColumns: ColumnInfo[],
  tableName: string,
): Promise<ValidationResult> => {
  const errors: ValidationError[] = [];
  const warnings: ValidationWarning[] = [];

  try {
    // 1. 스키마 검증 (컬럼 존재 여부, 타입 일치)
    const schemaValidation = validateFormSchema(formData, components, tableColumns);
    if (!schemaValidation.isValid) {
      errors.push(
        ...schemaValidation.missingColumns.map((col) => ({
          field: col,
          code: "COLUMN_NOT_EXISTS",
          message: `테이블 '${tableName}'에 '${col}' 컬럼이 존재하지 않습니다.`,
          severity: "error" as const,
        })),
      );

      errors.push(
        ...schemaValidation.invalidTypes.map((type) => ({
          field: type.field,
          code: "INVALID_WEB_TYPE",
          message: `필드 '${type.field}'의 웹타입이 올바르지 않습니다. 예상: ${type.expected}, 실제: ${type.actual}`,
          severity: "error" as const,
        })),
      );
    }

    // 2. 필수 필드 검증 (NOT NULL 메타데이터 포함)
    const requiredValidation = validateRequiredFields(formData, components, tableName);
    errors.push(...requiredValidation);

    // 3. 데이터 타입 검증 및 변환
    const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];

    for (const component of widgetComponents) {
      const fieldName = component.columnName || component.id;
      const value = formData[fieldName];

      if (value !== undefined && value !== null && value !== "") {
        const fieldValidation = validateFieldValue(
          fieldName,
          value,
          component.widgetType,
          component.webTypeConfig,
          component.validationRules,
        );

        if (!fieldValidation.isValid && fieldValidation.error) {
          errors.push(fieldValidation.error);
        }
      }
    }

    // 4. 비즈니스 로직 검증 (커스텀 규칙)
    const businessValidation = await validateBusinessRules(formData, tableName, components);
    errors.push(...businessValidation.errors);
    warnings.push(...businessValidation.warnings);
  } catch (error) {
    errors.push({
      field: "form",
      code: "VALIDATION_ERROR",
      message: `검증 중 오류가 발생했습니다: ${error}`,
      severity: "error",
    });
  }

  return {
    isValid: errors.filter((e) => e.severity === "error").length === 0,
    errors,
    warnings,
  };
};

/**
 * 스키마 검증 (컬럼 존재 여부, 타입 일치)
 */
export const validateFormSchema = (
  formData: Record<string, any>,
  components: ComponentData[],
  tableColumns: ColumnInfo[],
): SchemaValidationResult => {
  const missingColumns: string[] = [];
  const invalidTypes: { field: string; expected: WebType; actual: string }[] = [];
  const suggestions: string[] = [];

  const columnMap = new Map(tableColumns.map((col) => [col.columnName, col]));
  const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];

  for (const component of widgetComponents) {
    const fieldName = component.columnName;
    if (!fieldName) continue;

    // 컬럼 존재 여부 확인
    const columnInfo = columnMap.get(fieldName);
    if (!columnInfo) {
      missingColumns.push(fieldName);

      // 유사한 컬럼명 제안
      const similar = findSimilarColumns(fieldName, tableColumns);
      if (similar.length > 0) {
        suggestions.push(`'${fieldName}' 대신 '${similar.join("', '")}'을 사용하시겠습니까?`);
      }
      continue;
    }

    // 웹타입 일치 여부 확인
    const componentWebType = normalizeWebType(component.widgetType);
    const columnWebType = columnInfo.webType ? normalizeWebType(columnInfo.webType) : null;

    if (columnWebType && componentWebType !== columnWebType) {
      invalidTypes.push({
        field: fieldName,
        expected: columnWebType,
        actual: componentWebType,
      });
    }

    // 웹타입 유효성 확인
    if (!isValidWebType(component.widgetType)) {
      invalidTypes.push({
        field: fieldName,
        expected: "text", // 기본값
        actual: component.widgetType,
      });
    }
  }

  return {
    isValid: missingColumns.length === 0 && invalidTypes.length === 0,
    missingColumns,
    invalidTypes,
    suggestions,
  };
};

/**
 * 필수 필드 검증
 */
export const validateRequiredFields = (
  formData: Record<string, any>,
  components: ComponentData[],
  tableName?: string,
): ValidationError[] => {
  const errors: ValidationError[] = [];
  const widgetComponents = components.filter((c) => c.type === "widget") as WidgetComponent[];

  for (const component of widgetComponents) {
    const fieldName = component.columnName || component.id;
    // 수동 required + NOT NULL 메타데이터 기반 통합 체크
    const isRequired = component.required || isColumnRequiredByMeta(tableName, fieldName);
    if (!isRequired) continue;

    const value = formData[fieldName];

    if (
      value === undefined ||
      value === null ||
      (typeof value === "string" && value.trim() === "") ||
      (Array.isArray(value) && value.length === 0)
    ) {
      errors.push({
        field: fieldName,
        code: "REQUIRED_FIELD",
        message: `'${component.label || fieldName}'은(는) 필수 입력 항목입니다.`,
        severity: "error",
        value,
      });
    }
  }

  return errors;
};

/**
 * 개별 필드 값 검증
 */
export const validateFieldValue = (
  fieldName: string,
  value: any,
  webType: DynamicWebType,
  config?: Record<string, any>,
  rules?: any[],
): FieldValidationResult => {
  try {
    const normalizedWebType = normalizeWebType(webType);

    // 타입별 검증
    switch (normalizedWebType) {
      case "number":
        return validateNumberField(fieldName, value, config);

      case "decimal":
        return validateDecimalField(fieldName, value, config);

      case "date":
        return validateDateField(fieldName, value, config);

      case "datetime":
        return validateDateTimeField(fieldName, value, config);

      case "email":
        return validateEmailField(fieldName, value, config);

      case "tel":
        return validateTelField(fieldName, value, config);

      case "url":
        return validateUrlField(fieldName, value, config);

      case "text":
      case "textarea":
        return validateTextField(fieldName, value, config);

      case "boolean":
      case "checkbox":
        return validateBooleanField(fieldName, value, config);

      default:
        // 기본 문자열 검증
        return validateTextField(fieldName, value, config);
    }
  } catch (error) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "VALIDATION_ERROR",
        message: `필드 '${fieldName}' 검증 중 오류: ${error}`,
        severity: "error",
        value,
      },
    };
  }
};

/**
 * 숫자 필드 검증
 */
const validateNumberField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const numValue = Number(value);

  if (isNaN(numValue)) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_NUMBER",
        message: `'${fieldName}'에는 숫자만 입력할 수 있습니다.`,
        severity: "error",
        value,
      },
    };
  }

  if (!Number.isInteger(numValue)) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "NOT_INTEGER",
        message: `'${fieldName}'에는 정수만 입력할 수 있습니다.`,
        severity: "error",
        value,
      },
    };
  }

  // 범위 검증
  if (config?.min !== undefined && numValue < config.min) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "VALUE_TOO_SMALL",
        message: `'${fieldName}'의 값은 ${config.min} 이상이어야 합니다.`,
        severity: "error",
        value,
      },
    };
  }

  if (config?.max !== undefined && numValue > config.max) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "VALUE_TOO_LARGE",
        message: `'${fieldName}'의 값은 ${config.max} 이하여야 합니다.`,
        severity: "error",
        value,
      },
    };
  }

  return { isValid: true, transformedValue: numValue };
};

/**
 * 소수 필드 검증
 */
const validateDecimalField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const numValue = Number(value);

  if (isNaN(numValue)) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_DECIMAL",
        message: `'${fieldName}'에는 숫자만 입력할 수 있습니다.`,
        severity: "error",
        value,
      },
    };
  }

  // 소수점 자릿수 검증
  if (config?.decimalPlaces !== undefined) {
    const decimalPart = value.toString().split(".")[1];
    if (decimalPart && decimalPart.length > config.decimalPlaces) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "TOO_MANY_DECIMAL_PLACES",
          message: `'${fieldName}'의 소수점은 ${config.decimalPlaces}자리까지만 입력할 수 있습니다.`,
          severity: "error",
          value,
        },
      };
    }
  }

  return { isValid: true, transformedValue: numValue };
};

/**
 * 날짜 필드 검증
 */
const validateDateField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const dateValue = new Date(value);

  if (isNaN(dateValue.getTime())) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_DATE",
        message: `'${fieldName}'에는 올바른 날짜를 입력해주세요.`,
        severity: "error",
        value,
      },
    };
  }

  // 날짜 범위 검증
  if (config?.minDate) {
    const minDate = new Date(config.minDate);
    if (dateValue < minDate) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "DATE_TOO_EARLY",
          message: `'${fieldName}'의 날짜는 ${config.minDate} 이후여야 합니다.`,
          severity: "error",
          value,
        },
      };
    }
  }

  if (config?.maxDate) {
    const maxDate = new Date(config.maxDate);
    if (dateValue > maxDate) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "DATE_TOO_LATE",
          message: `'${fieldName}'의 날짜는 ${config.maxDate} 이전이어야 합니다.`,
          severity: "error",
          value,
        },
      };
    }
  }

  return { isValid: true, transformedValue: `${dateValue.getFullYear()}-${String(dateValue.getMonth() + 1).padStart(2, "0")}-${String(dateValue.getDate()).padStart(2, "0")}` };
};

/**
 * 날짜시간 필드 검증
 */
const validateDateTimeField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const dateValue = new Date(value);

  if (isNaN(dateValue.getTime())) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_DATETIME",
        message: `'${fieldName}'에는 올바른 날짜시간을 입력해주세요.`,
        severity: "error",
        value,
      },
    };
  }

  return { isValid: true, transformedValue: dateValue.toISOString() };
};

/**
 * 이메일 필드 검증
 */
const validateEmailField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

  if (!emailRegex.test(value)) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_EMAIL",
        message: `'${fieldName}'에는 올바른 이메일 주소를 입력해주세요.`,
        severity: "error",
        value,
      },
    };
  }

  return { isValid: true, transformedValue: value };
};

/**
 * 전화번호 필드 검증
 */
const validateTelField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  // 기본 전화번호 형식 검증 (한국)
  const telRegex = /^(\d{2,3}-?\d{3,4}-?\d{4}|\d{10,11})$/;

  if (!telRegex.test(value.replace(/\s/g, ""))) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_TEL",
        message: `'${fieldName}'에는 올바른 전화번호를 입력해주세요.`,
        severity: "error",
        value,
      },
    };
  }

  return { isValid: true, transformedValue: value };
};

/**
 * URL 필드 검증
 */
const validateUrlField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  try {
    new URL(value);
    return { isValid: true, transformedValue: value };
  } catch {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "INVALID_URL",
        message: `'${fieldName}'에는 올바른 URL을 입력해주세요.`,
        severity: "error",
        value,
      },
    };
  }
};

/**
 * 텍스트 필드 검증
 */
const validateTextField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  const strValue = String(value);

  // 길이 검증
  if (config?.minLength && strValue.length < config.minLength) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "TOO_SHORT",
        message: `'${fieldName}'은(는) 최소 ${config.minLength}자 이상이어야 합니다.`,
        severity: "error",
        value,
      },
    };
  }

  if (config?.maxLength && strValue.length > config.maxLength) {
    return {
      isValid: false,
      error: {
        field: fieldName,
        code: "TOO_LONG",
        message: `'${fieldName}'은(는) 최대 ${config.maxLength}자까지만 입력할 수 있습니다.`,
        severity: "error",
        value,
      },
    };
  }

  // 패턴 검증
  if (config?.pattern) {
    const regex = new RegExp(config.pattern);
    if (!regex.test(strValue)) {
      return {
        isValid: false,
        error: {
          field: fieldName,
          code: "PATTERN_MISMATCH",
          message: `'${fieldName}'의 형식이 올바르지 않습니다.`,
          severity: "error",
          value,
        },
      };
    }
  }

  return { isValid: true, transformedValue: strValue };
};

/**
 * 불린 필드 검증
 */
const validateBooleanField = (fieldName: string, value: any, config?: Record<string, any>): FieldValidationResult => {
  let boolValue: boolean;

  if (typeof value === "boolean") {
    boolValue = value;
  } else if (typeof value === "string") {
    boolValue = value.toLowerCase() === "true" || value === "1";
  } else if (typeof value === "number") {
    boolValue = value === 1;
  } else {
    boolValue = Boolean(value);
  }

  return { isValid: true, transformedValue: boolValue };
};

/**
 * 비즈니스 로직 검증 (커스텀)
 */
const validateBusinessRules = async (
  formData: Record<string, any>,
  tableName: string,
  components: ComponentData[],
): Promise<{ errors: ValidationError[]; warnings: ValidationWarning[] }> => {
  const errors: ValidationError[] = [];
  const warnings: ValidationWarning[] = [];

  // 여기에 테이블별 비즈니스 로직 검증 추가
  // 예: 중복 체크, 외래키 제약조건, 커스텀 규칙 등

  return { errors, warnings };
};

/**
 * 유사한 컬럼명 찾기 (오타 제안용)
 */
const findSimilarColumns = (targetColumn: string, columns: ColumnInfo[], threshold: number = 0.6): string[] => {
  const similar: string[] = [];

  for (const column of columns) {
    const similarity = calculateStringSimilarity(targetColumn, column.columnName);
    if (similarity >= threshold) {
      similar.push(column.columnName);
    }
  }

  return similar.slice(0, 3); // 최대 3개까지
};

/**
 * 문자열 유사도 계산 (Levenshtein distance 기반)
 */
const calculateStringSimilarity = (str1: string, str2: string): number => {
  const len1 = str1.length;
  const len2 = str2.length;

  const matrix: number[][] = [];

  for (let i = 0; i <= len1; i++) {
    matrix[i] = [i];
  }

  for (let j = 0; j <= len2; j++) {
    matrix[0][j] = j;
  }

  for (let i = 1; i <= len1; i++) {
    for (let j = 1; j <= len2; j++) {
      const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
      matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
    }
  }

  const distance = matrix[len1][len2];
  const maxLen = Math.max(len1, len2);

  return maxLen === 0 ? 1 : (maxLen - distance) / maxLen;
};

