/**
 * DDL 안전성 검증 서비스
 * 테이블/컬럼 생성 전 모든 보안 검증을 수행
 */

import {
  CreateColumnDefinition,
  ValidationResult,
  SYSTEM_TABLES,
  RESERVED_WORDS,
  RESERVED_COLUMNS,
} from "../types/ddl";
import { logger } from "../utils/logger";

export class DDLSafetyValidator {
  /**
   * 테이블 생성 전 전체 검증
   */
  static validateTableCreation(
    tableName: string,
    columns: CreateColumnDefinition[]
  ): ValidationResult {
    const errors: string[] = [];
    const warnings: string[] = [];

    try {
      // 1. 테이블명 기본 검증
      const tableNameValidation = this.validateTableName(tableName);
      if (!tableNameValidation.isValid) {
        errors.push(...tableNameValidation.errors);
      }

      // 2. 컬럼 기본 검증
      if (columns.length === 0) {
        errors.push("최소 1개의 컬럼이 필요합니다.");
      }

      // 3. 컬럼 목록 검증
      const columnsValidation = this.validateColumnList(columns);
      if (!columnsValidation.isValid) {
        errors.push(...columnsValidation.errors);
      }
      if (columnsValidation.warnings) {
        warnings.push(...columnsValidation.warnings);
      }

      // 4. 컬럼명 중복 검증
      const duplicateValidation = this.validateColumnDuplication(columns);
      if (!duplicateValidation.isValid) {
        errors.push(...duplicateValidation.errors);
      }

      logger.info("테이블 생성 검증 완료", {
        tableName,
        columnCount: columns.length,
        errorCount: errors.length,
        warningCount: warnings.length,
      });

      return {
        isValid: errors.length === 0,
        errors,
        warnings,
      };
    } catch (error) {
      logger.error("테이블 생성 검증 중 오류 발생:", error);
      return {
        isValid: false,
        errors: ["테이블 생성 검증 중 내부 오류가 발생했습니다."],
      };
    }
  }

  /**
   * 컬럼 추가 전 검증
   */
  static validateColumnAddition(
    tableName: string,
    column: CreateColumnDefinition
  ): ValidationResult {
    const errors: string[] = [];
    const warnings: string[] = [];

    try {
      // 1. 테이블명 검증 (시스템 테이블 확인)
      if (this.isSystemTable(tableName)) {
        errors.push(
          `'${tableName}'은 시스템 테이블이므로 컬럼을 추가할 수 없습니다.`
        );
      }

      // 2. 컬럼 정의 검증
      const columnValidation = this.validateSingleColumn(column);
      if (!columnValidation.isValid) {
        errors.push(...columnValidation.errors);
      }
      if (columnValidation.warnings) {
        warnings.push(...columnValidation.warnings);
      }

      logger.info("컬럼 추가 검증 완료", {
        tableName,
        columnName: column.name,
        webType: column.webType,
        errorCount: errors.length,
      });

      return {
        isValid: errors.length === 0,
        errors,
        warnings,
      };
    } catch (error) {
      logger.error("컬럼 추가 검증 중 오류 발생:", error);
      return {
        isValid: false,
        errors: ["컬럼 추가 검증 중 내부 오류가 발생했습니다."],
      };
    }
  }

  /**
   * 테이블명 검증
   */
  private static validateTableName(tableName: string): ValidationResult {
    const errors: string[] = [];

    // 1. 기본 형식 검증
    if (!this.isValidTableName(tableName)) {
      errors.push(
        "유효하지 않은 테이블명입니다. 영문자로 시작하고 영문자, 숫자, 언더스코어만 사용 가능합니다."
      );
    }

    // 2. 길이 검증
    if (tableName.length > 63) {
      errors.push("테이블명은 63자를 초과할 수 없습니다.");
    }

    if (tableName.length < 2) {
      errors.push("테이블명은 최소 2자 이상이어야 합니다.");
    }

    // 3. 시스템 테이블 보호
    if (this.isSystemTable(tableName)) {
      errors.push(
        `'${tableName}'은 시스템 테이블명으로 사용할 수 없습니다. 다른 이름을 선택해주세요.`
      );
    }

    // 4. 예약어 검증
    if (this.isReservedWord(tableName)) {
      errors.push(
        `'${tableName}'은 SQL 예약어이므로 테이블명으로 사용할 수 없습니다.`
      );
    }

    // 5. 일반적인 네이밍 컨벤션 검증
    if (tableName.startsWith("_") || tableName.endsWith("_")) {
      errors.push("테이블명은 언더스코어로 시작하거나 끝날 수 없습니다.");
    }

    if (tableName.includes("__")) {
      errors.push("테이블명에 연속된 언더스코어는 사용할 수 없습니다.");
    }

    return {
      isValid: errors.length === 0,
      errors,
    };
  }

  /**
   * 컬럼 목록 검증
   */
  private static validateColumnList(
    columns: CreateColumnDefinition[]
  ): ValidationResult {
    const errors: string[] = [];
    const warnings: string[] = [];

    for (let i = 0; i < columns.length; i++) {
      const column = columns[i];
      const columnValidation = this.validateSingleColumn(column, i + 1);

      if (!columnValidation.isValid) {
        errors.push(...columnValidation.errors);
      }

      if (columnValidation.warnings) {
        warnings.push(...columnValidation.warnings);
      }
    }

    return {
      isValid: errors.length === 0,
      errors,
      warnings,
    };
  }

