<script setup lang="ts">
import { resolveI18n } from '@bagelink/vue'
import { computed, onMounted, ref, watch } from 'vue'
import type { BagelInputShellProps } from './bagelInputShell'

export interface RangeInputProps extends BagelInputShellProps {
	modelValue: number | [number, number]
	min?: number
	max?: number
	step?: number
	required?: boolean
	label?: string
	disabled?: boolean
	id?: string
	/** Force direction. Omit to inherit the page/parent `dir` (recommended). */
	rtl?: boolean
	multiRange?: boolean
	formatValue?: (value: number) => string
	/** Track (background) color. */
	trackColor?: string
	/** Active range + thumb color — defaults to labelActiveColor or primary. */
	activeColor?: string
	/** Slim variant for bare sliders / media scrubbers (smaller thumb + track). */
	size?: 'sm' | 'md'
	/** Hide the min/max footer labels (e.g. for a media scrubber). */
	hideMinMax?: boolean
}

const props = defineProps<RangeInputProps>()
const emit = defineEmits(['update:modelValue'])

const {
	min = 0,
	max = 100,
	step = 1,
	multiRange = false,
	formatValue = (value: number) => value.toString()
} = props

// Resolve direction: explicit `rtl` prop wins; otherwise inherit the element's
// computed `dir` so a slider in an RTL page fills from the start edge natively.
const rootEl = ref<HTMLElement | null>(null)
const inheritedRtl = ref(false)
onMounted(() => {
	if (props.rtl === undefined && rootEl.value) {
		inheritedRtl.value = getComputedStyle(rootEl.value).direction === 'rtl'
	}
})
const from = ref<number>(Array.isArray(props.modelValue) ? props.modelValue[0] : (props.modelValue ?? min))
const to = ref<number>(Array.isArray(props.modelValue) ? props.modelValue[1] : max)

const validFrom = computed(() => Math.min(Math.max(from.value, min), multiRange ? to.value : max))
const validTo = computed(() => Math.max(Math.min(to.value, max), from.value))

const fromPercentage = computed(() => ((validFrom.value - min) / (max - min)) * 100)
const toPercentage = computed(() => ((validTo.value - min) / (max - min)) * 100)

watch(() => props.modelValue, (newVal) => {
	if (Array.isArray(newVal) && multiRange) {
		from.value = newVal[0] ?? min
		to.value = newVal[1] ?? max
	} else if (!Array.isArray(newVal)) {
		from.value = newVal ?? min
		to.value = max
	}
}, { immediate: true })

function handleInput(value: number, isFromInput: boolean) {
	if (isFromInput) {
		from.value = value
	} else {
		to.value = value
	}
	emit('update:modelValue', multiRange ? [validFrom.value, validTo.value] : validFrom.value)
}

// Position the active range with logical properties so the browser handles RTL.
const rangeStyle = computed(() => {
	if (multiRange) {
		return {
			insetInlineStart: `${fromPercentage.value}%`,
			width: `${toPercentage.value - fromPercentage.value}%`
		}
	}
	// Single value: always fills from the inline-start edge (start = min).
	return { insetInlineStart: '0', width: `${fromPercentage.value}%` }
})

const displayFrom = computed(() => formatValue(validFrom.value))
const displayTo = computed(() => formatValue(validTo.value))
</script>

