import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
import { createRouter, createMemoryHistory } from 'vue-router';

vi.mock('../../resources/js/api/auth', () => ({
    login: vi.fn(),
    register: vi.fn(),
    me: vi.fn(),
    logout: vi.fn(),
    verifyTwoFactor: vi.fn(),
    forgotPassword: vi.fn(),
    resetPassword: vi.fn(),
    useRecoveryCode: vi.fn(),
}));

vi.mock('../../resources/js/api/client', () => ({
    extractRateLimitRetry: vi.fn(() => null),
    extractValidationErrors: vi.fn(() => null),
    extractErrorMessage: vi.fn(() => 'Ошибка'),
    apiClient: {},
    ensureCsrfCookie: vi.fn(),
}));

import * as authApi from '../../resources/js/api/auth';
import { useAuthStore } from '../../resources/js/stores/auth';
import UseRecoveryCodeView from '../../resources/js/views/auth/UseRecoveryCodeView.vue';

const mountView = async () => {
    const pinia = createPinia();
    setActivePinia(pinia);
    // Симулируем pending-2FA state — иначе onMounted редиректит на /login.
    const auth = useAuthStore();
    auth.requires2fa = true;

    const router = createRouter({
        history: createMemoryHistory(),
        routes: [
            { path: '/recovery-use', name: 'recovery-use', component: UseRecoveryCodeView },
            { path: '/login', name: 'login', component: { template: '<div>stub</div>' } },
            { path: '/2fa', name: '2fa', component: { template: '<div>stub</div>' } },
            { path: '/dashboard', name: 'dashboard', component: { template: '<div>stub</div>' } },
        ],
    });
    await router.push('/recovery-use');
    await router.isReady();
    return mount(UseRecoveryCodeView, {
        global: { plugins: [pinia, createVuetify(), router] },
    });
};

describe('UseRecoveryCodeView.vue', () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    it('монтируется и содержит заголовок «Резервный код»', async () => {
        const wrapper = await mountView();
        expect(wrapper.text()).toContain('Резервный код');
    });

    it('содержит input с label XXXX-XXXX и autocomplete=one-time-code', async () => {
        const wrapper = await mountView();
        const input = wrapper.find('input');
        expect(input.exists()).toBe(true);
        expect(input.attributes('autocomplete')).toBe('one-time-code');
    });

    it('успешный submit вызывает auth.useRecoveryCode и сохраняет remaining в sessionStorage', async () => {
        vi.mocked(authApi.useRecoveryCode).mockResolvedValue({
            user: {
                id: 1,
                email: 'r@example.ru',
                first_name: 'R',
                last_name: 'C',
                tenant_id: 1,
                totp_enabled: true,
                last_login_at: null,
            },
            requires_2fa: false,
            recovery_codes_remaining: 3,
        });

        const wrapper = await mountView();
        await wrapper.find('input').setValue('ABCD-1234');
        await wrapper.find('form').trigger('submit.prevent');
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(authApi.useRecoveryCode).toHaveBeenCalledWith('ABCD-1234');
        expect(window.sessionStorage.getItem('recovery_codes_remaining')).toBe('3');
    });

    it('при 429 показывает lockout-alert', async () => {
        const wrapper = await mountView();
        const auth = useAuthStore();
        auth.lockoutSeconds = 600;
        await wrapper.vm.$nextTick();

        const alert = wrapper.find('[data-testid="lockout-alert"]');
        expect(alert.exists()).toBe(true);
        expect(alert.text()).toContain('10 мин');
    });
});
