<script setup lang="ts">
/**
 * Экран 2FA — ввод 6-значного кода из приложения-аутентификатора.
 *
 * Источник дизайна: liderra_v8_handoff/concepts/v8_login.html секция #form-2fa.
 * Источник логики: ТЗ v8.5 §1.6 / Прил. Г.4.2 — TOTP 6 цифр, 30-сек окно;
 * после 5 неудачных попыток — блок аккаунта на 15 минут.
 *
 * UX: 6 input-cell'ов, авто-переход на следующую при вводе цифры,
 * Backspace удаляет и переходит назад, paste 6 цифр заполняет все.
 */
import { extractValidationErrors } from '../../api/client';
import { useAuthStore } from '../../stores/auth';
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue';
import { useRouter } from 'vue-router';

const code = ref(['', '', '', '', '', '']);
// Vue 3.5 useTemplateRef() — Sprint 2 Phase B / O-stack-04. Заменяет
// `const inputs = ref<HTMLInputElement[]>([])`; имя ref'а в template = 'inputs'.
const inputs = useTemplateRef<HTMLInputElement[]>('inputs');
const codeFull = computed(() => code.value.join(''));
const canSubmit = computed(() => codeFull.value.length === 6 && /^\d{6}$/.test(codeFull.value));
const errors = ref<Record<string, string[]>>({});

const auth = useAuthStore();
const router = useRouter();

const userEmail = computed(() => auth.user?.email ?? 'аккаунт');

// Если попали на /2fa без pending state (requires2fa=false и не залогинен) —
// прямой URL без login → отправляем на /login.
onMounted(() => {
    if (!auth.requires2fa && !auth.isAuthenticated) {
        router.replace('/login');
    }
});

function onInput(index: number, event: Event) {
    const target = event.target as HTMLInputElement;
    const v = target.value.replace(/\D/g, '').slice(-1);
    code.value[index] = v;
    if (v && index < 5) {
        nextTick(() => inputs.value?.[index + 1]?.focus());
    }
}

function onKeydown(index: number, event: KeyboardEvent) {
    if (event.key === 'Backspace' && !code.value[index] && index > 0) {
        nextTick(() => inputs.value?.[index - 1]?.focus());
    }
}

function onPaste(event: ClipboardEvent) {
    const pasted = event.clipboardData?.getData('text').replace(/\D/g, '').slice(0, 6) ?? '';
    if (pasted.length === 6) {
        event.preventDefault();
        for (let i = 0; i < 6; i++) {
            code.value[i] = pasted[i];
        }
        nextTick(() => inputs.value?.[5]?.focus());
    }
}

async function handleSubmit() {
    errors.value = {};
    try {
        await auth.verifyTwoFactor(codeFull.value);
        await router.push('/dashboard');
    } catch (error: unknown) {
        const validationErrors = extractValidationErrors(error);
        if (validationErrors) {
            errors.value = validationErrors;
        } else {
            errors.value = { code: ['Произошла ошибка. Попробуйте ещё раз.'] };
        }
        // Очищаем код и фокусим первую cell для повторного ввода.
        code.value = ['', '', '', '', '', ''];
        nextTick(() => inputs.value?.[0]?.focus());
    }
}
</script>

<template>
    <v-card variant="flat" :max-width="380" width="100%" color="transparent" class="twofactor-card">
        <header class="twofactor-header">
            <h1 class="text-h5 mb-1">Двухфакторная проверка</h1>
            <p class="text-body-2 text-medium-emphasis ma-0">
                Откройте приложение-аутентификатор и введите 6-значный код для
                <strong>{{ userEmail }}</strong>
            </p>
        </header>

        <v-form class="twofactor-form" @submit.prevent="handleSubmit">
            <div class="code-row" @paste="onPaste">
                <input
                    v-for="(_, i) in code"
                    :key="i"
                    ref="inputs"
                    v-model="code[i]"
                    type="text"
                    inputmode="numeric"
                    maxlength="1"
                    class="code-cell"
                    :aria-label="`Цифра ${i + 1}`"
                    @input="onInput(i, $event)"
                    @keydown="onKeydown(i, $event)"
                />
            </div>

            <div v-if="errors.code?.length" class="text-error text-caption mb-1">
                {{ errors.code[0] }}
            </div>

            <v-alert
                v-if="auth.lockoutSeconds !== null"
                type="error"
                variant="tonal"
                density="compact"
                class="mb-2"
                data-testid="lockout-alert"
            >
                Слишком много попыток. Попробуйте через {{ Math.ceil(auth.lockoutSeconds / 60) }} мин.
            </v-alert>

            <div class="d-flex justify-space-between align-center mb-2">
                <RouterLink to="/recovery-use" class="text-body-2 text-primary">
                    Использовать резервный код
                </RouterLink>
                <span class="text-caption text-medium-emphasis font-mono">02:34</span>
            </div>

            <v-btn
                type="submit"
                color="primary"
                block
                size="large"
                variant="flat"
                :disabled="!canSubmit"
                :loading="auth.loading"
            >
                Подтвердить
            </v-btn>
        </v-form>
    </v-card>
</template>

<style scoped>
.twofactor-card {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.twofactor-header h1 {
    font-variation-settings: 'opsz' 26;
    letter-spacing: -0.018em;
}

.twofactor-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.code-row {
    display: flex;
    gap: 8px;
    justify-content: space-between;
}

.code-cell {
    width: 48px;
    height: 56px;
    text-align: center;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 22px;
    font-weight: 500;
    border: 1px solid #d9d5cd;
    border-radius: 8px;
    background: #fff;
    color: #081319;
    transition: border-color 0.15s;
}
.code-cell:focus {
    outline: none;
    border-color: #0f6e56;
    box-shadow: 0 0 0 2px rgba(15, 110, 86, 0.2);
}

.font-mono {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-feature-settings: 'tnum';
}
</style>
