// 수식 평가 엔진 - 조건문, 범위, 수학 함수 지원

export interface FormulaVariable {
  [key: string]: number;
}

/**
 * 수식 평가 엔진
 * 지원 기능:
 * - 기본 수학 연산: +, -, *, /, %, ^(거듭제곱), (괄호)
 * - 비교 연산자: >, <, >=, <=, ==, !=
 * - 논리 연산자: &&, ||
 * - 조건문: IF(조건, 참값, 거짓값)
 * - 수학 함수: CEIL(값), FLOOR(값), ROUND(값), ABS(값), SQRT(값), MAX(값1, 값2, ...), MIN(값1, 값2, ...)
 * - 범위 함수: RANGE(값, min, max, 결과) - 값이 범위 안에 있으면 결과 반환
 * 
 * 예시:
 * - "IF(W1 > 3000, CEIL(W1 / 3000), 1)"
 * - "RANGE(S, 0, 1219, '1219') || RANGE(S, 1219, 2438, '2438') || '규격 초과'"
 * - "2 + FLOOR((W1 - 500) / 1000)"
 */

export class FormulaEngine {
  /**
   * 수식 평가
   */
  static evaluate(formula: string, variables: FormulaVariable): any {
    try {
      // 변수 치환
      let processedFormula = formula;
      
      // 함수 처리
      processedFormula = this.processFunctions(processedFormula, variables);
      
      // 변수 치환
      for (const [key, value] of Object.entries(variables)) {
        const regex = new RegExp(`\\b${key}\\b`, 'g');
        processedFormula = processedFormula.replace(regex, String(value));
      }
      
      // 평가 (안전한 eval 대신 Function 사용)
      const result = this.safeEval(processedFormula);
      return result;
    } catch (error) {
      console.error('수식 평가 오류:', error, '수식:', formula);
      return null;
    }
  }

  /**
   * 함수 처리 (재귀적으로 처리)
   */
  private static processFunctions(formula: string, variables: FormulaVariable): string {
    let processed = formula;
    
    // IF 함수 처리
    processed = this.processIfFunction(processed, variables);
    
    // RANGE 함수 처리
    processed = this.processRangeFunction(processed, variables);
    
    // 수학 함수 처리
    processed = this.processMathFunctions(processed, variables);
    
    return processed;
  }

  /**
   * IF 함수 처리: IF(조건, 참값, 거짓값)
   * 중첩된 IF 문도 처리 가능
   */
  private static processIfFunction(formula: string, variables: FormulaVariable): string {
    let result = formula;
    let maxIterations = 100; // 무한 루프 방지
    let iterations = 0;
    
    while (result.includes('IF(') && iterations < maxIterations) {
      iterations++;
      
      // 가장 안쪽의 IF 문부터 처리 (중첩된 IF 처리)
      const ifMatch = this.findInnermostIf(result);
      
      if (!ifMatch) break;
      
      const { fullMatch, condition, trueValue, falseValue } = ifMatch;
      
      // 조건 평가
      const conditionResult = this.evaluateCondition(condition, variables);
      
      // 결과 선택
      const selectedValue = conditionResult ? trueValue : falseValue;
      
      // 결과 치환
      result = result.replace(fullMatch, selectedValue);
    }
    
    return result;
  }

  /**
   * 가장 안쪽의 IF 문 찾기 (중첩된 IF 처리용)
   */
  private static findInnermostIf(formula: string): { fullMatch: string; condition: string; trueValue: string; falseValue: string } | null {
    const ifStart = formula.indexOf('IF(');
    if (ifStart === -1) return null;
    
    let depth = 0;
    let argStart = ifStart + 3;
    let commaPositions: number[] = [];
    
    for (let i = argStart; i < formula.length; i++) {
      const char = formula[i];
      
      if (char === '(') {
        depth++;
      } else if (char === ')') {
        if (depth === 0) {
          // IF 문의 끝을 찾음
          const fullMatch = formula.substring(ifStart, i + 1);
          
          if (commaPositions.length < 2) {
            console.error('IF 함수의 인자가 부족합니다:', fullMatch);
            return null;
          }
          
          const condition = formula.substring(argStart, commaPositions[0]).trim();
          const trueValue = formula.substring(commaPositions[0] + 1, commaPositions[1]).trim();
          const falseValue = formula.substring(commaPositions[1] + 1, i).trim();
          
          return { fullMatch, condition, trueValue, falseValue };
        }
        depth--;
      } else if (char === ',' && depth === 0) {
        commaPositions.push(i);
      }
    }
    
    return null;
  }

