<script setup lang="ts">
    import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
    import { buildYupChain } from '@/UI-kit/utils';
    import UIInput from '@/UI-kit/components/UI/inputs/UIInput.vue';
    import UIInfo from '@/UI-kit/components/UI/info/UIInfo.vue';
    import { EInputComponentType, EInputInputmode, UIColorName } from '@/UI-kit/types';
    import { useField, useForm } from 'vee-validate';
    import { string } from 'yup';
    import { requiredValueValidator } from '@/modules/documents/constants/validation/common.validation';
    import { UIColor } from '@/UI-kit/constants';
    import { useI18n } from 'vue-i18n';

    type InputElement = {
        focus: () => void;
        blur: () => void;
        setSelectionRange: (start: number, end: number) => void;
    };

    const props = withDefaults(
        defineProps<{
            isError?: boolean;
            isSubmitted?: boolean;
            errorMessage?: string;
            codeArray?: string[];
            codeLength?: number;
        }>(),
        {
            isError: false,
            isSubmitted: false,
            errorMessage: '',
            codeArray: () => [],
            codeLength: 4,
        },
    );

    const emit = defineEmits<{
        'update:model-value': [string];
        'code:send': [{ code: string; codeArray: string[] }];
        'code:update': [{ value: string; index: number }];
    }>();

    const { t } = useI18n();

    const inputs = ref<(InputElement | null)[]>([]);

    const isReady = ref(false);
    const activeInput = ref<number | null>(null);
    const isExternalErrorDismissed = ref(false);

    const formData = reactive({
        codeArray: Array(props.codeLength).fill(''),
    });

    const { handleSubmit, errors, setFieldError } = useForm({
        validationSchema: {
            code: buildYupChain(string(), [requiredValueValidator()]).min(
                props.codeLength,
                t('helpers.validation.error'),
            ),
        },
    });

    const { value: code } = useField<string>('code', undefined, {
        validateOnValueUpdate: false,
    });

    const hasInputCodeError = computed(() => {
        return !!errors.value.code || (props.isError && !isExternalErrorDismissed.value);
    });

    const errorMsg = computed(() => {
        if (errors.value.code) {
            return errors.value.code;
        }

        if (props.isError && !isExternalErrorDismissed.value) {
            return props.errorMessage;
        }

        return '';
    });

    const resetErrorState = (key: any) => {
        setFieldError(key, '');
    };

    const update = (value: string, index: number) => {
        if (!isReady.value) {
            return;
        }

        isExternalErrorDismissed.value = true;

        emit('code:update', { value, index });

        resetErrorState('code');

        formData.codeArray[index] = value;

        if (value && index < inputs.value.length - 1) {
            inputs.value[index]?.blur();
            inputs.value[index + 1]?.focus();
            inputs.value[index + 1]?.setSelectionRange(0, 1);
            activeInput.value = index + 1;
        }

        if (!value && index > 0) {
            inputs.value[index - 1]?.focus();
            activeInput.value = index - 1;
        }

        code.value = formData.codeArray.join('') || '';

        emit('update:model-value', code.value);
    };

    const send = async () => {
        await handleSubmit(async () => {
            emit('code:send', { code: code.value, codeArray: formData.codeArray });
        })();
    };

    const resetForm = () => {
        formData.codeArray = Array(props.codeLength).fill('');
        code.value = '';
        resetErrorState('code');
        isExternalErrorDismissed.value = true;
    };

    const resetFocus = (evt: Event) => {
        if (evt.target?.className === 'ui-input') {
            return;
        }
        activeInput.value = null;
    };

    onUnmounted(() => {
        resetForm();

        document.removeEventListener('click', resetFocus);
    });

    onMounted(() => {
        setTimeout(() => {
            isReady.value = true;
        }, 100);

        document.addEventListener('click', resetFocus);
    });

    defineExpose({
        send,
        resetForm,
    });

    watch(
        () => props.isError,
        (isError, prevIsError) => {
            if (isError && !prevIsError) {
                isExternalErrorDismissed.value = false;
            }

            if (!isError) {
                isExternalErrorDismissed.value = false;
            }
        },
    );
</script>

<template>
    <div class="auth-check-code-form__inner">
        <div class="auth-check-code-form__inputs">
            <UIInput
                v-for="(_, index) in codeLength"
                :key="index"
                :ref="(el: any) => (inputs[index] = el)"
                v-model.trim="formData.codeArray[index]"
                mask="#"
                :component="EInputComponentType.mask"
                placeholder="0"
                class="auth-check-code-form__input"
                :class="{
                    '--error': hasInputCodeError,
                    '--active': activeInput === index,
                }"
                :disabled="isSubmitted"
                :inputmode="EInputInputmode.numeric"
                @update:model-value="update($event.trim(), index)"
                @input="resetErrorState('code')"
                @click="activeInput = index"
            />
        </div>
        <UIInfo
            v-if="hasInputCodeError"
            class="auth-check-code-form__error"
            is-hide-icon
            is-hide-line
            :bg-color="UIColor.Red2"
            :color="UIColor[UIColorName.Red600]"
        >
            {{ errorMsg }}
        </UIInfo>
    </div>
</template>

<style lang="scss" scoped>
    @use '@/UI-kit/assets/scss' as *;

    .auth-check-code-form {
        &__inner {
            @include text;
            @include flex(column);

            gap: 18px;
        }

        &__error {
            @include text(13px, 400, 20px, var(--red-600));
            @include flex(row, 0, center, center);

            margin: 4px auto 0;
            text-align: center;
        }

        &__inputs {
            @include flex(row, 0, center, center);

            gap: 10px;
            margin: 0 auto;
        }

        &__input {
            width: 60px;
            height: 60px;

            &:deep(.ui-input) {
                @include text(18px, 400, 24px);

                height: 60px;
                text-align: center;
                transition:
                    color 0.25s,
                    border 0.25s;

                &:hover,
                &:focus {
                    border-color: var(--gray-300);
                }

                &::placeholder {
                    font-size: 18px;
                }
            }

            &.--error {
                &:deep(.ui-input) {
                    color: var(--red-600);
                    border: 2px solid rgb(250 51 79 / 75%);
                    border-radius: 8px;
                }
            }

            &.--active {
                &:deep(.ui-input) {
                    border-color: var(--blue-500);
                }
            }
        }
    }
</style>
