/**
 * 엑셀 함수를 지원하는 수식 평가기
 * 
 * 지원 함수:
 * - SUM(a, b, c, ...) : 합계
 * - AVERAGE(a, b, c, ...) : 평균
 * - MAX(a, b, c, ...) : 최대값
 * - MIN(a, b, c, ...) : 최소값
 * - ROUND(value, decimals) : 반올림
 * - CEIL(value) : 올림
 * - FLOOR(value) : 내림
 * - ABS(value) : 절대값
 * - IF(condition, trueValue, falseValue) : 조건문
 * - SQRT(value) : 제곱근
 * - POWER(base, exponent) : 거듭제곱
 */

// 지원되는 함수 목록
export const SUPPORTED_FUNCTIONS = [
  {
    name: 'SUM',
    description: '숫자들의 합계를 계산합니다',
    syntax: 'SUM(숫자1, 숫자2, ...)',
    example: 'SUM(W0, H0, 100) → W0 + H0 + 100',
  },
  {
    name: 'AVERAGE',
    description: '숫자들의 평균을 계산합니다',
    syntax: 'AVERAGE(숫자1, 숫자2, ...)',
    example: 'AVERAGE(W0, H0) → (W0 + H0) / 2',
  },
  {
    name: 'MAX',
    description: '숫자들 중 최대값을 반환합니다',
    syntax: 'MAX(숫자1, 숫자2, ...)',
    example: 'MAX(W0, H0, 1000) → 가장 큰 값',
  },
  {
    name: 'MIN',
    description: '숫자들 중 최소값을 반환합니다',
    syntax: 'MIN(숫자1, 숫자2, ...)',
    example: 'MIN(W0, H0, 100) → 가장 작은 값',
  },
  {
    name: 'ROUND',
    description: '숫자를 지정된 자릿수로 반올림합니다',
    syntax: 'ROUND(숫자, 자릿수)',
    example: 'ROUND(W0 / 3, 2) → 소수점 2자리까지',
  },
  {
    name: 'CEIL',
    description: '숫자를 올림합니다',
    syntax: 'CEIL(숫자)',
    example: 'CEIL(W0 / 10) → 올림 값',
  },
  {
    name: 'FLOOR',
    description: '숫자를 내림합니다',
    syntax: 'FLOOR(숫자)',
    example: 'FLOOR(W0 / 10) → 내림 값',
  },
  {
    name: 'ABS',
    description: '숫자의 절대값을 반환합니다',
    syntax: 'ABS(숫자)',
    example: 'ABS(-100) → 100',
  },
  {
    name: 'IF',
    description: '조건에 따라 다른 값을 반환합니다',
    syntax: 'IF(조건, 참일때값, 거짓일때값)',
    example: 'IF(W0 > 1000, 150, 100) → W0이 1000보다 크면 150, 아니면 100',
  },
  {
    name: 'SQRT',
    description: '숫자의 제곱근을 계산합니다',
    syntax: 'SQRT(숫자)',
    example: 'SQRT(W0) → W0의 제곱근',
  },
  {
    name: 'POWER',
    description: '숫자의 거듭제곱을 계산합니다',
    syntax: 'POWER(밑, 지수)',
    example: 'POWER(W0, 2) → W0의 제곱',
  },
];

// 함수 구현
const functions: Record<string, (...args: number[]) => number> = {
  SUM: (...args) => args.reduce((sum, val) => sum + val, 0),
  AVERAGE: (...args) => args.reduce((sum, val) => sum + val, 0) / args.length,
  MAX: (...args) => Math.max(...args),
  MIN: (...args) => Math.min(...args),
  ROUND: (value, decimals = 0) => {
    const multiplier = Math.pow(10, decimals);
    return Math.round(value * multiplier) / multiplier;
  },
  CEIL: (value) => Math.ceil(value),
  FLOOR: (value) => Math.floor(value),
  ABS: (value) => Math.abs(value),
  IF: (condition, trueValue, falseValue) => condition ? trueValue : falseValue,
  SQRT: (value) => Math.sqrt(value),
  POWER: (base, exponent) => Math.pow(base, exponent),
};

/**
 * 수식을 평가합니다
 * @param formula 평가할 수식 (예: "W0 + 140", "SUM(W0, H0, 100)")
 * @param variables 변수 값들 (예: { W0: 1000, H0: 2000 })
 * @returns 계산 결과
 */
export function evaluateFormula(formula: string, variables: Record<string, number>): number {
  try {
    // 수식 전처리
    let processedFormula = formula.trim();
    
    // 변수 치환
    Object.entries(variables).forEach(([key, value]) => {
      // 변수명을 정확히 매칭 (단어 경계 사용)
      const regex = new RegExp(`\\b${key}\\b`, 'g');
      processedFormula = processedFormula.replace(regex, String(value));
    });
    
    // 엑셀 함수 처리
    processedFormula = processFunctions(processedFormula);
    
    // 수식 평가 (안전하게)
    // eslint-disable-next-line no-new-func
    const result = new Function(`return ${processedFormula}`)();
    
    if (typeof result !== 'number' || isNaN(result)) {
      throw new Error('계산 결과가 유효한 숫자가 아닙니다');
    }
    
    return result;
  } catch (error) {
    throw new Error(`수식 평가 오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`);
  }
}

