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

vi.mock('../../resources/js/api/admin', () => ({
    updateSystemSetting: vi.fn(),
    listSystemSettings: vi.fn(),
}));
vi.mock('../../resources/js/api/client', () => ({
    extractValidationErrors: vi.fn(() => null),
    extractErrorMessage: vi.fn((_e, fb?: string) => fb ?? 'err'),
    apiClient: {},
    ensureCsrfCookie: vi.fn(),
}));

import * as adminApi from '../../resources/js/api/admin';
import SystemSettingEditDialog from '../../resources/js/components/admin/SystemSettingEditDialog.vue';

const sampleSetting: adminApi.SystemSetting = {
    key: 'login_max_attempts',
    value: '5',
    type: 'int',
    description: 'Макс. неудачных попыток входа в окне 15 минут',
    updated_at: '2026-05-09T10:00:00',
    updated_by: null,
};

const factory = (
    props: { modelValue: boolean; setting: adminApi.SystemSetting | null } = {
        modelValue: true,
        setting: sampleSetting,
    },
) =>
    mount(SystemSettingEditDialog, {
        props: { ...props, requestedBy: 1 },
        global: {
            plugins: [createVuetify()],
            stubs: {
                VDialog: {
                    template: '<div class="dialog-stub" v-if="modelValue"><slot /></div>',
                    props: ['modelValue'],
                },
            },
        },
    });

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

    it('не рендерит content при modelValue=false', () => {
        const wrapper = factory({ modelValue: false, setting: sampleSetting });
        expect(wrapper.find('.dialog-stub').exists()).toBe(false);
    });

    it('step 1 — показывает key + type-chip + текущее значение + 2 input', () => {
        const wrapper = factory();
        const text = wrapper.text();
        expect(text).toContain('login_max_attempts');
        expect(text).toContain('int');
        expect(text).toContain('Макс. неудачных попыток');
        expect(wrapper.find('[data-testid="value-input"]').exists()).toBe(true);
        expect(wrapper.find('[data-testid="reason-input"]').exists()).toBe(true);
        expect(wrapper.find('[data-testid="next-btn"]').exists()).toBe(true);
    });

    it('newValue по умолчанию = текущее значение setting', () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as { newValue: string };
        expect(vm.newValue).toBe('5');
    });

    it('Далее без изменения значения → ошибка «совпадает»', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as { reason: string; valueError: string | null; step: string };
        vm.reason = 'A'.repeat(35);
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="next-btn"]').trigger('click');
        await flushPromises();
        expect(vm.valueError).toContain('совпадает');
        expect(vm.step).toBe('edit');
    });

    it('Далее с reason < 30 chars → ошибка', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as {
            newValue: string;
            reason: string;
            reasonError: string | null;
            step: string;
        };
        vm.newValue = '7';
        vm.reason = 'короткое';
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="next-btn"]').trigger('click');
        await flushPromises();
        expect(vm.reasonError).toBe('Минимум 30 символов.');
        expect(vm.step).toBe('edit');
    });

    it('Далее с валидными данными → step confirm + diff before/after', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as { newValue: string; reason: string; step: string };
        vm.newValue = '7';
        vm.reason = 'Решение CTO от 09.05.2026: ослабляем лимит для UX тестирования.';
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="next-btn"]').trigger('click');
        await flushPromises();
        expect(vm.step).toBe('confirm');
        const text = wrapper.text();
        expect(text).toContain('Было');
        expect(text).toContain('Станет');
        // Reason отображается на confirm-step
        expect(text).toContain('CTO от 09.05');
    });

    it('Применить → API + emit updated + step done', async () => {
        vi.mocked(adminApi.updateSystemSetting).mockResolvedValue({
            key: 'login_max_attempts',
            value: '7',
            previous_value: '5',
            updated_at: '2026-05-09T11:00:00',
            message: 'OK',
        });
        const wrapper = factory();
        const vm = wrapper.vm as unknown as { newValue: string; reason: string; step: string };
        vm.newValue = '7';
        vm.reason = 'Решение CTO от 09.05.2026: ослабляем лимит для UX тестирования.';
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="next-btn"]').trigger('click');
        await flushPromises();
        await wrapper.find('[data-testid="submit-btn"]').trigger('click');
        await flushPromises();

        expect(adminApi.updateSystemSetting).toHaveBeenCalledWith('login_max_attempts', {
            value: '7',
            reason: expect.stringContaining('CTO'),
            admin_user_id: 1,
        });
        expect(wrapper.emitted('updated')).toBeDefined();
        expect(vm.step).toBe('done');
    });

    it('Назад с confirm возвращает на edit с сохранёнными данными', async () => {
        const wrapper = factory();
        const vm = wrapper.vm as unknown as { newValue: string; reason: string; step: string };
        vm.newValue = '7';
        vm.reason = 'Решение CTO от 09.05.2026: ослабляем лимит для UX тестирования.';
        await wrapper.vm.$nextTick();
        await wrapper.find('[data-testid="next-btn"]').trigger('click');
        await flushPromises();
        expect(vm.step).toBe('confirm');
        await wrapper.find('[data-testid="back-btn"]').trigger('click');
        await flushPromises();
        expect(vm.step).toBe('edit');
        expect(vm.newValue).toBe('7'); // сохранилось
    });
});
