import type { Validation } from '@vuelidate/core';
import type { VNode } from 'vue';
import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item';
import type { FormItemWrapperArgs, MarginType } from '../form/form-item-wrapper';
import { Prop, toNative, Watch } from 'vue-facing-decorator';
import PowerduckState from '../../app/powerduck-state';
import TsxComponent, { Component } from '../../app/vuetsx';
import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
import FormItemWrapper from '../form/form-item-wrapper';
import './css/bootstrap-toggle.css';

interface BootstrapToggleArgs extends Omit<FormItemWrapperArgs, 'errorId'> {
	value: boolean;
	enabled?: boolean;
	captionTrue?: string;
	captionFalse?: string;
	size?: BootstrapToggleSize;
	cssClassRoot?: string;
	changed: (newValue: boolean) => void;
	validationProperty?: Validation & any;
}

export enum BootstrapToggleSize {
	Large = 'btn-lg',
	Normal = '',
	Small = 'btn-sm',
	Mini = 'btn-xs',
}

export class BootstrapToggleConfig {
	static getOnStyle() {
		return PowerduckState.getBootstrapOnStyle();
	}
}

@Component
class BootstrapToggleComponent extends TsxComponent<BootstrapToggleArgs> implements BootstrapToggleArgs {
	@Prop() label!: string | VNode;
	@Prop() labelButtons!: DropdownButtonItemArgs[];
	@Prop() subtitle!: string;
	@Prop() value!: boolean;
	@Prop() wrap!: boolean;
	@Prop() mandatory!: boolean;
	@Prop() hint: string;
	@Prop() appendIcon: string;
	@Prop() prependIcon: string;
	@Prop() cssClass!: string;
	@Prop() cssClassRoot!: string;
	@Prop() maxWidth?: number;
	@Prop() appendClicked: () => void;
	@Prop() prependClicked: () => void;
	@Prop() changed: (newValue: boolean) => void;
	@Prop({ default: true }) enabled!: boolean;
	@Prop() captionTrue!: string;
	@Prop() captionFalse!: string;
	@Prop() marginType?: MarginType;
	@Prop() size!: BootstrapToggleSize;
	_sizeCache: { key: string; width: number };
	_skipNoTrans: boolean;

	raiseChangeEvent(newValue: boolean) {
		this.populateValidationDeclaration();

		if (this.changed != null) {
			this._skipNoTrans = true;
			this.changed(newValue);
		}
	}

	getBootstrapToggleElement(): HTMLElement {
		return this.$el.querySelector('input');
	}

	mounted() {

	}

	@Watch('value')
	onValueChanged() {
		this.$forceUpdate();
	}

	getCaptionTrue(): string {
		return this.captionTrue || PowerduckState.getResourceValue('yes');
	}

	getCaptionFalse(): string {
		return this.captionFalse || PowerduckState.getResourceValue('no');
	}

	private get errorId(): string {
		return `toggle-error-${this.$.uid}`;
	}

	getMinWidthCssClass(): string {
		if (this.getCaptionFalse().length > 5 || this.getCaptionTrue().length > 5) {
			return 'toggle-min-100';
		}

		return '';
	}

	handleValueToggle() {
		if (this.value == true) {
			this.raiseChangeEvent(false);
		} else {
			this.raiseChangeEvent(true);
		}
	}

	getCacheKey(): string {
		return `${this.getCaptionTrue()}-${this.getCaptionFalse()}`;
	}

	getSizes(): { width: number } {
		const key = this.getCacheKey();
		if (key != this._sizeCache?.key) {
			this.invalidateDimensionsCache();
		}

		return this._sizeCache;
	}

	invalidateDimensionsCache() {
		const newEl = document.createElement('label');
		newEl.className = `btn btn-light ${this.size || ''}`.trim();
		newEl.style.position = 'absolute';
		newEl.style.left = '-9999px';
		document.body.appendChild(newEl);

		newEl.textContent = this.getCaptionTrue();
		let width = newEl.offsetWidth;

		newEl.textContent = this.getCaptionFalse();
		const itemWidth = newEl.offsetWidth;
		newEl.remove();

		if (itemWidth > width) {
			width = itemWidth;
		}

		this._sizeCache = {
			key: this.getCacheKey(),
			width: width + 15,
		};
	}

	beforeUpdate(): void {
		if (!this.$el?.getAttribute) {
			return;
		}

		if (!this._skipNoTrans) {
			const prevVal = (this.$el as HTMLElement).getAttribute('data-value') == '1';
			if (prevVal != this.value) {
				const toggleGroup: HTMLElement = (this.$el as HTMLElement).querySelector('.bs-toggle-group');
				if (toggleGroup != null) {
					toggleGroup.classList.add('no-bs-toggle-trans');

					this.$nextTick(() => {
						setTimeout(() => {
							(this.$el as HTMLElement).querySelector('.bs-toggle-group').classList.remove('no-bs-toggle-trans');
						}, 150);
					});
				}
			}
		}

		this._skipNoTrans = false;
	}

	render(h) {
		const { width } = this.getSizes();

		return (
			<div data-value={this.value == true ? '1' : '0'} class={this.getMinWidthCssClass() + (!isNullOrEmpty(this.cssClassRoot) ? ` ${this.cssClassRoot}` : '')}>
				<FormItemWrapper
					label={this.label}
					mandatory={this.mandatory}
					wrap={this.wrap}
					cssClass={this.cssClass}
					appendIcon={this.appendIcon}
					prependIcon={this.prependIcon}
					hint={this.hint}
					appendClicked={this.appendClicked}
					prependClicked={this.prependClicked}
					marginType={this.marginType}
					maxWidth={this.maxWidth}
					validationState={this.validationState}
					labelButtons={this.labelButtons}
					subtitle={this.subtitle}
					errorId={this.errorId}
				>
					<div
						class={`bs-toggle btn btn-light ${this.size || ''} ${this.value == true ? 'on' : 'off'} ${this.enabled === false ? 'disabled' : ''}`}
						data-toggle="toggle"
						role="switch"
						aria-checked={this.value === true}
						aria-disabled={this.enabled === false ? 'true' : undefined}
						aria-required={this.mandatory ? 'true' : undefined}
						aria-invalid={this.hasValidationError ? 'true' : undefined}
						aria-describedby={this.hasValidationError ? this.errorId : undefined}
						tabindex={this.enabled !== false ? 0 : -1}
						style={`width: ${width}px;`}
						onClick={() => {
							if (this.enabled != false) {
								this.handleValueToggle();
							}
						}}
						onKeydown={(e: KeyboardEvent) => {
							if ((e.key === ' ' || e.key === 'Enter') && this.enabled !== false) {
								e.preventDefault();
								this.handleValueToggle();
							}
						}}
					>
						<div class="bs-toggle-group">
							<label class={`btn btn-${BootstrapToggleConfig.getOnStyle()} ${this.size || ''} bs-toggle-on`}>{this.getCaptionTrue()}</label>
							<label class={`btn btn-light ${this.size || ''} bs-toggle-off`}>{this.getCaptionFalse()}</label>
							<span class={`bs-toggle-handle btn btn-light ${this.size || ''}`}></span>
						</div>
					</div>
				</FormItemWrapper>
			</div>
		);
	}
}

const BootstrapToggle = toNative(BootstrapToggleComponent);
export default BootstrapToggle;
