import { mount, type VueWrapper } from '@vue/test-utils';
import { nextTick } from 'vue';
import { afterEach, describe, expect, it } from 'vitest';
import {
    KviewAlert,
    KviewIconButton,
    KviewTextInput,
} from '@kview/ui-core/vue';
import KviewPrimitiveShowcase from '@/components/foundation/KviewPrimitiveShowcase.vue';

const showcases: VueWrapper[] = [];

const mountShowcase = (): VueWrapper => {
    const wrapper = mount(KviewPrimitiveShowcase, { attachTo: document.body });

    showcases.push(wrapper);

    return wrapper;
};

afterEach(() => {
    showcases.splice(0).forEach((wrapper) => wrapper.unmount());
    document.body.innerHTML = '';
});

describe('kView Vue primitives', () => {
    it('connects a text input to its hint, error and v-model contract', async () => {
        const wrapper = mount(KviewTextInput, {
            props: {
                name: 'query',
                label: 'Recherche',
                hint: 'Trois caractères minimum.',
                error: 'La recherche est obligatoire.',
                required: true,
                modelValue: '',
            },
            attrs: {
                'aria-describedby': 'server-help query_hint server-help',
                'aria-invalid': 'spelling',
                'aria-busy': true,
            },
        });

        const input = wrapper.get('input');

        expect(input.attributes('aria-describedby')).toBe('query_hint query_error server-help');
        expect(input.attributes('aria-invalid')).toBe('true');
        expect(input.attributes('aria-busy')).toBe('true');
        expect(input.attributes('required')).toBeDefined();

        await wrapper.setProps({ error: null });

        expect(input.attributes('aria-invalid')).toBe('spelling');

        await input.setValue('North Atlantic');

        expect(wrapper.emitted('update:modelValue')).toEqual([['North Atlantic']]);
    });

    it('forwards native text and datetime constraints without an application wrapper', () => {
        const wrapper = mount(KviewTextInput, {
            props: {
                name: 'observed_at',
                label: 'Observed at',
                type: 'datetime-local',
                modelValue: '2026-07-19T14:00',
                maxlength: 16,
                min: '2026-01-01T00:00',
                max: '2026-12-31T23:59',
                step: 60,
            },
        });

        expect(wrapper.get('input').attributes()).toMatchObject({
            type: 'datetime-local',
            maxlength: '16',
            min: '2026-01-01T00:00',
            max: '2026-12-31T23:59',
            step: '60',
        });
    });

    it('keeps icon buttons named and disabled without relying on a title attribute', () => {
        const wrapper = mount(KviewIconButton, {
            props: {
                id: 'create-search-trigger',
                icon: 'plus',
                label: 'Créer une recherche',
                disabled: true,
                haspopup: 'dialog',
            },
        });

        const button = wrapper.get('button');

        expect(button.attributes('aria-label')).toBe('Créer une recherche');
        expect(button.attributes('id')).toBe('create-search-trigger');
        expect(button.attributes('aria-haspopup')).toBe('dialog');
        expect(button.attributes('disabled')).toBeDefined();
        expect(button.attributes('title')).toBeUndefined();
    });

    it('does not turn a disabled tooltip control into a wrapper tab stop', () => {
        const wrapper = mount(KviewIconButton, {
            props: {
                icon: 'panel-left-close',
                label: 'Masquer la navigation',
                disabled: true,
            },
        });
        const button = wrapper.get('button');
        const trigger = wrapper.get('.ui-tooltip__trigger');

        expect(button.attributes('disabled')).toBeDefined();
        expect(button.attributes('aria-describedby')).toMatch(/^kview-tooltip-/);
        expect(trigger.attributes('tabindex')).toBeUndefined();
    });

    it('dismisses an alert through an explicit accessible command', async () => {
        const wrapper = mount(KviewAlert, {
            props: {
                title: 'Information',
                message: 'Contrat partagé.',
                dismissLabel: 'Fermer',
                resetKey: 'flash-1',
            },
        });

        await wrapper.get('button[aria-label="Fermer"]').trigger('click');

        expect(wrapper.emitted('dismiss')).toHaveLength(1);
        expect(wrapper.find('.ui-alert').exists()).toBe(false);

        await wrapper.setProps({ resetKey: 'flash-2' });

        expect(wrapper.get('.ui-alert').text()).toContain('Contrat partagé.');
    });

    it('updates the rendered feedback from a real Vue interaction', async () => {
        const wrapper = mountShowcase();

        await wrapper.get('input[name="phase3-query"]').setValue('Growth Europe');
        await wrapper.get('button.btn-primary').trigger('click');

        expect(wrapper.text()).toContain('Interaction Vue vérifiée');
        expect(wrapper.text()).toContain('Growth Europe');
    });

    it('keeps navigation and choice state inside the Vue showcase', async () => {
        const wrapper = mountShowcase();
        const performanceTab = wrapper.findAll('[role="tab"]')
            .find((tab) => tab.text().includes('Performance'));
        const oneYearSegment = wrapper.findAll('button.ui-segment')
            .find((segment) => segment.text().includes('1Y'));

        expect(performanceTab).toBeDefined();
        expect(oneYearSegment).toBeDefined();

        await performanceTab?.trigger('click');
        await oneYearSegment?.trigger('click');
        await wrapper.get<HTMLInputElement>('[role="switch"]').setValue(false);

        expect(wrapper.get('.kview-vue-showcase__choice-summary').text()).toContain('performance · one-year · comfortable');
    });

    it('demonstrates the public avatar, popover and toast contracts', async () => {
        const wrapper = mountShowcase();
        const labelledAvatar = wrapper.get('[aria-label="Avatar de démonstration de Guillaume Nahum"]');
        const popoverTrigger = wrapper.get<HTMLButtonElement>(
            'button[aria-label="Ouvrir le menu de démonstration"]',
        );
        const toastTrigger = wrapper.findAll('button')
            .find((button) => button.text().includes('Afficher une notification'));

        expect(labelledAvatar.text()).toBe('GN');
        expect(labelledAvatar.attributes('role')).toBe('img');
        expect(toastTrigger).toBeDefined();

        popoverTrigger.element.focus();
        await popoverTrigger.trigger('click');
        await nextTick();

        const popoverId = popoverTrigger.attributes('aria-controls');
        const popover = popoverId
            ? document.getElementById(popoverId)
            : null;
        const popoverClose = popover?.querySelector<HTMLButtonElement>(
            '[data-kview-showcase-popover-close]',
        );

        expect(popover?.parentElement).toBe(document.body);
        expect(popover?.hidden).toBe(false);
        expect(popover?.getAttribute('aria-labelledby')).toBeTruthy();
        expect(document.activeElement).toBe(popoverClose);

        document.dispatchEvent(new KeyboardEvent('keydown', {
            key: 'Escape',
            bubbles: true,
            cancelable: true,
        }));
        await nextTick();
        await nextTick();

        expect(popover?.hidden).toBe(true);
        expect(document.activeElement).toBe(popoverTrigger.element);

        toastTrigger?.element.focus();
        await toastTrigger?.trigger('click');
        await nextTick();

        const toastViewport = document.body.querySelector<HTMLElement>('.ui-toast-viewport');
        const toast = toastViewport?.querySelector<HTMLElement>('[role="status"]');

        expect(toastViewport?.parentElement).toBe(document.body);
        expect(toast?.textContent).toContain('Notification Vue');
        expect(toast?.textContent).toContain('sans déplacer le focus');
        expect(document.activeElement).toBe(toastTrigger?.element);
        expect(document.body.querySelectorAll('.ui-toast')).toHaveLength(1);
        expect(document.body.innerHTML).not.toContain('data-ui-');
    });
});
