/**
 * 데이터베이스 관련 검증 유틸리티
 *
 * SQL 인젝션 방지 및 데이터 무결성 보장을 위한 검증 함수들
 */

export class DatabaseValidator {
  // PostgreSQL 예약어 목록 (주요 키워드만)
  private static readonly RESERVED_WORDS = new Set([
    "SELECT",
    "INSERT",
    "UPDATE",
    "DELETE",
    "FROM",
    "WHERE",
    "JOIN",
    "INNER",
    "LEFT",
    "RIGHT",
    "FULL",
    "ON",
    "GROUP",
    "BY",
    "ORDER",
    "HAVING",
    "LIMIT",
    "OFFSET",
    "UNION",
    "ALL",
    "DISTINCT",
    "AS",
    "AND",
    "OR",
    "NOT",
    "NULL",
    "TRUE",
    "FALSE",
    "CASE",
    "WHEN",
    "THEN",
    "ELSE",
    "END",
    "IF",
    "EXISTS",
    "IN",
    "BETWEEN",
    "LIKE",
    "ILIKE",
    "SIMILAR",
    "TO",
    "CREATE",
    "DROP",
    "ALTER",
    "TABLE",
    "INDEX",
    "VIEW",
    "FUNCTION",
    "PROCEDURE",
    "TRIGGER",
    "DATABASE",
    "SCHEMA",
    "USER",
    "ROLE",
    "GRANT",
    "REVOKE",
    "COMMIT",
    "ROLLBACK",
    "BEGIN",
    "TRANSACTION",
    "SAVEPOINT",
    "RELEASE",
    "CONSTRAINT",
    "PRIMARY",
    "FOREIGN",
    "KEY",
    "UNIQUE",
    "CHECK",
    "DEFAULT",
    "REFERENCES",
    "CASCADE",
    "RESTRICT",
    "SET",
    "ACTION",
    "DEFERRABLE",
    "INITIALLY",
    "DEFERRED",
    "IMMEDIATE",
    "MATCH",
    "PARTIAL",
    "SIMPLE",
    "FULL",
  ]);

  // 유효한 PostgreSQL 데이터 타입 패턴
  private static readonly DATA_TYPE_PATTERNS = [
    /^(SMALLINT|INTEGER|BIGINT|DECIMAL|NUMERIC|REAL|DOUBLE\s+PRECISION|SMALLSERIAL|SERIAL|BIGSERIAL)$/i,
    /^(MONEY)$/i,
    /^(CHARACTER\s+VARYING|VARCHAR|CHARACTER|CHAR|TEXT)(\(\d+\))?$/i,
    /^(BYTEA)$/i,
    /^(TIMESTAMP|TIME)(\s+(WITH|WITHOUT)\s+TIME\s+ZONE)?(\(\d+\))?$/i,
    /^(DATE|INTERVAL)(\(\d+\))?$/i,
    /^(BOOLEAN|BOOL)$/i,
    /^(POINT|LINE|LSEG|BOX|PATH|POLYGON|CIRCLE)$/i,
    /^(CIDR|INET|MACADDR|MACADDR8)$/i,
    /^(BIT|BIT\s+VARYING)(\(\d+\))?$/i,
    /^(TSVECTOR|TSQUERY)$/i,
    /^(UUID)$/i,
    /^(XML)$/i,
    /^(JSON|JSONB)$/i,
    /^(ARRAY|INTEGER\[\]|TEXT\[\]|VARCHAR\[\])$/i,
    /^(DECIMAL|NUMERIC)\(\d+,\d+\)$/i,
  ];

  /**
   * 테이블명 검증
   */
  static validateTableName(tableName: string): boolean {
    if (!tableName || typeof tableName !== "string") {
      return false;
    }

    // 길이 제한 (PostgreSQL 최대 63자)
    if (tableName.length === 0 || tableName.length > 63) {
      return false;
    }

    // 유효한 식별자 패턴 (문자 또는 밑줄로 시작, 문자/숫자/밑줄만 포함)
    const validPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
    if (!validPattern.test(tableName)) {
      return false;
    }

    // 예약어 체크
    if (this.RESERVED_WORDS.has(tableName.toUpperCase())) {
      return false;
    }

    return true;
  }

  /**
   * 컬럼명 검증
   */
  static validateColumnName(columnName: string): boolean {
    if (!columnName || typeof columnName !== "string") {
      return false;
    }

    // 길이 제한
    if (columnName.length === 0 || columnName.length > 63) {
      return false;
    }

    // JSON 연산자 포함 컬럼명 허용 (예: config->>'type', data->>path)
    if (columnName.includes("->") || columnName.includes("->>")) {
      const baseName = columnName.split(/->|->>/)[0];
      return this.validateColumnName(baseName);
    }

    // 유효한 식별자 패턴
    const validPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
    if (!validPattern.test(columnName)) {
      return false;
    }

    // 예약어 체크
    if (this.RESERVED_WORDS.has(columnName.toUpperCase())) {
      return false;
    }

    return true;
  }

