import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import ImpersonationBanner from '../../resources/js/components/admin/ImpersonationBanner.vue';
import type { ImpersonationActiveSession } from '../../resources/js/api/admin';

vi.mock('../../resources/js/api/admin', async (importOriginal) => {
    const orig = await importOriginal<typeof import('../../resources/js/api/admin')>();
    return { ...orig, impersonationActive: vi.fn() };
});

const adminApi = await import('../../resources/js/api/admin');

function makeSession(overrides: Partial<ImpersonationActiveSession> = {}): ImpersonationActiveSession {
    return {
        token_id: 1,
        tenant_id: 10,
        tenant_name: 'ООО Ромашка',
        requested_by: 7,
        reason: 'Диагностика проблемы с балансом по обращению клиента',
        sent_to_email: 'client@romashka.ru',
        used_at: '2026-05-16T08:00:00Z',
        expires_at: '2026-05-16T08:15:00Z',
        ...overrides,
    };
}

const mountBanner = () =>
    mount(ImpersonationBanner, {
        global: {
            plugins: [createVuetify()],
            stubs: {
                RouterLink: {
                    props: ['to'],
                    inheritAttrs: false,
                    template: '<a v-bind="$attrs" :href="to"><slot /></a>',
                },
            },
        },
    });

beforeEach(() => vi.clearAllMocks());
afterEach(() => vi.useRealTimers());

describe('ImpersonationBanner', () => {
    it('вызывает impersonationActive на mount', async () => {
        vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([]);
        const wrapper = mountBanner();
        await flushPromises();
        expect(adminApi.impersonationActive).toHaveBeenCalledTimes(1);
        wrapper.unmount();
    });

    it('0 активных сессий — баннер не рендерится', async () => {
        vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([]);
        const wrapper = mountBanner();
        await flushPromises();
        expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
        wrapper.unmount();
    });

    it('1 активная сессия — баннер с именем тенанта + ссылка на /admin/impersonation', async () => {
        vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([makeSession({ tenant_name: 'ООО Ромашка' })]);
        const wrapper = mountBanner();
        await flushPromises();
        const banner = wrapper.find('[data-testid="impersonation-banner"]');
        expect(banner.exists()).toBe(true);
        expect(banner.text()).toContain('Активна impersonation-сессия');
        expect(banner.text()).toContain('ООО Ромашка');
        expect(wrapper.find('[data-testid="impersonation-banner-link"]').attributes('href')).toBe(
            '/admin/impersonation',
        );
        wrapper.unmount();
    });

    it('несколько активных сессий — баннер показывает счётчик', async () => {
        vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([
            makeSession({ token_id: 1 }),
            makeSession({ token_id: 2 }),
            makeSession({ token_id: 3 }),
        ]);
        const wrapper = mountBanner();
        await flushPromises();
        expect(wrapper.find('[data-testid="impersonation-banner"]').text()).toContain(
            'Активны impersonation-сессии: 3',
        );
        wrapper.unmount();
    });

    it('tenant_name=null — fallback на «тенант #id»', async () => {
        vi.mocked(adminApi.impersonationActive).mockResolvedValueOnce([
            makeSession({ tenant_name: null, tenant_id: 42 }),
        ]);
        const wrapper = mountBanner();
        await flushPromises();
        expect(wrapper.find('[data-testid="impersonation-banner"]').text()).toContain('тенант #42');
        wrapper.unmount();
    });

    it('ошибка impersonationActive — баннер не падает и остаётся скрыт', async () => {
        vi.mocked(adminApi.impersonationActive).mockRejectedValueOnce(new Error('500'));
        const wrapper = mountBanner();
        await flushPromises();
        expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
        wrapper.unmount();
    });

    it('polling — impersonationActive вызывается повторно через 30 с', async () => {
        vi.useFakeTimers();
        vi.mocked(adminApi.impersonationActive).mockResolvedValue([]);
        const wrapper = mountBanner();
        await vi.advanceTimersByTimeAsync(0);
        expect(adminApi.impersonationActive).toHaveBeenCalledTimes(1);
        await vi.advanceTimersByTimeAsync(30_000);
        expect(adminApi.impersonationActive).toHaveBeenCalledTimes(2);
        wrapper.unmount();
    });

    it('polling — данные второго опроса обновляют баннер', async () => {
        vi.useFakeTimers();
        vi.mocked(adminApi.impersonationActive)
            .mockResolvedValueOnce([])
            .mockResolvedValue([makeSession({ tenant_name: 'ООО Ромашка' })]);
        const wrapper = mountBanner();
        await vi.advanceTimersByTimeAsync(0);
        expect(wrapper.find('[data-testid="impersonation-banner"]').exists()).toBe(false);
        await vi.advanceTimersByTimeAsync(30_000);
        await wrapper.vm.$nextTick();
        const banner = wrapper.find('[data-testid="impersonation-banner"]');
        expect(banner.exists()).toBe(true);
        expect(banner.text()).toContain('ООО Ромашка');
        wrapper.unmount();
    });
});
