<template>
  <div class="barneo-range-selector">
    <div class="barneo-range-track">
      <div 
        class="barneo-range-fill" 
        :style="{ 
          left: `${minPercentage}%`, 
          width: `${rangeWidth}%` 
        }"
      ></div>
             <input
         ref="minSlider"
         type="range"
         :min="min"
         :max="max"
         :value="progressiveToLinear(currentMin)"
         class="barneo-range-slider barneo-range-slider--min"
         @input="handleMinChange"
         @mousedown="handleSliderMouseDown"
         @mouseup="handleSliderMouseUp"
         @touchend="handleSliderMouseUp"
       />
       <input
         ref="maxSlider"
         type="range"
         :min="min"
         :max="max"
         :value="progressiveToLinear(currentMax)"
         class="barneo-range-slider barneo-range-slider--max"
         @input="handleMaxChange"
         @mousedown="handleSliderMouseDown"
         @mouseup="handleSliderMouseUp"
         @touchend="handleSliderMouseUp"
       />
    </div>
    
    <div class="barneo-range-inputs">
      <div class="barneo-range-input-group">
        <label class="barneo-range-input-label">ОТ</label>
        <input
          ref="minInput"
          type="number"
          :value="getInputValue(currentMin)"
          class="barneo-range-input"
          @input="handleMinInputChange"
          @blur="handleMinInputBlur"
          @keyup.enter="handleMinInputBlur"
        />
      </div>
      
      <div class="barneo-range-separator"></div>
      
      <div class="barneo-range-input-group">
        <label class="barneo-range-input-label">ДО</label>
        <input
          ref="maxInput"
          type="number"
          :value="getInputValue(currentMax)"
          class="barneo-range-input"
          @input="handleMaxInputChange"
          @blur="handleMaxInputBlur"
          @keyup.enter="handleMaxInputBlur"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue';

interface Props {
  min: number;
  max: number;
  value?: [number, number];
  step?: number;
  formatValue?: (value: number) => string;
}

const props = withDefaults(defineProps<Props>(), {
  step: 1,
  formatValue: (value: number) => value.toString()
});

const emit = defineEmits<{
  'update:value': [value: [number, number]];
  'change': [value: [number, number]];
}>();

const minSlider = ref<HTMLInputElement>();
const maxSlider = ref<HTMLInputElement>();
const minInput = ref<HTMLInputElement>();
const maxInput = ref<HTMLInputElement>();

const currentMin = ref(props.value?.[0] ?? props.min);
const currentMax = ref(props.value?.[1] ?? props.max);

// Флаг для отслеживания состояния перетаскивания
const isDragging = ref(false);
// Временные значения для хранения изменений во время перетаскивания
const tempMin = ref(currentMin.value);
const tempMax = ref(currentMax.value);

// Отслеживаем изменения в инпутах для предотвращения лишних запросов
const inputMinChanged = ref(false);
const inputMaxChanged = ref(false);

/**
 * Преобразует линейную позицию ползунка в прогрессивное значение
 * 
 * Прогрессивная шкала работает следующим образом:
 * - 0-50% позиции: покрывает первые 25% значений (4x медленнее)
 * - 50-75% позиции: покрывает следующие 25% значений (2x медленнее) 
 * - 75-100% позиции: покрывает последние 50% значений (линейно)
 * 
 * Это позволяет пользователям более точно выбирать значения в нижнем диапазоне
 */
const linearToProgressive = (linearValue: number): number => {
  const range = props.max - props.min;
  const normalizedValue = (linearValue - props.min) / range;
  
  if (normalizedValue <= 0.5) {
    // Первые 50%: значения увеличиваются в 4 раза медленнее
    // Используем квадратичную функцию для более плавного перехода
    const factor = normalizedValue * 2; // 0-1 для первой половины
    return props.min + (range * 0.25) * (factor * factor);
  } else if (normalizedValue <= 0.75) {
    // 50%-75%: значения увеличиваются в 2 раза медленнее
    const factor = (normalizedValue - 0.5) * 4; // 0-1 для второй четверти
    return props.min + (range * 0.25) + (range * 0.25) * (factor * factor);
  } else {
    // 75%-100%: линейное увеличение
    const factor = (normalizedValue - 0.75) * 4; // 0-1 для последней четверти
    return props.min + (range * 0.5) + (range * 0.5) * factor;
  }
};

