/*
 * HSThemeSwitch
 * @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 { IThemeSwitch, IThemeSwitchOptions } from '../theme-switch/interfaces';

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

class HSThemeSwitch
	extends HSBasePlugin<IThemeSwitchOptions>
	implements IThemeSwitch
{
	public theme: string;
	public type: 'change' | 'click';

	private onElementChangeListener: (evt: Event) => void;
	private onElementClickListener: () => void;

	private static systemThemeObserver:
		| ((e: MediaQueryListEvent) => void)
		| null = null;

	constructor(
		el: HTMLElement | HTMLInputElement,
		options?: IThemeSwitchOptions,
	) {
		super(el, options);

		const data = el.getAttribute('data-hs-theme-switch');
		const dataOptions: IThemeSwitchOptions = data ? JSON.parse(data) : {};
		const concatOptions = {
			...dataOptions,
			...options,
		};

		this.theme =
			concatOptions?.theme || localStorage.getItem('hs_theme') || 'default';
		this.type = concatOptions?.type || 'change';

		this.init();
	}

	private elementChange(evt: Event) {
		const theme = (evt.target as HTMLInputElement).checked ? 'dark' : 'default';

		this.setAppearance(theme);
		this.toggleObserveSystemTheme();
	}

	private elementClick(theme: string) {
		this.setAppearance(theme);
		this.toggleObserveSystemTheme();
	}

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

		if (this.theme !== 'default') this.setAppearance();

		if (this.type === 'click') this.buildSwitchTypeOfClick();
		else this.buildSwitchTypeOfChange();
	}

	private buildSwitchTypeOfChange() {
		(this.el as HTMLInputElement).checked = this.theme === 'dark';

		this.toggleObserveSystemTheme();

		this.onElementChangeListener = (evt) => this.elementChange(evt);

		this.el.addEventListener('change', this.onElementChangeListener);
	}

	private buildSwitchTypeOfClick() {
		const theme = this.el.getAttribute('data-hs-theme-click-value');

		this.toggleObserveSystemTheme();

		this.onElementClickListener = () => this.elementClick(theme);

		this.el.addEventListener('click', this.onElementClickListener);
	}

	private setResetStyles() {
		const style = document.createElement('style');
		style.innerText = `*{transition: unset !important;}`;
		style.setAttribute('data-hs-appearance-onload-styles', '');

		document.head.appendChild(style);

		return style;
	}

	private addSystemThemeObserver() {
		if (HSThemeSwitch.systemThemeObserver) return;

		HSThemeSwitch.systemThemeObserver = (e: MediaQueryListEvent) => {
			window.$hsThemeSwitchCollection?.forEach((instance) => {
				if (localStorage.getItem('hs_theme') === 'auto') {
					instance.element.setAppearance('auto', false);
				}
			});
		};

		window
			.matchMedia('(prefers-color-scheme: dark)')
			.addEventListener('change', HSThemeSwitch.systemThemeObserver);
	}

	private removeSystemThemeObserver() {
		if (!HSThemeSwitch.systemThemeObserver) return;

		window
			.matchMedia('(prefers-color-scheme: dark)')
			.removeEventListener('change', HSThemeSwitch.systemThemeObserver);

		HSThemeSwitch.systemThemeObserver = null;
	}

	private toggleObserveSystemTheme() {
		if (localStorage.getItem('hs_theme') === 'auto') {
			this.addSystemThemeObserver();
		} else this.removeSystemThemeObserver();
	}

	// Public methods
	public setAppearance(
		theme = this.theme,
		isSaveToLocalStorage = true,
		isSetDispatchEvent = true,
	) {
		const html = document.querySelector('html');
		const resetStyles = this.setResetStyles();

		if (isSaveToLocalStorage) localStorage.setItem('hs_theme', theme);

		let computedTheme = theme;
		if (computedTheme === 'default') computedTheme = 'light';

		if (computedTheme === 'auto') {
			computedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
				? 'dark'
				: 'light';
		}

		html.classList.remove('light', 'dark', 'default', 'auto');

		if (theme === 'auto') {
			html.classList.add('auto', computedTheme);
		} else {
			html.classList.add(computedTheme);
		}

		setTimeout(() => resetStyles.remove());

		if (isSetDispatchEvent) {
			window.dispatchEvent(
				new CustomEvent('on-hs-appearance-change', { detail: theme }),
			);
		}
	}

	public destroy() {
		// Clear listeners
		if (this.type === 'change') {
			this.el.removeEventListener('change', this.onElementChangeListener);
		}
		if (this.type === 'click') {
			this.el.removeEventListener('click', this.onElementClickListener);
		}

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

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

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

	static autoInit() {
		if (!window.$hsThemeSwitchCollection) window.$hsThemeSwitchCollection = [];

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

		document
			.querySelectorAll('[data-hs-theme-switch]:not(.--prevent-on-load-init)')
			.forEach((el: HTMLElement) => {
				if (
					!window.$hsThemeSwitchCollection.find(
						(elC) => (elC?.element?.el as HTMLElement) === el,
					)
				) {
					new HSThemeSwitch(el, { type: 'change' });
				}
			});

		document
			.querySelectorAll(
				'[data-hs-theme-click-value]:not(.--prevent-on-load-init)',
			)
			.forEach((el: HTMLElement) => {
				if (
					!window.$hsThemeSwitchCollection.find(
						(elC) => (elC?.element?.el as HTMLElement) === el,
					)
				) {
					new HSThemeSwitch(el, { type: 'click' });
				}
			});
	}
}

export default HSThemeSwitch;
