export type CellValue = string | number | boolean | null | undefined

export interface ExcelFunction {
  (...args: any[]): CellValue
}

// Волатильные функции - пересчитываются при любом изменении
export const VOLATILE_FUNCTIONS = new Set(['СЛУЧ', 'СЛУЧМЕЖДУ', 'СЛУЧДР'])

export const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
  СЛУЧ: () => Math.random(),
  СЛУЧМЕЖДУ: (низ: number, верх: number) =>
    Math.floor(Math.random() * (верх - низ + 1)) + низ,
  СЛУЧДР: (низ: number, верх: number, знаков: number = 0) => {
    // Проверка валидности входных параметров
    if (
      typeof низ !== 'number' ||
      typeof верх !== 'number' ||
      typeof знаков !== 'number'
    ) {
      return '#ЗНАЧ!' // Ошибка значения
    }

    if (!isFinite(низ) || !isFinite(верх) || !isFinite(знаков)) {
      return '#ЧИСЛО!' // Ошибка числа
    }

    // Если низ больше верха, меняем местами
    if (низ > верх) {
      ;[низ, верх] = [верх, низ]
    }

    // Если низ равен верху, возвращаем это значение
    if (низ === верх) {
      const множитель = Math.pow(10, Math.max(0, Math.floor(знаков)))
      return Math.round(низ * множитель) / множитель
    }

    // Ограничиваем количество знаков после запятой (максимум 15 для точности JavaScript)
    const ограниченныеЗнаки = Math.max(0, Math.min(15, Math.floor(знаков)))

    // Генерируем случайное число
    const случайное = Math.random() * (верх - низ) + низ
    const множитель = Math.pow(10, ограниченныеЗнаки)

    // Округляем и возвращаем результат
    const результат = Math.round(случайное * множитель) / множитель

    // Проверяем, что результат в допустимых пределах
    return Math.max(низ, Math.min(верх, результат))
  },
  СУММ: (...args: any[]) => {
    const flatArgs = args.flat()
    return flatArgs.reduce(
      (sum, val) => sum + (typeof val === 'number' ? val : 0),
      0
    )
  },
  СРЗНАЧ: (...args: any[]) => {
    const flatArgs = args.flat()
    const validArgs = flatArgs.filter(val => typeof val === 'number')
    return validArgs.length > 0
      ? validArgs.reduce((sum, val) => sum + val, 0) / validArgs.length
      : 0
  },
  СЧЁТ: (...args: any[]) => {
    const flatArgs = args.flat()
    return flatArgs.filter(val => typeof val === 'number').length
  },
  МАКС: (число1: number, число2: number) => {
    // Проверка валидности входных параметров
    if (typeof число1 !== 'number' || typeof число2 !== 'number') {
      return '#ЗНАЧ!' // Ошибка значения
    }

    if (!isFinite(число1) || !isFinite(число2)) {
      return '#ЧИСЛО!' // Ошибка числа
    }

    // Возвращаем максимальное из двух чисел
    return Math.max(число1, число2)
  },
  МИН: (...args: any[]) => {
    const flatArgs = args.flat()
    const validArgs = flatArgs.filter(val => typeof val === 'number')
    return validArgs.length > 0 ? Math.min(...validArgs) : 0
  },
  ЕСЛИ: (условие: boolean, истина: CellValue, ложь: CellValue) =>
    условие ? истина : ложь,
  И: (...args: boolean[]) => args.every(Boolean),
  ИЛИ: (...args: boolean[]) => args.some(Boolean),
  НЕ: (value: boolean) => !value,
  СЦЕПИТЬ: (...args: string[]) => args.join(''),
  ДЛСТР: (текст: string) => (текст ? текст.length : 0),
  ПРОПИСН: (текст: string) => (текст ? текст.toUpperCase() : ''),
  СТРОЧН: (текст: string) => (текст ? текст.toLowerCase() : ''),
  СЖПРОБЕЛЫ: (текст: string) => (текст ? текст.trim() : ''),

  // Функции округления
  ОКРУГЛ: (число: number, разряды: number = 0) => {
    const factor = Math.pow(10, разряды)
    return Math.round(число * factor) / factor
  },
  ОКРУГЛВВЕРХ: (число: number, разряды: number = 0) => {
    const factor = Math.pow(10, разряды)
    return Math.ceil(число * factor) / factor
  },
  ОКРУГЛВНИЗ: (число: number, разряды: number = 0) => {
    const factor = Math.pow(10, разряды)
    return Math.floor(число * factor) / factor
  },
  ОКРВВЕРХ: (число: number, кратное: number = 1) => {
    if (кратное === 0) return 0
    return Math.ceil(число / кратное) * кратное
  },
  ОКРВНИЗ: (число: number, кратное: number = 1) => {
    if (кратное === 0) return 0
    return Math.floor(число / кратное) * кратное
  },
  ЦЕЛОЕ: (число: number) => Math.floor(число),
  ОТБР: (число: number, разряды: number = 0) => {
    const factor = Math.pow(10, разряды)
    return Math.trunc(число * factor) / factor
  },
  ОКРУГЛТ: (число: number, кратное: number) => {
    if (кратное === 0) return 0
    return Math.round(число / кратное) * кратное
  },

  // Функция корня с проверками
  КОРЕНЬ: (число: number) => {
    // Проверка валидности входного параметра
    if (typeof число !== 'number') {
      return '#ЗНАЧ!' // Ошибка значения
    }

    if (!isFinite(число)) {
      return '#ЧИСЛО!' // Ошибка числа
    }

    // Проверка на отрицательное число
    if (число < 0) {
      return '#ЧИСЛО!' // Корень из отрицательного числа
    }

    // Возвращаем квадратный корень
    return Math.sqrt(число)
  },

  // Функция возведения в степень
  СТЕПЕНЬ: (число: number, степень: number) => {
    if (typeof число !== 'number' || typeof степень !== 'number') {
      return '#ЗНАЧ!'
    }

    if (!isFinite(число) || !isFinite(степень)) {
      return '#ЧИСЛО!'
    }

    const результат = Math.pow(число, степень)

    if (!isFinite(результат)) {
      return '#ЧИСЛО!'
    }

    return результат
  },

  // Функция сравнения БОЛЬШЕ (>)
  БОЛЬШЕ: (число1: number, число2: number) => {
    if (typeof число1 !== 'number' || typeof число2 !== 'number') {
      return '#ЗНАЧ!'
    }

    if (!isFinite(число1) || !isFinite(число2)) {
      return '#ЧИСЛО!'
    }

    return число1 > число2
  },

  // Функция сравнения МЕНЬШЕ (<)
  МЕНЬШЕ: (число1: number, число2: number) => {
    if (typeof число1 !== 'number' || typeof число2 !== 'number') {
      return '#ЗНАЧ!'
    }

    if (!isFinite(число1) || !isFinite(число2)) {
      return '#ЧИСЛО!'
    }

    return число1 < число2
  },

  // Функция проверки на пустоту
  ЕПУСТО: (значение: CellValue) => {
    return значение === null || значение === undefined || значение === ''
  },

  // Функция модуля числа
  ABS: (число: number) => {
    // Проверка валидности входного параметра
    if (typeof число !== 'number') {
      return '#ЗНАЧ!' // Ошибка значения
    }

    if (!isFinite(число)) {
      return '#ЧИСЛО!' // Ошибка числа
    }

    // Возвращаем абсолютное значение
    return Math.abs(число)
  },
}
