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

// Мокаем api/auth до import'а ForgotPasswordView (внутри useAuthStore).
vi.mock('../../resources/js/api/auth', () => ({
    login: vi.fn(),
    register: vi.fn(),
    me: vi.fn(),
    logout: vi.fn(),
    verifyTwoFactor: vi.fn(),
    forgotPassword: 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 ForgotPasswordView from '../../resources/js/views/auth/ForgotPasswordView.vue';

const mountForgot = async () => {
    const pinia = createPinia();
    setActivePinia(pinia);
    const router = createRouter({
        history: createMemoryHistory(),
        routes: [
            { path: '/forgot', component: ForgotPasswordView },
            { path: '/login', name: 'login', component: { template: '<div>stub</div>' } },
        ],
    });
    await router.push('/forgot');
    await router.isReady();
    return mount(ForgotPasswordView, {
        global: { plugins: [pinia, createVuetify(), router] },
    });
};

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

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

    it('содержит rate-limit alert с лимитом 5 попыток / 15 минут', async () => {
        const wrapper = await mountForgot();
        const text = wrapper.text();
        expect(text).toContain('5 попыток в 15 минут');
    });

    it('содержит email-input и кнопку «Отправить ссылку»', async () => {
        const wrapper = await mountForgot();
        expect(wrapper.find('input[type="email"]').exists()).toBe(true);
        expect(wrapper.text()).toContain('Отправить ссылку');
    });

    it('после успешного submit показывает success-state и скрывает форму', async () => {
        vi.mocked(authApi.forgotPassword).mockResolvedValue({
            message: 'Если такой email зарегистрирован — мы отправили ссылку для сброса пароля.',
        });

        const wrapper = await mountForgot();
        const input = wrapper.find('input[type="email"]');
        await input.setValue('user@example.ru');
        await wrapper.find('form').trigger('submit.prevent');

        // Дожидаемся async submit.
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(wrapper.find('[data-testid="forgot-success"]').exists()).toBe(true);
        // Форма скрыта после submit.
        expect(wrapper.find('form').exists()).toBe(false);
        expect(authApi.forgotPassword).toHaveBeenCalledWith('user@example.ru');
    });

    it('при 429 показывает lockout-alert через auth.lockoutSeconds', async () => {
        const wrapper = await mountForgot();
        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 мин');
    });

    it('A5: при не-валидационной ошибке (500/network) показывает generic fallback', async () => {
        // forgotPassword отклоняется обычной ошибкой; extractValidationErrors и
        // extractRateLimitRetry замоканы → null (см. vi.mock в шапке файла).
        vi.mocked(authApi.forgotPassword).mockRejectedValue(new Error('Network Error'));

        const wrapper = await mountForgot();
        await wrapper.find('input[type="email"]').setValue('user@example.ru');
        await wrapper.find('form').trigger('submit.prevent');
        await flushPromises();

        const messages = wrapper.findAll('.v-messages__message').map((m) => m.text());
        expect(messages.join(' ')).toContain('Произошла ошибка. Попробуйте позже.');
    });
});