  /**
   * 개별 컬럼 검증
   */
  private static validateSingleColumn(
    column: CreateColumnDefinition,
    position?: number
  ): ValidationResult {
    const errors: string[] = [];
    const warnings: string[] = [];
    const prefix = position
      ? `컬럼 ${position}(${column.name}): `
      : `컬럼 '${column.name}': `;

    // 1. 컬럼명 기본 검증
    if (!column.name || column.name.trim() === "") {
      errors.push(`${prefix}컬럼명은 필수입니다.`);
      return { isValid: false, errors };
    }

    if (!this.isValidColumnName(column.name)) {
      errors.push(
        `${prefix}유효하지 않은 컬럼명입니다. 영문자로 시작하고 영문자, 숫자, 언더스코어만 사용 가능합니다.`
      );
    }

    // 2. 길이 검증
    if (column.name.length > 63) {
      errors.push(`${prefix}컬럼명은 63자를 초과할 수 없습니다.`);
    }

    if (column.name.length < 2) {
      errors.push(`${prefix}컬럼명은 최소 2자 이상이어야 합니다.`);
    }

    // 3. 예약된 컬럼명 검증
    if (this.isReservedColumnName(column.name)) {
      errors.push(
        `${prefix}'${column.name}'은 예약된 컬럼명입니다. 기본 컬럼(id, created_date, updated_date, company_code)과 중복됩니다.`
      );
    }

    // 4. SQL 예약어 검증
    if (this.isReservedWord(column.name)) {
      errors.push(
        `${prefix}'${column.name}'은 SQL 예약어이므로 컬럼명으로 사용할 수 없습니다.`
      );
    }

    // 5. 웹타입 검증
    if (!column.webType) {
      errors.push(`${prefix}웹타입이 지정되지 않았습니다.`);
    }

    // 6. 길이 설정 검증 (text, code 타입에서만 허용)
    if (column.length !== undefined) {
      if (
        !["text", "code", "email", "tel", "select", "radio"].includes(
          column.webType || "text"
        )
      ) {
        warnings.push(
          `${prefix}${column.webType || "text"} 타입에서는 길이 설정이 무시됩니다.`
        );
      } else if (column.length <= 0 || column.length > 65535) {
        errors.push(`${prefix}길이는 1 이상 65535 이하여야 합니다.`);
      }
    }

    // 7. 네이밍 컨벤션 검증
    if (column.name.startsWith("_") || column.name.endsWith("_")) {
      warnings.push(
        `${prefix}컬럼명이 언더스코어로 시작하거나 끝나는 것은 권장하지 않습니다.`
      );
    }

    if (column.name.includes("__")) {
      errors.push(`${prefix}컬럼명에 연속된 언더스코어는 사용할 수 없습니다.`);
    }

    return {
      isValid: errors.length === 0,
      errors,
      warnings,
    };
  }

  /**
   * 컬럼명 중복 검증
   */
  private static validateColumnDuplication(
    columns: CreateColumnDefinition[]
  ): ValidationResult {
    const errors: string[] = [];
    const columnNames = columns.map((col) => col.name.toLowerCase());
    const seen = new Set<string>();
    const duplicates = new Set<string>();

    for (const name of columnNames) {
      if (seen.has(name)) {
        duplicates.add(name);
      } else {
        seen.add(name);
      }
    }

    if (duplicates.size > 0) {
      errors.push(
        `중복된 컬럼명이 있습니다: ${Array.from(duplicates).join(", ")}`
      );
    }

    return {
      isValid: errors.length === 0,
      errors,
    };
  }

  /**
   * 테이블명 유효성 검증 (정규식)
   */
  private static isValidTableName(tableName: string): boolean {
    const tableNameRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
    return tableNameRegex.test(tableName);
  }

  /**
   * 컬럼명 유효성 검증 (정규식)
   */
  private static isValidColumnName(columnName: string): boolean {
    const columnNameRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
    return columnNameRegex.test(columnName) && columnName.length <= 63;
  }

  /**
   * 시스템 테이블 확인
   */
  private static isSystemTable(tableName: string): boolean {
    return SYSTEM_TABLES.includes(tableName.toLowerCase() as any);
  }

  /**
   * SQL 예약어 확인
   */
  private static isReservedWord(word: string): boolean {
    return RESERVED_WORDS.includes(word.toLowerCase() as any);
  }

  /**
   * 예약된 컬럼명 확인
   */
  private static isReservedColumnName(columnName: string): boolean {
    return RESERVED_COLUMNS.includes(columnName.toLowerCase() as any);
  }

  /**
   * 전체 검증 통계 생성
   */
  static generateValidationReport(
    tableName: string,
    columns: CreateColumnDefinition[]
  ): {
    tableName: string;
    totalColumns: number;
    validationResult: ValidationResult;
    summary: string;
  } {
    const validationResult = this.validateTableCreation(tableName, columns);

    let summary = `테이블 '${tableName}' 검증 완료. `;
    summary += `컬럼 ${columns.length}개 중 `;

    if (validationResult.isValid) {
      summary += "모든 검증 통과.";
    } else {
      summary += `${validationResult.errors.length}개 오류 발견.`;
    }

    if (validationResult.warnings && validationResult.warnings.length > 0) {
      summary += ` ${validationResult.warnings.length}개 경고 있음.`;
    }

    return {
      tableName,
      totalColumns: columns.length,
      validationResult,
      summary,
    };
  }
}
