import {CONDITIONAL_OPERATORS, RESERVED_NAMES} from "../internal/internalConstants.js";
import {ExpressionQualifier} from "./expression/ExpressionQualifier.js";


/**
 * @description
 * Used for variable or conditional expressions
 * <b>Learn more:</b> {@link https://cloudinary.com/documentation/user_defined_variables#arithmetic_expressions|Arithmetic expressions }
 * @namespace Expression
 * @memberOf Qualifiers
 */


/**
 * @summary qualifier
 * @memberOf Qualifiers.Expression
 * @return {Qualifiers.Expression.ExpressionQualifier}
 */
function expression(exp: string): ExpressionQualifier {
  // Prepare the CONDITIONAL_OPERATORS object to be used in a regex
  // Properly escape |, +, ^ and *
  // This step also adds a regex space ( \s ) around each operator, since these are only replaced when wrapped with spaces
  // $foo * $bar is replaced to $foo_mul_$bar
  // $foo*bar is treated AS-IS.
  const reservedOperatorList = Object.keys(CONDITIONAL_OPERATORS).map((key) => {
    return `\\s${key.replace(/(\*|\+|\^|\|)/g, '\\$1')}\\s`;
  });

  // reservedOperatorList is now an array of values, joining with | creates the regex list
  const regexSafeOperatorList = reservedOperatorList.join('|');
  const operatorsReplaceRE = new RegExp(`(${regexSafeOperatorList})`, "g");

  // First, we replace all the operators
  // Notice how we pad the matched operators with `_`, this is following the step above.
  // This turns $foo * $bar into $foo_mul_$bar (notice how the spaces were replaced with an underscore
  const stringWithOperators = exp.toString()
    .replace(operatorsReplaceRE, (match: string) => {
      // match contains spaces around the expression, we need to trim it as the original list
      // does not contain spaces.
      return `_${CONDITIONAL_OPERATORS[match.trim() as keyof typeof CONDITIONAL_OPERATORS]}_`;
    });


  // Handle reserved names (width, height, etc.)
  const ReservedNames = Object.keys(RESERVED_NAMES);
  const regexSafeReservedNameList = ReservedNames.join('|');
  // Gather all statements that begin with a dollar sign, underscore or a space
  // Gather all RESERVED NAMES
  // $foo_bar is matched
  // height is matched
  const reservedNamesRE = new RegExp(`(\\$_*[^_ ]+)|${regexSafeReservedNameList}`, "g");

  // Since this regex captures both user variables and our reserved keywords, we need to add some logic in the replacer
  const stringWithVariables = stringWithOperators.replace(reservedNamesRE, (match) => {
    // Do not do anything to user variables (anything starting with $)
    if (match.startsWith('$')) {
      return match;
    } else {
      return RESERVED_NAMES[match as keyof typeof RESERVED_NAMES] || match;
    }
  });

  // Serialize remaining spaces with an underscore
  const finalExpressionString = stringWithVariables.replace(/\s/g, '_');

  return new ExpressionQualifier(finalExpressionString);
}

// as a namespace
const Expression = {
  expression
};

export {
  Expression,
  expression
};