/* eslint-disable guard-for-in */
/* eslint-disable max-len */
/* eslint-disable require-jsdoc */

declare global {
	interface Window {
		KTGlobalComponentsConfig: object;
	}
}

import KTData from '../helpers/data';
import KTDom from '../helpers/dom';
import KTUtils from '../helpers/utils';
import KTGlobalComponentsConfig from './config';
import { KTBreakpointType, KTOptionType } from '../types';

export default class KTComponent {
	protected _name: string;
	protected _defaultConfig: object;
	protected _config: object;
	protected _events: Map<string, Map<string, CallableFunction>>;
	protected _uid: string | null = null;
	protected _element: HTMLElement | null = null;

	protected _init(element: HTMLElement | null) {
		element = KTDom.getElement(element);

		if (!element) {
			return;
		}

		this._element = element;
		this._events = new Map();
		this._uid = KTUtils.geUID(this._name);

		KTData.set(this._element, this._name, this);
	}

	protected _fireEvent(eventType: string, payload: object = null): void {
		this._events.get(eventType)?.forEach((callable) => {
			callable(payload);
		});
	}

	protected _dispatchEvent(eventType: string, payload: object = null): void {
		const event = new CustomEvent(eventType, {
			detail: { payload },
			bubbles: true,
			cancelable: true,
			composed: false,
		});

		if (!this._element) return;
		this._element.dispatchEvent(event);
	}

	protected _getOption(name: string): KTOptionType {
		const value = this._config[name as keyof object];

		if (value && typeof value === 'string') {
			return this._getResponsiveOption(value);
		} else {
			return value;
		}
	}

	protected _getResponsiveOption(value: string): KTOptionType {
		let result = null;
		const width = KTDom.getViewPort().width;
		const parts = String(value).split('|');

		if (parts.length > 1) {
			for (let i = parts.length - 1; i < parts.length; i--) {
				const part = parts[i];

				if (part.includes(':')) {
					const [breakpointKey, breakpointValue] = part.split(':');

					const breakpoint = KTUtils.getBreakpoint(
						breakpointKey as KTBreakpointType
					);

					if (breakpoint <= width) {
						result = breakpointValue;
						break;
					}
				} else {
					result = part;
					break;
				}
			}
		} else {
			result = value;
		}

		result = KTUtils.parseDataAttribute(result);

		return result;
	}

	protected _getGlobalConfig(): object {
		if (
			window.KTGlobalComponentsConfig &&
			(window.KTGlobalComponentsConfig as object)[this._name as keyof object]
		) {
			return (window.KTGlobalComponentsConfig as object)[
				this._name as keyof object
			] as object;
		} else if (
			KTGlobalComponentsConfig &&
			(KTGlobalComponentsConfig as object)[this._name as keyof object]
		) {
			return (KTGlobalComponentsConfig as object)[
				this._name as keyof object
			] as object;
		} else {
			return {};
		}
	}

	protected _buildConfig(config: object = {}): void {
		if (!this._element) return;

		this._config = {
			...this._defaultConfig,
			...this._getGlobalConfig(),
			...KTDom.getDataAttributes(this._element, this._name),
			...config,
		};
	}

	public dispose(): void {
		if (!this._element) return;
		KTData.remove(this._element, this._name);
	}

	public on(eventType: string, callback: CallableFunction): string {
		const eventId = KTUtils.geUID();

		if (!this._events.get(eventType)) {
			this._events.set(eventType, new Map());
		}

		this._events.get(eventType).set(eventId, callback);

		return eventId;
	}

	public off(eventType: string, eventId: string): void {
		this._events.get(eventType)?.delete(eventId);
	}

	public getOption(name: string): KTOptionType {
		return this._getOption(name as keyof object);
	}

	public getElement(): HTMLElement {
		if (!this._element) return null;
		return this._element;
	}
}
