<template>
  <v-row
    class="flex-column"
    no-gutters
  >
    <v-row
      v-if="label"
      no-gutters
      justify="start"
    >
      <v-col>
        <span class="pl-0 pb-1 sh-label">{{ label }}</span>
      </v-col>
    </v-row>
    <v-row
      class="py-2 new-style"
      no-gutters
      v-if="!hideInput"
    >
      <v-col cols="6">
        <v-text-field
          class="mr-1"
          data-testid="lower"
          :model-value="sliderState[0]"
          @update:model-value="input[0] = parseInt($event)"
          @blur="onUpdateLower"
          @keyup.enter="onUpdateLower"
          density="compact"
          hide-details
          type="number"
          variant="outlined"
        />
      </v-col>
      <v-col cols="6">
        <v-text-field
          class="ml-1"
          data-testid="upper"
          :model-value="sliderState[1]"
          @update:model-value="input[1] = parseInt($event)"
          @blur="onUpdateHigher"
          @keyup.enter="onUpdateHigher"
          density="compact"
          hide-details
          type="number"
          variant="outlined"
        />
      </v-col>
    </v-row>
    <v-row
      class="pb-0"
      no-gutters
    >
      <v-range-slider
        ref="slider"
        :style="`--thumbBackgroundColor: ${bgColor}`"
        v-model="sliderState"
        data-testid="slider"
        :loading="loading"
        :thumb-label="showThumb"
        thumb-size="16"
        thumb-color="primary"
        :min="min"
        :max="max"
        :step="10"
        hide-details
        :strict="true"
      >
      </v-range-slider>
    </v-row>
  </v-row>
</template>

<script setup lang="ts">
import { clamp } from '@/functions/clamp';
import { computed, ref, toRefs, unref, watch } from 'vue';
import { VRangeSlider } from 'vuetify/components';

interface Props {
  modelValue?: [number, number];
  loading?: boolean;
  label?: string;
  min?: number;
  max?: number;
  bgColor?: string;
  hideInput?: boolean;
}

type Emits = {
  'update:modelValue': [[number, number]];
};

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [0, 0],
  loading: false,
  label: '',
  min: 0,
  max: 100,
  bgColor: 'rgb(var(--v-theme-background))',
  hideInput: false,
});

const { label } = toRefs(props);

const emit = defineEmits<Emits>();

const _modelValue = ref<[number, number]>(props.modelValue);
const input = ref<[number, number]>(props.modelValue);
const showThumb = ref<'always' | boolean>(false);
const showThumbTimeout = ref<number>();
const slider = ref<VRangeSlider>();

const rightOffset = ref<string>('');
const leftOffset = ref<string>('');

const sliderState = computed({
  get(): [number, number] {
    return _modelValue.value;
  },
  set(value: [number, number]): void {
    _modelValue.value = value;
    emit('update:modelValue', _modelValue.value);
  },
});

watch(
  () => props.modelValue,
  (value: [number, number]) => {
    _modelValue.value = value;
    applyRangeSliderLabelBoundaries();
  },
);

watch([() => sliderState.value[0], () => sliderState.value[1]], () => {
  clearTimeout(unref(showThumbTimeout));
  showThumb.value = 'always';
  showThumbTimeout.value = setTimeout(() => {
    showThumb.value = false;
  }, 1000);
});

function applyRangeSliderLabelBoundaries(): void {
  const thumbs = slider.value?.$el.getElementsByClassName(
    'v-slider-thumb',
  ) as any;

  if (thumbs[0]['offsetLeft'] < 10) {
    const offset = -50 + (30 - thumbs[0]['offsetLeft']);
    leftOffset.value = 'translateX(' + offset + '%)';
  } else if (thumbs[0]['offsetLeft'] > 220) {
    const offset = -50 - (40 - (240 - thumbs[0]['offsetLeft']));
    leftOffset.value = 'translateX(' + offset + '%)';
  } else {
    leftOffset.value = 'translateX(-50%)';
  }

  if (thumbs[1]['offsetLeft'] < 10) {
    const offset = -50 + (30 - thumbs[0]['offsetLeft']);
    rightOffset.value = 'translateX(' + offset + '%)';
  } else if (thumbs[1]['offsetLeft'] > 220) {
    const offset = -50 - (40 - (240 - thumbs[1]['offsetLeft']));
    rightOffset.value = 'translateX(' + offset + '%)';
  } else {
    rightOffset.value = 'translateX(-50%)';
  }
}

function onUpdateLower(): void {
  const higher = sliderState.value[1];
  const lower = clamp(input.value[0], props.min, higher);
  sliderState.value = [lower, higher];
}

function onUpdateHigher(): void {
  const lower = sliderState.value[0];
  const higher = clamp(input.value[1], lower, props.max);
  sliderState.value = [lower, higher];
}

defineExpose({
  sliderState,
  applyRangeSliderLabelBoundaries,
  leftOffset,
  rightOffset,
});
</script>

<style scoped lang="scss">
:deep(.v-slider-thumb) {
  .v-slider-thumb__label {
    transition: none !important;
    height: fit-content;
    transform: v-bind(leftOffset) !important;
  }

  ~ .v-slider-thumb {
    .v-slider-thumb__label {
      transition: none !important;
      height: fit-content;
      transform: v-bind(rightOffset) !important;
    }
  }
}
</style>