<template>
	<div
		ref="rootEl"
		:dir="rtl === undefined ? undefined : (rtl ? 'rtl' : 'ltr')"
		:class="{ 'range-sm': size === 'sm' }"
		:style="[
			minWidth ? { minWidth } : {},
			maxWidth ? { maxWidth } : {},
			activeColor ? { '--bgl-range-thumb-color': activeColor } : {},
			activeColor ? { '--bgl-input-label-active-color': activeColor } : {},
			labelActiveColor ? { '--bgl-input-label-active-color': labelActiveColor } : {},
		]"
	>
		<label v-if="props.label" class="label">{{ resolveI18n(props.label) }}</label>
		<div class="range-slider relative w-100">
			<input
				:id="id"
				:value="validFrom"
				class="from"
				type="range"
				:min="min"
				:max="max"
				:step="step"
				:required="required"
				:disabled="disabled"
				:aria-label="multiRange ? 'Minimum value' : 'Value'"
				@input="(e) => handleInput(+(e.target as HTMLInputElement).value, true)"
			>
			<input
				v-if="multiRange"
				:value="validTo"
				class="to"
				type="range"
				:min="min"
				:max="max"
				:step="step"
				:required="required"
				:disabled="disabled"
				aria-label="Maximum value"
				@input="(e) => handleInput(+(e.target as HTMLInputElement).value, false)"
			>
			<div
				class="track absolute pointer-events-none overflow-hidden round"
				:style="trackColor ? { background: trackColor } : {}"
			>
				<div
					class="active-range absolute h-100 pointer-events-none"
					:style="{ ...rangeStyle, background: activeColor || 'var(--bgl-input-label-active-color, var(--bgl-primary))' }"
				/>
			</div>
			<p
				v-if="validFrom !== min"
				class="txt-center txt-12 range-slider-position-txt absolute line-height-1"
				:style="{ '--progress': `${fromPercentage}` }"
			>
				<slot v-if="!multiRange" name="progress" :progress="displayFrom" :min="formatValue(min)" :max="formatValue(max)">
					{{ displayFrom }}
				</slot>
				<slot v-if="multiRange" name="from" :from="displayFrom" :to="displayTo" :min="formatValue(min)" :max="formatValue(max)">
					{{ displayFrom }}
				</slot>
			</p>
			<p
				v-if="multiRange && validTo !== max"
				class="txt-center txt-12 range-slider-position-txt line-height-1 absolute"
				:style="{ '--progress': `${toPercentage}` }"
			>
				<slot name="to" :to="displayTo" :from="displayFrom" :min="formatValue(min)" :max="formatValue(max)">
					{{ displayTo }}
				</slot>
			</p>
		</div>
		<p v-if="!hideMinMax" class="txt-center txt-14 user-select-none range-slider-txt flex space-between opacity-4 mx-05">
			<slot name="min" :max="formatValue(max)" :min="formatValue(min)" :from="displayFrom" :to="displayTo" :progress="displayFrom">
				<span>{{ formatValue(min) }}</span>
			</slot>
			<slot name="max" :max="formatValue(max)" :min="formatValue(min)" :from="displayFrom" :to="displayTo" :progress="displayFrom">
				<span>{{ formatValue(max) }}</span>
			</slot>
		</p>
	</div>
</template>

<style scoped>

.range-slider-position-txt{
margin-inline-start:  calc((var(--progress) * 1%) - (var(--bgl-range-thumb-size) * var(--progress) / 100));
/* Float the value bubble ABOVE the thumb so the thumb never covers it. */
bottom: calc(100% + 0.25rem);
transition: transform 0.1s, opacity 0.3s, bottom 0.3s;
transform: scale(0.8);
transform-origin: bottom center;
opacity: 0;
width: var(--bgl-range-thumb-size);
}
.range-slider:hover .range-slider-position-txt,
.range-slider:focus-within .range-slider-position-txt{
opacity: 1;
transform: scale(1);
}

.range-slider {
height: var(--bgl-range-thumb-size);
display: flex;
align-items: center;
margin-top: calc(var(--bgl-range-thumb-size) / 2 + 0.5rem) ;
}

/* Slim variant — smaller thumb + thinner track for bare sliders / scrubbers. */
.range-sm {
--bgl-range-thumb-size: 14px;
--bgl-range-track-height: 4px;
}
.range-sm .range-slider { margin-top: 0; }
.range-slider-txt{
margin-top: calc(var(--bgl-range-thumb-size) / 2) !important;
}

input[type="range"] {
  position: absolute;
  width: 100%;
  height: 100%;
  height: var(--bgl-range-thumb-size);
  background: transparent;
  pointer-events: none;
  appearance: none;
  margin: 0;
}

input[type="range"]::-webkit-slider-thumb {
  pointer-events: all;
  appearance: none;
  width: var(--bgl-range-thumb-size);
  height: var(--bgl-range-thumb-size);
  background: var(--bgl-range-thumb-color);
  border-radius: 50%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  position: relative;
  z-index: 2;
  cursor: grab;
  transition: var(--bgl-transition);
}

input[type="range"]::-webkit-slider-thumb:hover {
box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2) color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 18%, transparent);
filter: brightness(90%);
}

input[type="range"]:focus::-webkit-slider-thumb {
  outline: none;
  box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2.2) color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 18%, transparent);
}

input[type="range"]:active::-webkit-slider-thumb {
cursor: grabbing;
}

.track {
  height: var(--bgl-range-track-height);
  background: var(--bgl-bg);
  margin-inline-start: calc(var(--bgl-range-thumb-size) / 2);
  width: calc(100% - var(--bgl-range-thumb-size));
}
@media screen and (max-width: 768px) {
.range-slider-position-txt{
	opacity: 1;
	transform: scale(1);
}
}

</style>