  /**
   * RANGE 함수 처리: RANGE(값, min, max, 결과)
   * 값이 min < 값 <= max 범위에 있으면 결과 반환, 아니면 null
   */
  private static processRangeFunction(formula: string, variables: FormulaVariable): string {
    const rangeRegex = /RANGE\s*\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^)]+)\s*\)/gi;
    
    let result = formula;
    let match;
    
    while ((match = rangeRegex.exec(formula)) !== null) {
      const valueStr = match[1].trim();
      const minStr = match[2].trim();
      const maxStr = match[3].trim();
      const resultValue = match[4].trim();
      
      // 값 평가
      const value = this.evaluateExpression(valueStr, variables);
      const min = this.evaluateExpression(minStr, variables);
      const max = this.evaluateExpression(maxStr, variables);
      
      // 범위 체크 (min < value <= max)
      if (value > min && value <= max) {
        result = result.replace(match[0], resultValue);
      } else {
        result = result.replace(match[0], 'null');
      }
    }
    
    return result;
  }

  /**
   * 수학 함수 처리 (중첩된 괄호 지원)
   */
  private static processMathFunctions(formula: string, variables: FormulaVariable): string {
    let result = formula;
    let maxIterations = 100;
    let iterations = 0;
    
    const mathFunctions = ['CEIL', 'FLOOR', 'ROUND', 'ABS', 'SQRT', 'MAX', 'MIN'];
    
    while (iterations < maxIterations) {
      iterations++;
      let changed = false;
      
      for (const funcName of mathFunctions) {
        const funcMatch = this.findInnermostFunction(result, funcName);
        if (!funcMatch) continue;
        
        const { fullMatch, args } = funcMatch;
        let replacement: string;
        
        try {
          if (funcName === 'MAX' || funcName === 'MIN') {
            // 쉼표로 구분된 여러 인자
            const values = args.split(',').map(arg => this.evaluateExpression(arg.trim(), variables));
            const resultValue = funcName === 'MAX' ? Math.max(...values) : Math.min(...values);
            replacement = String(resultValue);
          } else {
            // 단일 인자
            const value = this.evaluateExpression(args, variables);
            let resultValue: number;
            
            switch (funcName) {
              case 'CEIL':
                resultValue = Math.ceil(value);
                break;
              case 'FLOOR':
                resultValue = Math.floor(value);
                break;
              case 'ROUND':
                resultValue = Math.round(value);
                break;
              case 'ABS':
                resultValue = Math.abs(value);
                break;
              case 'SQRT':
                resultValue = Math.sqrt(value);
                break;
              default:
                resultValue = value;
            }
            
            replacement = String(resultValue);
          }
          
          result = result.replace(fullMatch, replacement);
          changed = true;
          break; // 한 번에 하나씩 처리
        } catch (error) {
          console.error(`${funcName} 함수 처리 오류:`, error, 'args:', args);
          // 오류 발생 시 원래 값 유지하고 계속 진행
          break;
        }
      }
      
      if (!changed) break;
    }
    
    return result;
  }

  /**
   * 가장 안쪽의 함수 찾기
   */
  private static findInnermostFunction(formula: string, funcName: string): { fullMatch: string; args: string } | null {
    const funcStart = formula.toUpperCase().indexOf(funcName + '(');
    if (funcStart === -1) return null;
    
    let depth = 0;
    let argStart = funcStart + funcName.length + 1;
    
    for (let i = argStart; i < formula.length; i++) {
      const char = formula[i];
      
      if (char === '(') {
        depth++;
      } else if (char === ')') {
        if (depth === 0) {
          // 함수의 끝을 찾음
          const fullMatch = formula.substring(funcStart, i + 1);
          const args = formula.substring(argStart, i);
          return { fullMatch, args };
        }
        depth--;
      }
    }
    
    return null;
  }

  /**
   * 조건 평가
   */
  private static evaluateCondition(condition: string, variables: FormulaVariable): boolean {
    // 변수 치환
    let processedCondition = condition;
    for (const [key, value] of Object.entries(variables)) {
      const regex = new RegExp(`\\b${key}\\b`, 'g');
      processedCondition = processedCondition.replace(regex, String(value));
    }
    
    try {
      return this.safeEval(processedCondition);
    } catch (error) {
      console.error('조건 평가 오류:', error, '조건:', condition);
      return false;
    }
  }

  /**
   * 표현식 평가 (숫자 반환)
   */
  private static evaluateExpression(expr: string, variables: FormulaVariable): number {
    // 변수 치환
    let processed = expr;
    for (const [key, value] of Object.entries(variables)) {
      const regex = new RegExp(`\\b${key}\\b`, 'g');
      processed = processed.replace(regex, String(value));
    }
    
    // 수학 함수 처리
    processed = this.processMathFunctions(processed, variables);
    
    try {
      return this.safeEval(processed);
    } catch (error) {
      console.error('표현식 평가 오류:', error, '표현식:', expr);
      return 0;
    }
  }

  /**
   * 안전한 eval (제한된 범위에서만 동작)
   */
  private static safeEval(expression: string): any {
    // 위험한 문자열 체크
    if (expression.includes('import') || 
        expression.includes('require') || 
        expression.includes('eval') ||
        expression.includes('Function') ||
        expression.includes('window') ||
        expression.includes('document') ||
        expression.includes('process')) {
      throw new Error('보안 위험: 허용되지 않는 표현식');
    }
    
    // null 처리
    if (expression.trim() === 'null') {
      return null;
    }
    
    // 문자열 처리 (따옴표로 감싸진 경우)
    if ((expression.startsWith("'") && expression.endsWith("'")) ||
        (expression.startsWith('"') && expression.endsWith('"'))) {
      return expression.slice(1, -1);
    }
    
    // Function을 사용한 안전한 평가
    const func = new Function(`'use strict'; return (${expression})`);
    return func();
  }

  /**
   * 수식 유효성 검증
   */
  static validate(formula: string): { valid: boolean; error?: string } {
    try {
      // 기본 구문 체크
      if (!formula || formula.trim() === '') {
        return { valid: false, error: '수식이 비어있습니다.' };
      }
      
      // 괄호 균형 체크
      const openCount = (formula.match(/\(/g) || []).length;
      const closeCount = (formula.match(/\)/g) || []).length;
      if (openCount !== closeCount) {
        return { valid: false, error: '괄호가 균형을 이루지 않습니다.' };
      }
      
      // 테스트 평가
      const testVars: FormulaVariable = { W0: 3000, H0: 4000, W1: 3050, H1: 4050, S: 3050 };
      this.evaluate(formula, testVars);
      
      return { valid: true };
    } catch (error) {
      return { valid: false, error: error instanceof Error ? error.message : '알 수 없는 오류' };
    }
  }
}

/**
 * 범위 규칙 정의
 */
export interface RangeRule {
  id: string;
  minValue: number; // 최소값 (초과)
  maxValue: number; // 최대값 (이하)
  result: string | number; // 결과값
  description?: string;
}

/**
 * 수식 규칙
 */
export interface FormulaRule {
  id: string;
  ruleName: string; // 규칙명 (예: "케이스판재 적용 기준")
  category: string; // 카테고리
  inputVariable: string; // 입력 변수 (예: "S")
  ruleType: 'range' | 'formula' | 'fixed'; // 범위 | 수식 | 고정값
  
  // 범위 규칙 (ruleType === 'range')
  ranges?: RangeRule[];
  
  // 수식 규칙 (ruleType === 'formula')
  formula?: string;
  
  // 고정값 (ruleType === 'fixed')
  fixedValue?: string | number;
  
  unit?: string;
  description?: string;
  status: 'active' | 'inactive';
  createdAt: string;
  updatedAt: string;
}
