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

vi.mock('../../resources/js/api/auth', () => ({
    twoFactorRegenerateRecoveryCodes: vi.fn(),
}));

import RecoveryCodesCard from '../../resources/js/components/settings/security/RecoveryCodesCard.vue';
import * as authApi from '../../resources/js/api/auth';
import { useAuthStore } from '../../resources/js/stores/auth';

const vuetify = createVuetify();

describe('RecoveryCodesCard (Q.DEFER.003 sub-B)', () => {
    beforeEach(() => {
        setActivePinia(createPinia());
        vi.clearAllMocks();
    });

    const factory = (totpEnabled: boolean) => {
        // Set auth state BEFORE mount — иначе computed has2fa зафиксируется false
        // на первом render и кнопка не появится reactive'но (Pinia store reactivity quirk).
        const auth = useAuthStore();
        auth.user = {
            id: 1,
            email: 'test@demo.local',
            first_name: 'Test',
            last_name: 'User',
            tenant_id: 1,
            totp_enabled: totpEnabled,
            last_login_at: null,
        } as unknown as ReturnType<typeof useAuthStore>['user'];
        const wrapper = mount(RecoveryCodesCard, {
            global: { plugins: [vuetify], stubs: { VDialog: true } },
        });
        return wrapper;
    };

    it('does NOT render button when 2FA disabled (auth.user.totp_enabled=false)', () => {
        const wrapper = factory(false);
        expect(wrapper.find('[data-testid="regen-codes-btn"]').exists()).toBe(false);
    });

    it('renders button when 2FA enabled', async () => {
        const wrapper = factory(true);
        await flushPromises();
        const btn = wrapper.find('[data-testid="regen-codes-btn"]');
        expect(btn.exists()).toBe(true);
        expect(btn.text()).toContain('Перегенерировать резервные коды');
    });

    it('clicking regen-codes-btn opens dialog (sets regenOpen)', async () => {
        const wrapper = factory(true);
        await flushPromises();
        await wrapper.find('[data-testid="regen-codes-btn"]').trigger('click');
        // VDialog is stubbed — check internal state via component instance
        expect((wrapper.vm as unknown as { regenOpen: boolean }).regenOpen).toBe(true);
    });

    it('confirmRegen() with valid password fetches 8 codes and clears password', async () => {
        const newCodes = [
            'AAAA-1111',
            'BBBB-2222',
            'CCCC-3333',
            'DDDD-4444',
            'EEEE-5555',
            'FFFF-6666',
            'GGGG-7777',
            'HHHH-8888',
        ];
        (authApi.twoFactorRegenerateRecoveryCodes as ReturnType<typeof vi.fn>).mockResolvedValue({
            recovery_codes: newCodes,
        });
        const wrapper = factory(true);
        await flushPromises();
        const vm = wrapper.vm as unknown as {
            regenOpen: boolean;
            regenPassword: string;
            regenCodes: string[];
            regenError: string;
            confirmRegen: () => Promise<void>;
        };
        vm.regenOpen = true;
        vm.regenPassword = 'correctPass';
        await vm.confirmRegen();
        await flushPromises();
        expect(vm.regenCodes).toEqual(newCodes);
        expect(vm.regenPassword).toBe('');
        expect(vm.regenError).toBe('');
        expect(authApi.twoFactorRegenerateRecoveryCodes).toHaveBeenCalledWith('correctPass');
    });

    it('confirmRegen() with invalid password sets error and keeps regenCodes empty', async () => {
        (authApi.twoFactorRegenerateRecoveryCodes as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('401'));
        const wrapper = factory(true);
        await flushPromises();
        const vm = wrapper.vm as unknown as {
            regenPassword: string;
            regenCodes: string[];
            regenError: string;
            confirmRegen: () => Promise<void>;
        };
        vm.regenPassword = 'wrongPass';
        await vm.confirmRegen();
        await flushPromises();
        expect(vm.regenError).toBe('Неверный пароль.');
        expect(vm.regenCodes).toEqual([]);
    });

    it('closeRegen() clears codes and closes dialog', async () => {
        const wrapper = factory(true);
        await flushPromises();
        const vm = wrapper.vm as unknown as {
            regenOpen: boolean;
            regenCodes: string[];
            closeRegen: () => void;
        };
        vm.regenOpen = true;
        vm.regenCodes = ['AAAA-1111'];
        vm.closeRegen();
        expect(vm.regenOpen).toBe(false);
        expect(vm.regenCodes).toEqual([]);
    });
});