/**
 * 함수 호출을 JavaScript 코드로 변환
 */
function processFunctions(formula: string): string {
  let processed = formula;
  
  // 함수별로 처리
  Object.keys(functions).forEach(funcName => {
    // 함수 호출 찾기 (중첩 가능)
    const regex = new RegExp(`${funcName}\\s*\\(([^)]+)\\)`, 'gi');
    
    processed = processed.replace(regex, (match, argsStr) => {
      if (funcName === 'IF') {
        // IF 함수는 특별 처리 (조건, 참, 거짓)
        return processIfFunction(argsStr);
      } else {
        // 일반 함수
        return processMathFunction(funcName, argsStr);
      }
    });
  });
  
  return processed;
}

/**
 * IF 함수 처리
 */
function processIfFunction(argsStr: string): string {
  const args = splitFunctionArgs(argsStr);
  if (args.length !== 3) {
    throw new Error('IF 함수는 3개의 인자가 필요합니다: IF(조건, 참일때값, 거짓일때값)');
  }
  
  return `(${args[0]} ? ${args[1]} : ${args[2]})`;
}

/**
 * 수학 함수 처리
 */
function processMathFunction(funcName: string, argsStr: string): string {
  const args = splitFunctionArgs(argsStr);
  const func = functions[funcName];
  
  if (!func) {
    throw new Error(`지원하지 않는 함수: ${funcName}`);
  }
  
  // 함수를 JavaScript 코드로 변환
  switch (funcName) {
    case 'SUM':
      return `(${args.join(' + ')})`;
    case 'AVERAGE':
      return `((${args.join(' + ')}) / ${args.length})`;
    case 'MAX':
      return `Math.max(${args.join(', ')})`;
    case 'MIN':
      return `Math.min(${args.join(', ')})`;
    case 'ROUND':
      return `(Math.round(${args[0]} * Math.pow(10, ${args[1] || 0})) / Math.pow(10, ${args[1] || 0}))`;
    case 'CEIL':
      return `Math.ceil(${args[0]})`;
    case 'FLOOR':
      return `Math.floor(${args[0]})`;
    case 'ABS':
      return `Math.abs(${args[0]})`;
    case 'SQRT':
      return `Math.sqrt(${args[0]})`;
    case 'POWER':
      return `Math.pow(${args[0]}, ${args[1]})`;
    default:
      throw new Error(`지원하지 않는 함수: ${funcName}`);
  }
}

/**
 * 함수 인자 분리 (콤마로 구분, 괄호 안 콤마는 무시)
 */
function splitFunctionArgs(argsStr: string): string[] {
  const args: string[] = [];
  let currentArg = '';
  let parenthesesDepth = 0;
  
  for (const char of argsStr) {
    if (char === '(') {
      parenthesesDepth++;
      currentArg += char;
    } else if (char === ')') {
      parenthesesDepth--;
      currentArg += char;
    } else if (char === ',' && parenthesesDepth === 0) {
      args.push(currentArg.trim());
      currentArg = '';
    } else {
      currentArg += char;
    }
  }
  
  if (currentArg.trim()) {
    args.push(currentArg.trim());
  }
  
  return args;
}

/**
 * 수식 유효성 검사
 */
export function validateFormula(formula: string): { valid: boolean; error?: string } {
  try {
    // 빈 수식
    if (!formula || formula.trim() === '') {
      return { valid: false, error: '수식을 입력해주세요' };
    }
    
    // 기본 문법 검사
    const openParens = (formula.match(/\(/g) || []).length;
    const closeParens = (formula.match(/\)/g) || []).length;
    
    if (openParens !== closeParens) {
      return { valid: false, error: '괄호가 일치하지 않습니다' };
    }
    
    // 지원하지 않는 문자 검사
    const validPattern = /^[A-Z0-9_\s+\-*/().>,<=%&|!]+$/i;
    if (!validPattern.test(formula)) {
      return { valid: false, error: '지원하지 않는 문자가 포함되어 있습니다' };
    }
    
    // 테스트 평가 (더미 변수로)
    const dummyVariables: Record<string, number> = {};
    const variablePattern = /\b([A-Z][A-Z0-9_]*)\b/gi;
    const matches = formula.matchAll(variablePattern);
    
    for (const match of matches) {
      const varName = match[1];
      // 함수명이 아닌 경우에만 더미 변수 추가
      if (!Object.keys(functions).includes(varName.toUpperCase())) {
        dummyVariables[varName] = 100;
      }
    }
    
    evaluateFormula(formula, dummyVariables);
    
    return { valid: true };
  } catch (error) {
    return {
      valid: false,
      error: error instanceof Error ? error.message : '수식이 유효하지 않습니다',
    };
  }
}

/**
 * 수식에서 사용된 변수 추출
 */
export function extractVariables(formula: string): string[] {
  const variables = new Set<string>();
  const variablePattern = /\b([A-Z][A-Z0-9_]*)\b/gi;
  const matches = formula.matchAll(variablePattern);
  
  for (const match of matches) {
    const varName = match[1];
    // 함수명이 아닌 경우에만 변수로 간주
    if (!Object.keys(functions).includes(varName.toUpperCase())) {
      variables.add(varName);
    }
  }
  
  return Array.from(variables);
}
