/**
 * 플로우 조건 파서
 * JSON 조건을 SQL WHERE 절로 변환
 */

import {
  FlowCondition,
  FlowConditionGroup,
  SqlWhereResult,
} from "../types/flow";

export class FlowConditionParser {
  /**
   * 조건 JSON을 SQL WHERE 절로 변환
   */
  static toSqlWhere(
    conditionGroup: FlowConditionGroup | null | undefined
  ): SqlWhereResult {
    if (
      !conditionGroup ||
      !conditionGroup.conditions ||
      conditionGroup.conditions.length === 0
    ) {
      return { where: "1=1", params: [] };
    }

    const conditions: string[] = [];
    const params: any[] = [];
    let paramIndex = 1;

    for (const condition of conditionGroup.conditions) {
      const column = this.sanitizeColumnName(condition.column);

      switch (condition.operator) {
        case "equals":
        case "=":
          conditions.push(`${column} = $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "not_equals":
        case "!=":
          conditions.push(`${column} != $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "in":
          if (Array.isArray(condition.value) && condition.value.length > 0) {
            const placeholders = condition.value
              .map(() => `$${paramIndex++}`)
              .join(", ");
            conditions.push(`${column} IN (${placeholders})`);
            params.push(...condition.value);
          }
          break;

        case "not_in":
          if (Array.isArray(condition.value) && condition.value.length > 0) {
            const placeholders = condition.value
              .map(() => `$${paramIndex++}`)
              .join(", ");
            conditions.push(`${column} NOT IN (${placeholders})`);
            params.push(...condition.value);
          }
          break;

        case "greater_than":
        case ">":
          conditions.push(`${column} > $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "less_than":
        case "<":
          conditions.push(`${column} < $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "greater_than_or_equal":
        case ">=":
          conditions.push(`${column} >= $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "less_than_or_equal":
        case "<=":
          conditions.push(`${column} <= $${paramIndex}`);
          params.push(condition.value);
          paramIndex++;
          break;

        case "is_null":
          conditions.push(`${column} IS NULL`);
          break;

        case "is_not_null":
          conditions.push(`${column} IS NOT NULL`);
          break;

        case "like":
          conditions.push(`${column} LIKE $${paramIndex}`);
          params.push(`%${condition.value}%`);
          paramIndex++;
          break;

        case "not_like":
          conditions.push(`${column} NOT LIKE $${paramIndex}`);
          params.push(`%${condition.value}%`);
          paramIndex++;
          break;

        default:
          throw new Error(`Unsupported operator: ${condition.operator}`);
      }
    }

    if (conditions.length === 0) {
      return { where: "1=1", params: [] };
    }

    const joinOperator = conditionGroup.type === "OR" ? " OR " : " AND ";
    const where = conditions.join(joinOperator);

    return { where, params };
  }

  /**
   * SQL 인젝션 방지를 위한 컬럼명 검증
   */
  static sanitizeColumnName(columnName: string): string {
    if (!/^[a-zA-Z0-9_.]+$/.test(columnName)) {
      throw new Error(`Invalid column name: ${columnName}`);
    }
    return columnName;
  }

  /**
   * SQL 인젝션 방지를 위한 테이블명 검증
   */
  static sanitizeTableName(tableName: string): string {
    if (!/^[a-zA-Z0-9_.]+$/.test(tableName)) {
      throw new Error(`Invalid table name: ${tableName}`);
    }
    return tableName;
  }

  /**
   * 조건 검증
   */
  static validateConditionGroup(conditionGroup: FlowConditionGroup): void {
    if (!conditionGroup) {
      throw new Error("Condition group is required");
    }

    if (!["AND", "OR"].includes(conditionGroup.type)) {
      throw new Error("Condition group type must be AND or OR");
    }

    if (!Array.isArray(conditionGroup.conditions)) {
      throw new Error("Conditions must be an array");
    }

    for (const condition of conditionGroup.conditions) {
      this.validateCondition(condition);
    }
  }

  /**
   * 개별 조건 검증
   */
  private static validateCondition(condition: FlowCondition): void {
    if (!condition.column) {
      throw new Error("Column name is required");
    }

    const validOperators = [
      "equals",
      "=",
      "not_equals",
      "!=",
      "in",
      "not_in",
      "greater_than",
      ">",
      "less_than",
      "<",
      "greater_than_or_equal",
      ">=",
      "less_than_or_equal",
      "<=",
      "is_null",
      "is_not_null",
      "like",
      "not_like",
    ];

    if (!validOperators.includes(condition.operator)) {
      throw new Error(`Invalid operator: ${condition.operator}`);
    }

    // is_null, is_not_null은 value가 필요 없음
    if (!["is_null", "is_not_null"].includes(condition.operator)) {
      if (condition.value === undefined || condition.value === null) {
        throw new Error(
          `Value is required for operator: ${condition.operator}`
        );
      }
    }

    // in, not_in은 배열이어야 함
    if (["in", "not_in"].includes(condition.operator)) {
      if (!Array.isArray(condition.value) || condition.value.length === 0) {
        throw new Error(
          `Operator ${condition.operator} requires a non-empty array value`
        );
      }
    }
  }
}
