import Decimal from 'decimal.js'

type MaybeNumber = number | null | undefined
type DecimalInput = Decimal.Value

export const toDecimal = (value: DecimalInput) => new Decimal(value)

export const roundTo = (value: DecimalInput, decimalPlaces = 2) =>
  new Decimal(value).toDecimalPlaces(decimalPlaces, Decimal.ROUND_HALF_UP).toNumber()

export const isFiniteNumber = (value: unknown): value is number =>
  typeof value === 'number' && Number.isFinite(value)

export const toFiniteNumberOrNull = (value: unknown): number | null =>
  isFiniteNumber(value) ? value : null

export const toFiniteNumberOrZero = (value: unknown): number =>
  toFiniteNumberOrNull(value) ?? 0

export const toFiniteNumber = (value: unknown): number | null => {
  if (isFiniteNumber(value)) return value
  if (typeof value === 'string') {
    const trimmed = value.trim()
    if (!trimmed) return null
    const numeric = Number(trimmed)
    return Number.isFinite(numeric) ? numeric : null
  }
  return null
}

const sumFiniteValues = (values: Iterable<unknown>) => {
  let total = new Decimal(0)
  for (const value of values) {
    if (!isFiniteNumber(value)) continue
    total = total.plus(value)
  }
  return total.toNumber()
}

export const addNumbers = (...values: MaybeNumber[]) => sumFiniteValues(values)

export const sumByNumber = <T>(list: T[], pick: (item: T) => MaybeNumber) => {
  let total = new Decimal(0)
  for (const item of list) {
    const value = pick(item)
    if (!isFiniteNumber(value)) continue
    total = total.plus(value)
  }
  return total.toNumber()
}

export const sumNullableNumbers = (values: MaybeNumber[]): number | null => {
  const validValues = values.filter(isFiniteNumber)
  if (validValues.length === 0) return null
  return addNumbers(...validValues)
}

export const decimalAggSum = (params: { values?: unknown[] }) => {
  const values = params.values || []
  let hasFinite = false
  for (const value of values) {
    if (!isFiniteNumber(value)) continue
    hasFinite = true
    break
  }
  if (!hasFinite) return null
  return sumFiniteValues(values)
}

class DecimalExpressionParser {
  private readonly source: string
  private index = 0

  constructor(source: string) {
    this.source = source
  }

  parse(): Decimal | null {
    const value = this.parseExpression()
    this.skipWhitespace()
    if (!value || this.index !== this.source.length) return null
    return value
  }

  private parseExpression(): Decimal | null {
    let value = this.parseTerm()
    if (!value) return null

    while (true) {
      this.skipWhitespace()
      const operator = this.peek()
      if (operator !== '+' && operator !== '-') return value
      this.index += 1
      const right = this.parseTerm()
      if (!right) return null
      value = operator === '+' ? value.plus(right) : value.minus(right)
    }
  }

  private parseTerm(): Decimal | null {
    let value = this.parseFactor()
    if (!value) return null

    while (true) {
      this.skipWhitespace()
      const operator = this.peek()
      if (operator !== '*' && operator !== '/') return value
      this.index += 1
      const right = this.parseFactor()
      if (!right) return null
      if (operator === '/') {
        if (right.isZero()) return null
        value = value.div(right)
        continue
      }
      value = value.mul(right)
    }
  }

  private parseFactor(): Decimal | null {
    this.skipWhitespace()
    const current = this.peek()
    if (current === '+') {
      this.index += 1
      return this.parseFactor()
    }
    if (current === '-') {
      this.index += 1
      const value = this.parseFactor()
      return value ? value.neg() : null
    }
    if (current === '(') {
      this.index += 1
      const value = this.parseExpression()
      this.skipWhitespace()
      if (!value || this.peek() !== ')') return null
      this.index += 1
      return value
    }
    return this.parseNumber()
  }

  private parseNumber(): Decimal | null {
    this.skipWhitespace()
    const start = this.index
    let hasDigit = false
    let hasDot = false

    while (this.index < this.source.length) {
      const char = this.source[this.index]
      if (char >= '0' && char <= '9') {
        hasDigit = true
        this.index += 1
        continue
      }
      if (char === '.' && !hasDot) {
        hasDot = true
        this.index += 1
        continue
      }
      break
    }

    if (!hasDigit) {
      this.index = start
      return null
    }

    const literal = this.source.slice(start, this.index)
    try {
      return new Decimal(literal)
    } catch {
      return null
    }
  }

  private skipWhitespace() {
    while (this.index < this.source.length && /\s/.test(this.source[this.index])) {
      this.index += 1
    }
  }

  private peek() {
    return this.source[this.index]
  }
}

// 支持 + - * / () 的高精度表达式计算，用于数字输入框和表格 valueParser。
export const evaluateDecimalExpression = (value: string): number | null => {
  const trimmed = String(value || '').trim()
  if (!trimmed) return null
  try {
    const parsed = new DecimalExpressionParser(trimmed).parse()
    return parsed ? parsed.toNumber() : null
  } catch {
    return null
  }
}
