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', () => ({
    twoFactorInit: vi.fn(),
    twoFactorConfirm: vi.fn(),
    twoFactorDisable: vi.fn(),
}));

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

const vuetify = createVuetify();

type CardVm = {
    setupOpen: boolean;
    setupStep: 'init' | 'confirm' | 'codes';
    setupSecret: string;
    setupQrUrl: string;
    setupCode: string;
    setupRecoveryCodes: string[];
    setupError: string;
    setupBusy: boolean;
    disableOpen: boolean;
    disablePassword: string;
    disableError: string;
    openSetup: () => Promise<void>;
    confirmSetup: () => Promise<void>;
    closeSetup: () => void;
    confirmDisable: () => Promise<void>;
};

describe('TwoFactorCard (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,
        // see Task 2 RecoveryCodesCard.spec.ts learning).
        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(TwoFactorCard, {
            global: { plugins: [vuetify], stubs: { VDialog: true } },
        });
        return wrapper;
    };

    it('shows «Включить 2FA» button when 2FA disabled', async () => {
        const wrapper = factory(false);
        await flushPromises();
        const btn = wrapper.find('[data-testid="enable-2fa-btn"]');
        expect(btn.exists()).toBe(true);
        expect(btn.text()).toContain('Включить 2FA');
    });

    it('shows «Отключить 2FA» button when 2FA enabled', async () => {
        const wrapper = factory(true);
        await flushPromises();
        const btn = wrapper.find('[data-testid="disable-2fa-btn"]');
        expect(btn.exists()).toBe(true);
        expect(btn.text()).toContain('Отключить 2FA');
    });

    it('shows «включена» chip when 2FA enabled', async () => {
        const wrapper = factory(true);
        await flushPromises();
        expect(wrapper.text()).toContain('включена');
    });

    it('openSetup() initializes wizard and transitions init→confirm with QR secret', async () => {
        (authApi.twoFactorInit as ReturnType<typeof vi.fn>).mockResolvedValue({
            secret: 'JBSWY3DPEHPK3PXP',
            qr_url: 'otpauth://totp/test',
        });
        const wrapper = factory(false);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        await vm.openSetup();
        await flushPromises();
        expect(vm.setupOpen).toBe(true);
        expect(vm.setupStep).toBe('confirm');
        expect(vm.setupSecret).toBe('JBSWY3DPEHPK3PXP');
        expect(vm.setupQrUrl).toBe('otpauth://totp/test');
        expect(authApi.twoFactorInit).toHaveBeenCalled();
    });

    it('confirmSetup() valid code → step=codes + 8 recovery codes + auth.totp_enabled=true', async () => {
        const codes = ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8'];
        (authApi.twoFactorConfirm as ReturnType<typeof vi.fn>).mockResolvedValue({
            recovery_codes: codes,
        });
        const wrapper = factory(false);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        vm.setupCode = '123456';
        await vm.confirmSetup();
        await flushPromises();
        expect(vm.setupStep).toBe('codes');
        expect(vm.setupRecoveryCodes).toEqual(codes);
        const auth = useAuthStore();
        expect(auth.user?.totp_enabled).toBe(true);
        expect(authApi.twoFactorConfirm).toHaveBeenCalledWith('123456');
    });

    it('confirmSetup() invalid code → setupError set, step stays confirm', async () => {
        (authApi.twoFactorConfirm as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('422'));
        const wrapper = factory(false);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        vm.setupStep = 'confirm';
        vm.setupCode = '999999';
        await vm.confirmSetup();
        await flushPromises();
        expect(vm.setupError).toBe('Неверный код. Проверьте время на устройстве.');
        expect(vm.setupStep).toBe('confirm');
    });

    it('closeSetup() resets all setup state', async () => {
        const wrapper = factory(false);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        vm.setupOpen = true;
        vm.setupSecret = 'SECRET';
        vm.setupQrUrl = 'url';
        vm.setupCode = '123';
        vm.closeSetup();
        expect(vm.setupOpen).toBe(false);
        expect(vm.setupSecret).toBe('');
        expect(vm.setupQrUrl).toBe('');
        expect(vm.setupCode).toBe('');
    });

    it('confirmDisable() valid password → auth.totp_enabled=false + dialog closes', async () => {
        (authApi.twoFactorDisable as ReturnType<typeof vi.fn>).mockResolvedValue(undefined);
        const wrapper = factory(true);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        vm.disableOpen = true;
        vm.disablePassword = 'correctPass';
        await vm.confirmDisable();
        await flushPromises();
        const auth = useAuthStore();
        expect(auth.user?.totp_enabled).toBe(false);
        expect(vm.disableOpen).toBe(false);
        expect(vm.disablePassword).toBe('');
        expect(authApi.twoFactorDisable).toHaveBeenCalledWith('correctPass');
    });

    it('confirmDisable() invalid password → disableError set, totp stays enabled', async () => {
        (authApi.twoFactorDisable as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('401'));
        const wrapper = factory(true);
        await flushPromises();
        const vm = wrapper.vm as unknown as CardVm;
        vm.disablePassword = 'wrongPass';
        await vm.confirmDisable();
        await flushPromises();
        expect(vm.disableError).toBe('Неверный пароль.');
        const auth = useAuthStore();
        expect(auth.user?.totp_enabled).toBe(true);
    });
});