/**
 * Преобразует прогрессивное значение обратно в линейную позицию для отображения
 * Это обратная функция к linearToProgressive
 */
const progressiveToLinear = (progressiveValue: number): number => {
  const range = props.max - props.min;
  const normalizedValue = (progressiveValue - props.min) / range;
  
  if (normalizedValue <= 0.25) {
    // Первые 25% прогрессивного значения
    const factor = Math.sqrt(normalizedValue / 0.25);
    return props.min + (range * 0.5) * factor;
  } else if (normalizedValue <= 0.5) {
    // 25%-50% прогрессивного значения
    const factor = Math.sqrt((normalizedValue - 0.25) / 0.25);
    return props.min + (range * 0.5) + (range * 0.25) * factor;
  } else {
    // 50%-100% прогрессивного значения
    const factor = (normalizedValue - 0.5) / 0.5;
    return props.min + (range * 0.75) + (range * 0.25) * factor;
  }
};

// Вычисляем процентные позиции для заполнения (используем линейные позиции для отображения)
const minPercentage = computed(() => {
  const linearMin = progressiveToLinear(currentMin.value);
  return ((linearMin - props.min) / (props.max - props.min)) * 100;
});

const maxPercentage = computed(() => {
  const linearMax = progressiveToLinear(currentMax.value);
  return ((linearMax - props.min) / (props.max - props.min)) * 100;
});

const rangeWidth = computed(() => {
  return maxPercentage.value - minPercentage.value;
});

// Функция для получения значения для number input
const getInputValue = (value: number): number => {
  // Если это поле цены, конвертируем копейки в рубли
  if (props.formatValue.toString().includes('₽')) {
    return Math.floor(value / 100);
  }
  return value;
};

// Обработчики изменения значений
const handleMinChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  const linearValue = Number(target.value);
  
  // Преобразуем линейную позицию в прогрессивное значение
  const progressiveValue = linearToProgressive(linearValue);
  const newMin = Math.min(progressiveValue, currentMax.value - props.step);
  
  if (isDragging.value) {
    // Во время перетаскивания обновляем только временные значения
    tempMin.value = newMin;
    currentMin.value = newMin;
  } else {
    // Если не перетаскиваем, сразу обновляем и отправляем событие
    currentMin.value = newMin;
    emit('update:value', [newMin, currentMax.value]);
    emit('change', [newMin, currentMax.value]);
  }
};

const handleMaxChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  const linearValue = Number(target.value);
  
  // Преобразуем линейную позицию в прогрессивное значение
  const progressiveValue = linearToProgressive(linearValue);
  const newMax = Math.max(progressiveValue, currentMin.value + props.step);
  
  if (isDragging.value) {
    // Во время перетаскивания обновляем только временные значения
    tempMax.value = newMax;
    currentMax.value = newMax;
  } else {
    // Если не перетаскиваем, сразу обновляем и отправляем событие
    currentMax.value = newMax;
    emit('update:value', [currentMin.value, newMax]);
    emit('change', [currentMin.value, newMax]);
  }
};

// Предотвращаем перекрытие ползунков и начинаем отслеживание перетаскивания
const handleSliderMouseDown = (event: Event) => {
  const target = event.target as HTMLInputElement;
  
  // Если ползунки сближаются, немного разводим их
  if (target === minSlider.value && currentMin.value >= currentMax.value - props.step) {
    currentMax.value = Math.min(currentMax.value + props.step, props.max);
  } else if (target === maxSlider.value && currentMax.value <= currentMin.value + props.step) {
    currentMin.value = Math.max(currentMin.value - props.step, props.min);
  }
  
  // Начинаем отслеживание перетаскивания
  isDragging.value = true;
  tempMin.value = currentMin.value;
  tempMax.value = currentMax.value;
};

