import Handsontable from 'handsontable'

/**
 * DC.ROW_STATUS is deliberately not handled here yet - it needs the
 * EDIT_STATUS column (a live per-row M/A/D/U cell) to exist as something to
 * point a cell reference at, which is a separate, riskier change (touches
 * COLHEADERS, read independently by dc-validator.ts and
 * editor.component.ts's headerColumns).
 */
export interface FormulaVariableContext {
  /** Column names in grid order - index drives the cell-reference letter. */
  columnNames: string[]
  /** 0-based physical row index - drives the cell-reference row number. */
  rowIndex: number
  /** Current logged-in user - DC.USER_NAME resolves to this, quoted. */
  userName: string
  /** Original (pre-edit) value of the cell this rule applies to - DC.ORIG_VALUE resolves to this, quoted. */
  origValue: string | number | undefined
}

const escapeRegExpMetacharacters = (text: string): string =>
  text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')

/**
 * Replaces `token` with `replacement` wherever it has a leading and
 * trailing blank (a literal space, or the start/end of this span) - per
 * the issue's own rule, this is what lets `PRICE * VOLUME` substitute
 * correctly while `MATCH(PRICE)` (no surrounding blanks) does not, without
 * needing to know anything about function names.
 */
const substituteBoundedToken = (
  text: string,
  token: string,
  replacement: string
): string => {
  const pattern = new RegExp(
    `(?<=^|\\s)${escapeRegExpMetacharacters(token)}(?=$|\\s)`,
    'g'
  )

  return text.replace(pattern, replacement)
}

const quoteLiteral = (value: string | number | undefined): string =>
  `"${value ?? ''}"`

export const parseFormulaRule = (
  ruleValue: string,
  context: FormulaVariableContext
): string => {
  const quotedSpanPattern = /"[^"]*"|'[^']*'/g
  let result = ''
  let lastIndex = 0
  let match: RegExpExecArray | null

  const substituteUnquotedSpan = (span: string): string => {
    let substituted = substituteBoundedToken(
      span,
      'DC.USER_NAME',
      quoteLiteral(context.userName)
    )
    substituted = substituteBoundedToken(
      substituted,
      'DC.ORIG_VALUE',
      quoteLiteral(context.origValue)
    )

    context.columnNames.forEach((columnName, columnIndex) => {
      const cellRef = `${Handsontable.helper.spreadsheetColumnLabel(columnIndex)}${context.rowIndex + 1}`
      substituted = substituteBoundedToken(substituted, columnName, cellRef)
    })

    return substituted
  }

  while ((match = quotedSpanPattern.exec(ruleValue))) {
    result += substituteUnquotedSpan(ruleValue.slice(lastIndex, match.index))
    result += match[0]
    lastIndex = match.index + match[0].length
  }
  result += substituteUnquotedSpan(ruleValue.slice(lastIndex))

  return `=${result}`
}
