import { Prop, toNative } from 'vue-facing-decorator';
import { TsxComponentExtendedBase } from '../../app/vuestsx-extended';
import { Component } from '../../app/vuetsx';
import './css/slider.css';

interface SliderProps {
	values: [number, number];
	minLimit?: number;
	maxLimit?: number;
	step?: number;
	priceGap?: number;
	onInput: (newValues: [number, number]) => void;
	/** Fires once with the final values when the user releases the handle (native `change`) — lets consumers commit only after dragging ended instead of on every drag tick. */
	onChange?: (newValues: [number, number]) => void;
	disabled?: boolean;
}

@Component
class Slider extends TsxComponentExtendedBase<SliderProps> implements SliderProps {
	@Prop() values: [number, number];
	@Prop() minLimit?: number;
	@Prop() maxLimit?: number;
	@Prop() step?: number;
	@Prop() priceGap?: number;
	@Prop() onInput: (newValues: [number, number]) => void;
	@Prop() onChange?: (newValues: [number, number]) => void;
	@Prop() disabled?: boolean;

	minVal = 0;
	maxVal = 0;

	mounted() {
		// Initialize state from props
		this.minVal = this.values[0];
		this.maxVal = this.values[1];
	}

	updated() {
		this.minVal = this.values[0];
		this.maxVal = this.values[1];
	}

	get progressStyle() {
		const min = this.minLimit ?? 0;
		const max = this.maxLimit ?? 100;
		const range = max - min;
		const left = ((this.minVal - min) / range) * 100;
		const right = 100 - ((this.maxVal - min) / range) * 100;
		return {
			left: `${left}%`,
			right: `${right}%`,
		};
	}

	get minThumbStyle() {
		const gap = this.priceGap || 0;

		// When the two thumbs overlap, whichever <input> paints on top captures the
		// pointer. The topmost thumb must be the one that still has room to escape,
		// otherwise the pair deadlocks (QA__G-383, both directions):
		// - overlap in the UPPER half (min was dragged up): min must be on top so it
		//   can be dragged back down — raise .range-min above .range-max.
		// - overlap in the LOWER half (max was dragged down): max must be on top so it
		//   can be dragged back up — keep the default DOM stacking (.range-max paints
		//   on top because it follows .range-min in the DOM).
		const overlapping = this.minVal >= this.maxVal - gap;
		const min = this.minLimit ?? 0;
		const max = this.maxLimit ?? 100;
		const overlapInUpperHalf = this.maxVal >= (min + max) / 2;

		return {
			zIndex: overlapping && overlapInUpperHalf ? 5 : undefined,
		};
	}

	handleSlider(e) {
		if (this.disabled) {
			return;
		}

		const value = parseInt(e.target.value);
		const isMin = e.target.classList.contains('range-min');
		let min = this.minVal;
		let max = this.maxVal;
		const gap = this.priceGap || 0;
		if (isMin) {
			min = Math.min(value, max - gap);
			min = Math.max(this.minLimit || 0, min);
			if (min !== this.minVal) {
				this.minVal = min;
				this.onInput([
					min,
					max,
				]);
			}
		} else {
			max = Math.max(value, min + gap);
			max = Math.min(this.maxLimit || 0, max);
			if (max !== this.maxVal) {
				this.maxVal = max;
				this.onInput([
					min,
					max,
				]);
			}
		}

		this.$nextTick(() => {
			this.setRangeMin();
			this.setRangeMax();
		});
	}

	handleSliderCommit() {
		if (this.disabled || this.onChange == null) {
			return;
		}

		// `input` events already clamped + stored the values — emit the settled pair.
		this.onChange([
			this.minVal,
			this.maxVal,
		]);
	}

	setRangeMin() {
		const minValInput = this.$refs.minValRef as HTMLInputElement;
		minValInput.value = this.values[0].toString();
	}

	setRangeMax() {
		const maxValInput = this.$refs.maxValRef as HTMLInputElement;
		maxValInput.value = this.values[1].toString();
	}

	render(h) {
		return (
			<div className={`slider-wrapper${this.disabled ? ' slider-wrapper--disabled' : ''}`}>
				<div className="slider">
					<div className="progress" style={this.progressStyle}></div>
				</div>

				<div className="range-input">
					<input
						ref="minValRef"
						type="range"
						className="range-min"
						min={this.minLimit}
						max={this.maxLimit}
						step={this.step}
						value={this.minVal}
						style={this.minThumbStyle}
						onInput={this.handleSlider}
						onChange={this.handleSliderCommit}
						disabled={this.disabled}
					/>
					<input
						ref="maxValRef"
						type="range"
						className="range-max"
						min={this.minLimit}
						max={this.maxLimit}
						step={this.step}
						value={this.maxVal}
						onInput={this.handleSlider}
						onChange={this.handleSliderCommit}
						disabled={this.disabled}
					/>
				</div>
			</div>
		);
	}
}

export default toNative(Slider);