  /**
   * 데이터 타입 검증
   */
  static validateDataType(dataType: string): boolean {
    if (!dataType || typeof dataType !== "string") {
      return false;
    }

    const normalizedType = dataType.trim().toUpperCase();

    return this.DATA_TYPE_PATTERNS.some((pattern) =>
      pattern.test(normalizedType)
    );
  }

  /**
   * WHERE 조건 검증
   */
  static validateWhereClause(whereClause: Record<string, any>): boolean {
    if (!whereClause || typeof whereClause !== "object") {
      return false;
    }

    // 모든 키가 유효한 컬럼명인지 확인
    for (const key of Object.keys(whereClause)) {
      if (!this.validateColumnName(key)) {
        return false;
      }
    }

    return true;
  }

  /**
   * 페이지네이션 파라미터 검증
   */
  static validatePagination(page: number, pageSize: number): boolean {
    // 페이지 번호는 1 이상
    if (!Number.isInteger(page) || page < 1) {
      return false;
    }

    // 페이지 크기는 1 이상 1000 이하
    if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 1000) {
      return false;
    }

    return true;
  }

  /**
   * ORDER BY 절 검증
   */
  static validateOrderBy(orderBy: string): boolean {
    if (!orderBy || typeof orderBy !== "string") {
      return false;
    }

    // 기본 패턴: column_name [ASC|DESC]
    const orderPattern = /^[a-zA-Z_][a-zA-Z0-9_]*(\s+(ASC|DESC))?$/i;

    // 여러 컬럼 정렬의 경우 콤마로 분리하여 각각 검증
    const orderClauses = orderBy.split(",").map((clause) => clause.trim());

    return orderClauses.every((clause) => {
      return (
        orderPattern.test(clause) &&
        this.validateColumnName(clause.split(/\s+/)[0])
      );
    });
  }

  /**
   * UUID 형식 검증
   */
  static validateUUID(uuid: string): boolean {
    if (!uuid || typeof uuid !== "string") {
      return false;
    }

    const uuidPattern =
      /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    return uuidPattern.test(uuid);
  }

  /**
   * 이메일 형식 검증
   */
  static validateEmail(email: string): boolean {
    if (!email || typeof email !== "string") {
      return false;
    }

    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailPattern.test(email) && email.length <= 254;
  }

  /**
   * SQL 인젝션 위험 문자열 검사
   */
  static containsSqlInjection(input: string): boolean {
    if (!input || typeof input !== "string") {
      return false;
    }

    // 위험한 SQL 패턴들
    const dangerousPatterns = [
      /('|\\')|(;)|(--)|(\s+(OR|AND)\s+\d+\s*=\s*\d+)/i,
      /(UNION|SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE)/i,
      /(\bxp_\w+|\bsp_\w+)/i, // SQL Server 확장 프로시저
      /(script|javascript|vbscript|onload|onerror)/i, // XSS 패턴
    ];

    return dangerousPatterns.some((pattern) => pattern.test(input));
  }

  /**
   * 숫자 범위 검증
   */
  static validateNumberRange(
    value: number,
    min?: number,
    max?: number
  ): boolean {
    if (typeof value !== "number" || !Number.isFinite(value)) {
      return false;
    }

    if (min !== undefined && value < min) {
      return false;
    }

    if (max !== undefined && value > max) {
      return false;
    }

    return true;
  }

  /**
   * 문자열 길이 검증
   */
  static validateStringLength(
    value: string,
    minLength?: number,
    maxLength?: number
  ): boolean {
    if (typeof value !== "string") {
      return false;
    }

    if (minLength !== undefined && value.length < minLength) {
      return false;
    }

    if (maxLength !== undefined && value.length > maxLength) {
      return false;
    }

    return true;
  }

  /**
   * JSON 형식 검증
   */
  static validateJSON(jsonString: string): boolean {
    try {
      JSON.parse(jsonString);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * 날짜 형식 검증 (ISO 8601)
   */
  static validateDateISO(dateString: string): boolean {
    if (!dateString || typeof dateString !== "string") {
      return false;
    }

    const date = new Date(dateString);
    return !isNaN(date.getTime()) && dateString === date.toISOString();
  }

  /**
   * 배열 요소 검증
   */
  static validateArray<T>(
    array: any[],
    validator: (item: T) => boolean,
    minLength?: number,
    maxLength?: number
  ): boolean {
    if (!Array.isArray(array)) {
      return false;
    }

    if (minLength !== undefined && array.length < minLength) {
      return false;
    }

    if (maxLength !== undefined && array.length > maxLength) {
      return false;
    }

    return array.every((item) => validator(item));
  }
}
