import { Spacing } from "../../styles/tokens/spacing.js";
import { textInputBorderColor } from "../../styles/fields.js";
import { SingleCodeInput } from "./SingleCodeInput.js";
import styled from "@emotion/styled";
import { isNumber } from "lodash-es";
import { createRef, useCallback, useRef, useState } from "react";
import { jsx, jsxs } from "@emotion/react/jsx-runtime";
import { nanoid } from "nanoid";
import { isChrome, isMobile, isMobileSafari } from "react-device-detect";
//#region src/components/CodeInput/CodeInput.tsx
function codeFromInputState(inputState) {
	return Object.values(inputState).map((input) => input.value).join("");
}
function getRef(key, refsObj) {
	if (key && refsObj && typeof refsObj === "object" && refsObj.current) {
		const ref = refsObj.current[key];
		if (ref && typeof ref === "object" && ref.current && ref.current instanceof HTMLInputElement) return ref.current;
	}
	return null;
}
function CodeInput({ codeLength = 8, onChange, onSubmit, inputCSS, autoFocus = true, onBlur, variant = "default" }) {
	const componentId = useRef(nanoid(5));
	const inputRefs = useRef({});
	const autofillValuesRef = useRef("");
	const getInputId = useCallback((index) => {
		return `${`two-factor-input-${componentId.current}-`}-${index}`;
	}, []);
	function getInputIndex(id) {
		return Number.parseInt(id.charAt(id.length - 1), 10);
	}
	const firstFieldRefCb = useCallback((ref) => {
		if (ref) {
			inputRefs.current[ref.id] = createRef();
			inputRefs.current[ref.id].current = ref;
			if (ref.id === getInputId(0) && autoFocus) ref.focus();
		}
	}, [getInputId, autoFocus]);
	const [inputState, setInputState] = useState(Array(codeLength).fill(0).reduce((acc, _, i) => {
		return {
			...acc,
			[getInputId(i)]: { value: "" }
		};
	}, {}));
	const onKeyDown = useCallback((event, inputId) => {
		const handleKeys = new Set([
			"0",
			"1",
			"2",
			"3",
			"4",
			"5",
			"6",
			"7",
			"8",
			"9",
			"Enter",
			"Escape",
			"Backspace",
			"Delete",
			"ArrowLeft",
			"ArrowRight"
		]);
		if ([
			"e",
			"E",
			"+",
			"-",
			"."
		].includes(event.key)) {
			event.preventDefault();
			return;
		}
		if (!handleKeys.has(event.key)) return;
		let replaceEntireState = false;
		const currentInputIndex = getInputIndex(inputId);
		const isFirstInput = currentInputIndex === 0;
		const isLastInput = currentInputIndex === codeLength - 1;
		const nextInputIndex = isLastInput ? void 0 : currentInputIndex + 1;
		const prevInputIndex = isFirstInput ? void 0 : currentInputIndex - 1;
		const nextInputId = isNumber(nextInputIndex) ? getInputId(nextInputIndex) : void 0;
		const prevInputId = isNumber(prevInputIndex) ? getInputId(prevInputIndex) : void 0;
		const nextInput = nextInputId ? inputState[nextInputId] : void 0;
		const prevInput = prevInputId ? inputState[prevInputId] : void 0;
		const currentInputRef = getRef(inputId, inputRefs);
		const nextInputRef = getRef(nextInputId, inputRefs);
		const prevInputRef = getRef(prevInputId, inputRefs);
		const firstInputRef = getRef(getInputId(0), inputRefs);
		const lastInputRef = getRef(getInputId(codeLength - 1), inputRefs);
		if (event.key === "ArrowLeft") {
			if (firstInputRef && (event.metaKey || event.ctrlKey || event.altKey)) {
				firstInputRef.focus();
				return;
			}
			if (prevInput && prevInputRef) {
				prevInputRef.focus();
				prevInputRef.select();
			}
			return;
		}
		if (event.key === "ArrowRight") {
			if (lastInputRef && (event.metaKey || event.ctrlKey || event.altKey)) {
				lastInputRef.focus();
				return;
			}
			if (nextInput && nextInputRef) {
				nextInputRef.focus();
				nextInputRef.select();
			}
			return;
		}
		if (event.key === "Enter" && onSubmit) {
			onSubmit(codeFromInputState(inputState));
			return;
		}
		if (event.key === "Escape" && currentInputRef) {
			currentInputRef.blur();
			return;
		}
		if (event.key === "Backspace" && (event.metaKey || event.ctrlKey || event.altKey)) {
			replaceEntireState = { ...inputState };
			Object.keys(replaceEntireState).forEach((inputId) => {
				const newState = replaceEntireState;
				newState[inputId] = {
					...newState[inputId],
					value: ""
				};
			});
		}
		let value = event.key;
		if (event.key === "Backspace" && !event.metaKey && !event.ctrlKey && !event.altKey) {
			value = "";
			if (!isFirstInput && prevInput && prevInputRef) prevInputRef.focus();
		} else if (event.key === "Delete") {
			value = "";
			if (!isLastInput && nextInput && nextInputRef) nextInputRef.focus();
		} else if (replaceEntireState) {
			const lastInputWithValue = Object.entries(replaceEntireState).reverse().find(([, { value }]) => value !== "");
			if (lastInputWithValue) {
				const inputId = lastInputWithValue[0];
				const inputRef = getRef(inputId, inputRefs);
				inputRef?.focus();
				inputRef?.select();
			} else if (firstInputRef) firstInputRef.focus();
		} else if (isLastInput) {} else if (nextInput && nextInputRef) {
			nextInputRef.focus();
			nextInputRef.select();
		}
		const newState = replaceEntireState ? replaceEntireState : {
			...inputState,
			[inputId]: { value }
		};
		const code = codeFromInputState(newState);
		if (onChange) onChange(code);
		setInputState(newState);
	}, [
		onChange,
		setInputState,
		onSubmit,
		getInputId,
		inputState,
		codeLength
	]);
	const onPasteCb = useCallback((event) => {
		event.preventDefault();
		const text = event.clipboardData.getData("text")?.replace(/[\s.]/g, "");
		if (!text || Number.isNaN(Number.parseInt(text))) return;
		const inputValues = text.split("");
		const newState = { ...inputState };
		inputValues.forEach((value, i) => {
			const inputId = getInputId(i);
			newState[inputId] = {
				...newState[inputId],
				value
			};
		});
		const firstInputRef = getRef(getInputId(0), inputRefs);
		const lastInputWithValue = Object.entries(newState).reverse().find(([, { value }]) => value !== "");
		if (lastInputWithValue) {
			const inputId = lastInputWithValue[0];
			const inputRef = getRef(inputId, inputRefs);
			inputRef?.focus();
			inputRef?.select();
		} else if (firstInputRef) firstInputRef.focus();
		const code = codeFromInputState(newState);
		if (onChange) onChange(code);
		setInputState(newState);
	}, [
		getInputId,
		inputState,
		onChange,
		setInputState
	]);
	const onBlurCb = useCallback(() => {
		/**
		* Need to move to the bottom of the stack to wait for the next focus event
		* to potentially fire on another input
		*/
		setTimeout(() => {
			let hasCodeFocused = false;
			if (inputRefs.current) {
				Object.values(inputRefs.current).forEach((ref) => {
					if (ref && ref.current && ref.current === document.activeElement) hasCodeFocused = true;
				});
				if (!hasCodeFocused && onBlur) onBlur();
			}
		}, 0);
	}, [onBlur]);
	const inputsPerGroup = Math.ceil(codeLength / 2);
	/**
	* When clicking on the unified code input container, handle focus appropriately
	* Uses onMouseDown instead of onClick because mousedown fires before focus,
	* allowing us to prevent the default focus behavior and redirect to the correct input.
	*/
	const onContainerMouseDown = useCallback((event) => {
		const target = event.target;
		if (!(target.tagName === "INPUT" && inputState[target.id]?.value !== "")) {
			event.preventDefault();
			const firstEmptyIndex = codeFromInputState(inputState).length;
			getRef(getInputId(firstEmptyIndex < codeLength ? firstEmptyIndex : codeLength - 1), inputRefs)?.focus();
		}
	}, [
		codeLength,
		getInputId,
		inputState,
		inputRefs
	]);
	const inputs = [];
	for (let i = 0; i < codeLength; i += 1) {
		const inputId = getInputId(i);
		inputs.push(/* @__PURE__ */ jsx(SingleCodeInput, {
			value: inputState[inputId].value,
			id: inputId,
			inputRef: firstFieldRefCb,
			onPaste: onPasteCb,
			onKeyDown: (event) => onKeyDown(event, inputId),
			onChange: (event) => {
				if (isMobileSafari && event.nativeEvent instanceof CustomEvent) {
					autofillValuesRef.current += event.target.value;
					if (autofillValuesRef.current.length === codeLength) {
						setInputState(Array(codeLength).fill(0).reduce((acc, _, i) => {
							return {
								...acc,
								[getInputId(i)]: { value: autofillValuesRef.current[i] }
							};
						}, {}));
						if (onSubmit) onSubmit(autofillValuesRef.current);
						autofillValuesRef.current = "";
					}
				} else if (isMobile && isChrome && event.target.value.length === codeLength) {
					setInputState(Array(codeLength).fill(0).reduce((acc, _, i) => {
						return {
							...acc,
							[getInputId(i)]: { value: event.target.value[i] }
						};
					}, {}));
					if (onSubmit) onSubmit(event.target.value);
				}
			},
			type: "number",
			ariaLabel: `Code input ${i + 1} of ${codeLength}`,
			cssProp: inputCSS,
			onBlur: onBlurCb,
			width: variant === "unified" ? "12px" : `${(100 / inputsPerGroup).toFixed(2)}%`,
			max: 9,
			variant
		}, inputId));
	}
	if (variant === "unified") return /* @__PURE__ */ jsx(UnifiedCodeInputContainer, {
		onMouseDown: onContainerMouseDown,
		children: inputs
	});
	return /* @__PURE__ */ jsxs(StyledCodeInput, { children: [
		/* @__PURE__ */ jsx(CodeInputGroup, { children: inputs.slice(0, inputsPerGroup) }),
		/* @__PURE__ */ jsx(CodeInputGroupSeparator, {}),
		/* @__PURE__ */ jsx(CodeInputGroup, { children: inputs.slice(inputsPerGroup, inputs.length) })
	] });
}
const StyledCodeInput = styled.div`
  display: inline-flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
`;
const separatorWidth = 12;
const separatorMargin = 8;
const CodeInputGroup = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  width: calc(50% - ${(separatorWidth + separatorMargin * 2) / 2}px);
`;
const CodeInputGroupSeparator = styled.div`
  width: ${separatorWidth}px;
  height: 4px;
  background-color: ${textInputBorderColor};
  border-radius: 2px;
  margin: 0 ${separatorMargin}px;
`;
const UnifiedCodeInputContainer = styled.div`
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: ${Spacing.px.xs};
  padding: 12px 24px;
  border: 0.5px solid ${({ theme }) => theme.border};
  border-radius: 16px;
  background-color: ${({ theme }) => theme.bg};
  width: 100%;

  &:focus-within {
    border: 2px solid ${({ theme }) => theme.text};
  }
`;
//#endregion
export { CodeInput, StyledCodeInput };
