import type { ValidationState } from '../../common/static-wrappers/interfaces/validation-interface';
import { Prop, toNative } from 'vue-facing-decorator';
import TsxComponent, { Component } from '../../app/vuetsx';
import { isNullOrEmpty } from '../../common/utils/is-null-or-empty';
import TextBox, { TextBoxTextType } from './textbox';
import './css/pin.css';

export enum PinInputType {
	Alpha = 'alpha',
	Numeric = 'numeric',
	AlphaNumeric = 'alphanumeric',
}

interface PinInputArgs {
	showValue?: boolean;
	length: number;
	cssClass?: string;
	inputType?: PinInputType;
	value: string[];
	changed: (value: string[]) => void;
	validationState?: ValidationState;
	/**
	 * Fired once every cell carries a valid character (typed OR pasted). Use this
	 * to auto-submit the form so the user doesn't have to also click a button
	 * after entering the last digit / pasting a code.
	 */
	completed?: (value: string[]) => void;
}

@Component
class PinInputComponent extends TsxComponent<PinInputArgs> implements PinInputArgs {
	@Prop() showValue: boolean;
	@Prop() length: number;
	@Prop() cssClass!: string;
	@Prop() inputType: PinInputType;
	@Prop() value: string[];
	@Prop() changed: (value: string[]) => void;
	@Prop() completed?: (value: string[]) => void;
	currentValue: string[] = null;

	mounted(): void {
		this.currentValue = Array.from({ length: this.length }).fill('') as any;
	}

	getCssClass(): string {
		return `pin ${this.cssClass || ''}`;
	}

	getInputType(): PinInputType {
		return this.inputType ?? PinInputType.Numeric;
	}

	get isFullyFilled(): boolean {
		return this.currentValue?.every(p => !isNullOrEmpty(p));
	}

	isValidInput(value: string): boolean {
		const type = this.getInputType();
		switch (type) {
			case PinInputType.Alpha:
				return /^[a-z]$/i.test(value);
			case PinInputType.Numeric:
				return /^\d$/.test(value);
			case PinInputType.AlphaNumeric:
				return /^[a-z0-9]$/i.test(value);
			default:
				return false;
		}
	}

	focusNext(index: number): void {
		if (index < this.length - 1) {
			const nextInput = this.getElement(index + 1);
			(nextInput.$el as HTMLElement)?.querySelector('input')?.focus();
		}
	}

	focusPrev(index: number): void {
		if (index > 0) {
			const prevInput = this.getElement(index - 1);
			(prevInput.$el as HTMLElement)?.querySelector('input')?.focus();
		}
	}

	updatePin(value: string, index: number): void {
		if (!this.isValidInput(value)) {
			return;
		}

		this.currentValue[index] = value.slice(-1);

		const isComplete = this.isFullyFilled;

		if (isComplete) {
			this.blurAllInputs();
		} else {
			this.focusNext(index);
		}

		this.changed(this.currentValue);

		if (isComplete) {
			this.completed?.(this.currentValue);
		}
	}

	/**
	 * Distribute a multi-character paste across the inputs. Filters by
	 * `inputType` (so a numeric pin ignores non-digit chars), trims to
	 * `length`, fills from index 0, then focuses the next-empty input — or
	 * blurs all + fires `completed` when the paste fills every cell.
	 */
	handlePaste(event: ClipboardEvent): void {
		const pasted = event.clipboardData?.getData('text') ?? '';
		const chars = pasted.split('').filter(c => this.isValidInput(c)).slice(0, this.length);

		if (chars.length === 0) {
			return;
		}

		event.preventDefault();
		event.stopPropagation();

		const next = Array.from({ length: this.length }).fill('') as string[];

		for (let i = 0; i < chars.length; i++) {
			next[i] = chars[i];
		}

		this.currentValue = next;

		const isComplete = this.isFullyFilled;

		if (isComplete) {
			this.blurAllInputs();
		} else {
			const focusIndex = Math.min(chars.length, this.length - 1);
			const target = this.getElement(focusIndex);
			(target?.$el as HTMLElement)?.querySelector('input')?.focus();
		}

		this.changed(this.currentValue);

		if (isComplete) {
			this.completed?.(this.currentValue);
		}
	}

	blurAllInputs(): void {
		for (let index = 0; index < this.currentValue?.length; index++) {
			const input = this.getElement(index);
			(input.$el as HTMLElement)?.querySelector('input')?.blur();
		}
	}

	getElement(index: number) {
		if (index < 0 || index >= this.length) {
			return null;
		}

		return this.$refs[`pin-${index}`] as typeof TextBox.prototype;
	}

	getTextType(): TextBoxTextType {
		return this.showValue ? TextBoxTextType.Text : TextBoxTextType.Password;
	}

	render(h) {
		if (!this.currentValue) {
			return null;
		}

		const textType = this.getTextType();

		return (
			<div class={this.getCssClass()} onPaste={(e: ClipboardEvent) => { this.handlePaste(e); }}>
				{Array.from({ length: this.length }, (_, index) => (
					<TextBox
						label=""
						key={index}
						wrap={false}
						ref={`pin-${index}`}
						nameAttr={`pin-${index}`}
						maxLength={1}
						cssClass="pin-input"
						updateMode="input"
						autoCompleteEnabled={false}
						textType={textType}
						hidePasswordToggle={true}
						value={this.currentValue[index]}
						keyDown={(e) => {
							if (e.key == 'Backspace') {
								this.currentValue[index] = '';
								this.focusPrev(index);
								this.changed(this.currentValue);
								return;
							}

							// Let clipboard/selection shortcuts (Ctrl/Cmd+V/C/A/X) and non-printable
							// keys (Tab, arrows) pass through - only block single printable chars
							// that aren't valid for this pin type. Blocking the Ctrl+V keydown here
							// cancelled the browser paste before the paste event could fire (QA_AT-103).
							if (e.ctrlKey || e.metaKey || e.altKey) {
								return;
							}

							if (e.key.length === 1 && !this.isValidInput(e.key)) {
								e.preventDefault();
								e.stopPropagation();
							}
						}}
						changed={(e) => { this.updatePin(e, index); }}
					/>
				))}
			</div>
		);
	}
}

const PinInput = toNative(PinInputComponent);
export default PinInput;