// Завершаем перетаскивание и отправляем событие
const handleSliderMouseUp = (event: Event) => {
  if (isDragging.value) {
    isDragging.value = false;
    
    // Отправляем событие только если значения изменились
    if (tempMin.value !== props.value?.[0] || tempMax.value !== props.value?.[1]) {
      emit('update:value', [tempMin.value, tempMax.value]);
      emit('change', [tempMin.value, tempMax.value]);
    }
  }
};

// Обработчики для текстовых полей ввода
const handleMinInputChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  const inputValue = parseFloat(target.value) || 0;
  
  // Если это поле цены, конвертируем рубли в копейки
  let numericValue: number;
  if (props.formatValue.toString().includes('₽')) {
    numericValue = inputValue * 100;
  } else {
    numericValue = inputValue;
  }
  
  // Ограничиваем значение диапазоном
  numericValue = Math.max(props.min, Math.min(numericValue, currentMax.value - props.step));
  
  // Проверяем, изменилось ли значение
  if (numericValue !== currentMin.value) {
    inputMinChanged.value = true;
    currentMin.value = numericValue;
  }
};

const handleMaxInputChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  const inputValue = parseFloat(target.value) || 0;
  
  // Если это поле цены, конвертируем рубли в копейки
  let numericValue: number;
  if (props.formatValue.toString().includes('₽')) {
    numericValue = inputValue * 100;
  } else {
    numericValue = inputValue;
  }
  
  // Ограничиваем значение диапазоном
  numericValue = Math.max(currentMin.value + props.step, Math.min(numericValue, props.max));
  
  // Проверяем, изменилось ли значение
  if (numericValue !== currentMax.value) {
    inputMaxChanged.value = true;
    currentMax.value = numericValue;
  }
};

const handleMinInputBlur = () => {
  // Отправляем событие только если значение изменилось
  if (inputMinChanged.value) {
    emit('update:value', [currentMin.value, currentMax.value]);
    emit('change', [currentMin.value, currentMax.value]);
    inputMinChanged.value = false;
  }
};

const handleMaxInputBlur = () => {
  // Отправляем событие только если значение изменилось
  if (inputMaxChanged.value) {
    emit('update:value', [currentMin.value, currentMax.value]);
    emit('change', [currentMin.value, currentMax.value]);
    inputMaxChanged.value = false;
  }
};

// Синхронизация с внешними изменениями
watch(() => props.value, (newValue) => {
  if (newValue) {
    currentMin.value = newValue[0];
    currentMax.value = newValue[1];
    tempMin.value = newValue[0];
    tempMax.value = newValue[1];
    // Сбрасываем флаги изменений при внешних изменениях
    inputMinChanged.value = false;
    inputMaxChanged.value = false;
  }
}, { deep: true });

// Глобальные обработчики событий для случая, когда пользователь отпускает кнопку мыши вне ползунка
const handleGlobalMouseUp = () => {
  if (isDragging.value) {
    handleSliderMouseUp(new Event('mouseup'));
  }
};

// Добавляем и удаляем глобальные обработчики
onMounted(() => {
  document.addEventListener('mouseup', handleGlobalMouseUp);
  document.addEventListener('touchend', handleGlobalMouseUp);
});

onUnmounted(() => {
  document.removeEventListener('mouseup', handleGlobalMouseUp);
  document.removeEventListener('touchend', handleGlobalMouseUp);
});

// Экспортируем методы для внешнего доступа
defineExpose({
  setValue: (min: number, max: number) => {
    // Убеждаемся, что значения находятся в допустимом диапазоне
    const clampedMin = Math.max(props.min, Math.min(min, props.max));
    const clampedMax = Math.max(props.min, Math.min(max, props.max));
    
    currentMin.value = clampedMin;
    currentMax.value = clampedMax;
    tempMin.value = clampedMin;
    tempMax.value = clampedMax;
  },
  getValue: () => [currentMin.value, currentMax.value] as [number, number]
});
</script>

<style scoped>
/* Component-specific styles can be added here if needed */
</style>
