/*
 * HSRangeSlider
 * @version: 4.2.0
 * @author: Preline Labs Ltd.
 * @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
 * Copyright 2024 Preline Labs Ltd.
 */

import type { cssClasses, target } from 'nouislider';

import {
	IRangeSlider,
	IRangeSliderCssClassesObject,
	IRangeSliderOptions,
} from './interfaces';

import HSBasePlugin from '../base-plugin';

class HSRangeSlider
	extends HSBasePlugin<IRangeSliderOptions>
	implements IRangeSlider
{
	private readonly concatOptions: IRangeSliderOptions;
	private readonly wrapper: HTMLElement | null;
	private readonly currentValue: HTMLElement[] | null;
	private format: any | null;
	private readonly icons: {
		handle?: string;
	};

	constructor(el: HTMLElement, options?: IRangeSliderOptions, events?: {}) {
		super(el, options || ({} as IRangeSliderOptions), events);

		if (!HSRangeSlider.isAvailable())
			throw new Error(HSRangeSlider.unavailableMessage);

		const data = el.getAttribute('data-hs-range-slider');
		const dataOptions: IRangeSliderOptions = data ? JSON.parse(data) : {};

		this.concatOptions = {
			...dataOptions,
			...options,
			cssClasses: {
				...noUiSlider.cssClasses,
				...this.processClasses(dataOptions.cssClasses),
			},
		};

		this.wrapper =
			this.concatOptions.wrapper ||
			el.closest('.hs-range-slider-wrapper') ||
			null;
		this.currentValue = this.concatOptions.currentValue
			? Array.from(this.concatOptions.currentValue)
			: Array.from(
					this.wrapper?.querySelectorAll('.hs-range-slider-current-value') ||
						[],
				);
		this.icons = this.concatOptions.icons || {};

		this.init();
	}

	get formattedValue() {
		const values: number | string | (string | number)[] = (
			this.el as target
		).noUiSlider.get();

		if (Array.isArray(values) && this.format) {
			const updateValues: (string | number)[] = [];

			values.forEach((val) => {
				updateValues.push(this.format.to(val));
			});

			return updateValues;
		} else if (this.format) {
			return this.format.to(values);
		} else {
			return values;
		}
	}

	private processClasses(cl: typeof cssClasses) {
		const mergedClasses: IRangeSliderCssClassesObject = {};

		Object.keys(cl).forEach((key: keyof typeof noUiSlider.cssClasses) => {
			if (key) mergedClasses[key] = `${noUiSlider.cssClasses[key]} ${cl[key]}`;
		});

		return mergedClasses as typeof cssClasses;
	}

	private init() {
		this.createCollection(window.$hsRangeSliderCollection, this);

		if (
			typeof this.concatOptions?.formatter === 'object'
				? this.concatOptions?.formatter?.type ===
					'thousandsSeparatorAndDecimalPoints'
				: this.concatOptions?.formatter === 'thousandsSeparatorAndDecimalPoints'
		) {
			this.thousandsSeparatorAndDecimalPointsFormatter();
		} else if (
			typeof this.concatOptions?.formatter === 'object'
				? this.concatOptions?.formatter?.type === 'integer'
				: this.concatOptions?.formatter === 'integer'
		) {
			this.integerFormatter();
		} else if (
			typeof this.concatOptions?.formatter === 'object' &&
			(this.concatOptions?.formatter?.prefix ||
				this.concatOptions?.formatter?.postfix)
		) {
			this.prefixOrPostfixFormatter();
		}

		noUiSlider.create(this.el, this.concatOptions);

		if (this.currentValue && this.currentValue.length > 0) {
			(this.el as target).noUiSlider.on(
				'update',
				(values: (string | number)[]) => {
					this.updateCurrentValue(values);
				},
			);
		}

		if (this.concatOptions.disabled) this.setDisabled();
		if (this.icons.handle) this.buildHandleIcon();
	}

	private formatValue(val: number | string) {
		let result = '';

		if (typeof this.concatOptions?.formatter === 'object') {
			if (this.concatOptions?.formatter?.prefix) {
				result += this.concatOptions?.formatter?.prefix;
			}
			result += val;
			if (this.concatOptions?.formatter?.postfix) {
				result += this.concatOptions?.formatter?.postfix;
			}
		} else result += val;

		return result;
	}

	private integerFormatter() {
		this.format = {
			to: (val: number) => this.formatValue(Math.round(val)),
			from: (val: string) => Math.round(+val),
		};

		if (this.concatOptions?.tooltips) this.concatOptions.tooltips = this.format;
	}

	private prefixOrPostfixFormatter() {
		this.format = {
			to: (val: number) => this.formatValue(val),
			from: (val: string) => +val,
		};

		if (this.concatOptions?.tooltips) this.concatOptions.tooltips = this.format;
	}

	private thousandsSeparatorAndDecimalPointsFormatter() {
		this.format = {
			to: (val: number) =>
				this.formatValue(
					new Intl.NumberFormat('en-US', {
						minimumFractionDigits: 2,
						maximumFractionDigits: 2,
					}).format(val),
				),
			from: (val: string) => parseFloat(val.replace(/,/g, '')),
		};

		if (this.concatOptions?.tooltips) this.concatOptions.tooltips = this.format;
	}

	private setDisabled() {
		this.el.setAttribute('disabled', 'disabled');
		this.el.classList.add('disabled');
	}

	private buildHandleIcon() {
		if (!this.icons.handle) return false;

		const handle = this.el.querySelector('.noUi-handle');

		if (!handle) return false;

		handle.innerHTML = this.icons.handle;
	}

	private updateCurrentValue(values: (string | number)[]) {
		if (!this.currentValue || this.currentValue.length === 0) return;

		values.forEach((value, index) => {
			const element = this.currentValue?.[index];

			if (!element) return;

			const formattedValue = this.format
				? this.format.to(value).toString()
				: value.toString();

			if (element instanceof HTMLInputElement) {
				element.value = formattedValue;
			} else {
				element.textContent = formattedValue;
			}
		});
	}

	// Public methods
	public destroy() {
		(this.el as target).noUiSlider.destroy();

		this.format = null;

		window.$hsRangeSliderCollection = window.$hsRangeSliderCollection.filter(
			({ element }) => element.el !== this.el,
		);
	}

	// Static methods
	private static readonly unavailableMessage =
		'HSRangeSlider: noUiSlider is not available, please add it to the page.';

	private static isAvailable() {
		return typeof noUiSlider !== 'undefined';
	}

	static getInstance(target: HTMLElement | string, isInstance = false) {
		const elInCollection = window.$hsRangeSliderCollection.find(
			(el) =>
				el.element.el ===
				(typeof target === 'string' ? document.querySelector(target) : target),
		);

		return elInCollection
			? isInstance
				? elInCollection
				: elInCollection.element.el
			: null;
	}

	static autoInit() {
		const selector = '[data-hs-range-slider]:not(.--prevent-on-load-init)';

		if (!HSRangeSlider.isAvailable()) {
			if (document.querySelectorAll(selector).length) {
				console.error(HSRangeSlider.unavailableMessage);
			}

			return;
		}

		if (!window.$hsRangeSliderCollection) window.$hsRangeSliderCollection = [];

		if (window.$hsRangeSliderCollection) {
			window.$hsRangeSliderCollection = window.$hsRangeSliderCollection.filter(
				({ element }) => document.contains(element.el),
			);
		}

		document.querySelectorAll(selector).forEach((el: HTMLElement) => {
			if (
				!window.$hsRangeSliderCollection.find(
					(elC) => (elC?.element?.el as HTMLElement) === el,
				)
			) {
				new HSRangeSlider(el);
			}
		});
	}
}

export default HSRangeSlider;
