/**
 * Accessibility Manager Tests
 *
 * Tests for ARIA live regions, focus management, and screen reader support.
 */

import { AccessibilityManager } from '../../../../resources/js/modules/livecomponent/AccessibilityManager.js';

describe('AccessibilityManager', () => {
    let manager;
    let container;

    beforeEach(() => {
        manager = new AccessibilityManager();
        container = document.createElement('div');
        container.dataset.liveComponent = 'test-component:1';
        document.body.appendChild(container);
    });

    afterEach(() => {
        document.body.innerHTML = '';
    });

    describe('initialize', () => {
        it('creates global live region', () => {
            manager.initialize();

            expect(manager.liveRegion).not.toBeNull();
            expect(manager.liveRegion.id).toBe('livecomponent-announcer');
            expect(manager.liveRegion.getAttribute('role')).toBe('status');
            expect(manager.liveRegion.getAttribute('aria-live')).toBe('polite');
        });

        it('applies screen reader only styles', () => {
            manager.initialize();

            const region = manager.liveRegion;
            expect(region.style.position).toBe('absolute');
            expect(region.style.left).toBe('-10000px');
            expect(region.style.width).toBe('1px');
            expect(region.style.height).toBe('1px');
        });
    });

    describe('createComponentLiveRegion', () => {
        beforeEach(() => {
            manager.initialize();
        });

        it('creates component-specific live region', () => {
            const region = manager.createComponentLiveRegion('test:1', container);

            expect(region).not.toBeNull();
            expect(region.id).toBe('livecomponent-test:1-announcer');
            expect(region.getAttribute('aria-live')).toBe('polite');
            expect(container.contains(region)).toBe(true);
        });

        it('reuses existing component live region', () => {
            const region1 = manager.createComponentLiveRegion('test:1', container);
            const region2 = manager.createComponentLiveRegion('test:1', container);

            expect(region1).toBe(region2);
            expect(container.querySelectorAll('[role="status"]')).toHaveLength(1);
        });

        it('supports assertive politeness level', () => {
            const region = manager.createComponentLiveRegion('test:1', container, 'assertive');

            expect(region.getAttribute('aria-live')).toBe('assertive');
        });
    });

    describe('announce', () => {
        beforeEach(() => {
            manager.initialize();
            jest.useFakeTimers();
        });

        afterEach(() => {
            jest.useRealTimers();
        });

        it('queues announcement', () => {
            manager.announce('Test message');

            expect(manager.announcementQueue).toHaveLength(1);
            expect(manager.announcementQueue[0].message).toBe('Test message');
        });

        it('throttles announcements', () => {
            manager.announce('Message 1');
            manager.announce('Message 2');
            manager.announce('Message 3');

            // Should only keep most recent
            jest.advanceTimersByTime(500);

            expect(manager.liveRegion.textContent).toBe('Message 3');
            expect(manager.announcementQueue).toHaveLength(0);
        });

        it('uses component-specific live region when provided', () => {
            manager.createComponentLiveRegion('test:1', container);

            manager.announce('Component message', 'polite', 'test:1');
            jest.advanceTimersByTime(600);

            const componentRegion = manager.componentLiveRegions.get('test:1');
            expect(componentRegion.textContent).toBe('Component message');
        });

        it('updates politeness for assertive announcements', () => {
            manager.announce('Urgent message', 'assertive');
            jest.advanceTimersByTime(500);

            expect(manager.liveRegion.getAttribute('aria-live')).toBe('assertive');
        });
    });

    describe('captureFocusState', () => {
        it('captures focus state for focused element', () => {
            const input = document.createElement('input');
            input.type = 'text';
            input.id = 'test-input';
            input.value = 'test value';
            container.appendChild(input);

            input.focus();
            input.selectionStart = 2;
            input.selectionEnd = 5;

            const focusState = manager.captureFocusState('test:1', container);

            expect(focusState).not.toBeNull();
            expect(focusState.id).toBe('test-input');
            expect(focusState.selectionStart).toBe(2);
            expect(focusState.selectionEnd).toBe(5);
        });

        it('captures data-lc-keep-focus attribute', () => {
            const button = document.createElement('button');
            button.setAttribute('data-lc-keep-focus', '');
            container.appendChild(button);

            button.focus();

            const focusState = manager.captureFocusState('test:1', container);

            expect(focusState.keepFocus).toBe(true);
        });

        it('returns null when focus is outside container', () => {
            const externalInput = document.createElement('input');
            document.body.appendChild(externalInput);
            externalInput.focus();

            const focusState = manager.captureFocusState('test:1', container);

            expect(focusState).toBeNull();
        });

        it('captures scroll position', () => {
            const textarea = document.createElement('textarea');
            textarea.style.height = '50px';
            textarea.style.overflow = 'scroll';
            textarea.value = 'Line 1\n'.repeat(20);
            container.appendChild(textarea);

            textarea.focus();
            textarea.scrollTop = 100;

            const focusState = manager.captureFocusState('test:1', container);

            expect(focusState.scrollTop).toBe(100);
        });
    });

    describe('restoreFocus', () => {
        it('restores focus to element with data-lc-keep-focus', () => {
            const input1 = document.createElement('input');
            input1.id = 'input1';
            container.appendChild(input1);

            const input2 = document.createElement('input');
            input2.id = 'input2';
            input2.setAttribute('data-lc-keep-focus', '');
            container.appendChild(input2);

            // Capture focus on input1
            input1.focus();
            manager.captureFocusState('test:1', container);

            // Restore should prefer data-lc-keep-focus
            const restored = manager.restoreFocus('test:1', container);

            expect(restored).toBe(true);
            expect(document.activeElement).toBe(input2);
        });

        it('restores focus by ID', () => {
            const input = document.createElement('input');
            input.id = 'test-input';
            container.appendChild(input);

            input.focus();
            manager.captureFocusState('test:1', container);

            // Blur and restore
            input.blur();
            const restored = manager.restoreFocus('test:1', container);

            expect(restored).toBe(true);
            expect(document.activeElement).toBe(input);
        });

        it('restores selection for input elements', () => {
            const input = document.createElement('input');
            input.type = 'text';
            input.id = 'test-input';
            input.value = 'test value';
            container.appendChild(input);

            input.focus();
            input.selectionStart = 2;
            input.selectionEnd = 5;

            manager.captureFocusState('test:1', container);

            // Change selection
            input.selectionStart = 0;
            input.selectionEnd = 0;

            manager.restoreFocus('test:1', container);

            expect(input.selectionStart).toBe(2);
            expect(input.selectionEnd).toBe(5);
        });

        it('restores scroll position', () => {
            const textarea = document.createElement('textarea');
            textarea.id = 'test-textarea';
            textarea.style.height = '50px';
            textarea.style.overflow = 'scroll';
            textarea.value = 'Line 1\n'.repeat(20);
            container.appendChild(textarea);

            textarea.focus();
            textarea.scrollTop = 100;

            manager.captureFocusState('test:1', container);

            textarea.scrollTop = 0;

            manager.restoreFocus('test:1', container);

            expect(textarea.scrollTop).toBe(100);
        });

        it('returns false when no focus state captured', () => {
            const restored = manager.restoreFocus('test:1', container);

            expect(restored).toBe(false);
        });

        it('clears focus state after restore', () => {
            const input = document.createElement('input');
            input.id = 'test-input';
            container.appendChild(input);

            input.focus();
            manager.captureFocusState('test:1', container);
            manager.restoreFocus('test:1', container);

            // Second restore should fail (state cleared)
            const restored2 = manager.restoreFocus('test:1', container);
            expect(restored2).toBe(false);
        });
    });

    describe('announceUpdate', () => {
        beforeEach(() => {
            manager.initialize();
            jest.useFakeTimers();
        });

        afterEach(() => {
            jest.useRealTimers();
        });

        it('announces fragment update', () => {
            manager.announceUpdate('test:1', 'fragment', { fragmentName: 'header' });

            jest.advanceTimersByTime(500);

            expect(manager.liveRegion.textContent).toBe('Updated header');
        });

        it('announces full component update', () => {
            manager.announceUpdate('test:1', 'full');

            jest.advanceTimersByTime(500);

            expect(manager.liveRegion.textContent).toBe('Component updated');
        });

        it('announces action completion', () => {
            manager.announceUpdate('test:1', 'action', { actionMessage: 'Form submitted' });

            jest.advanceTimersByTime(500);

            expect(manager.liveRegion.textContent).toBe('Form submitted');
        });
    });

    describe('cleanup', () => {
        beforeEach(() => {
            manager.initialize();
        });

        it('removes component live region', () => {
            manager.createComponentLiveRegion('test:1', container);

            const region = manager.componentLiveRegions.get('test:1');
            expect(container.contains(region)).toBe(true);

            manager.cleanup('test:1');

            expect(container.contains(region)).toBe(false);
            expect(manager.componentLiveRegions.has('test:1')).toBe(false);
        });

        it('clears focus state', () => {
            const input = document.createElement('input');
            input.id = 'test-input';
            container.appendChild(input);

            input.focus();
            manager.captureFocusState('test:1', container);

            manager.cleanup('test:1');

            expect(manager.focusStates.has('test:1')).toBe(false);
        });
    });

    describe('getStats', () => {
        beforeEach(() => {
            manager.initialize();
        });

        it('returns accurate statistics', () => {
            manager.createComponentLiveRegion('test:1', container);
            manager.createComponentLiveRegion('test:2', container);

            const input = document.createElement('input');
            container.appendChild(input);
            input.focus();
            manager.captureFocusState('test:1', container);

            manager.announce('Test message');

            const stats = manager.getStats();

            expect(stats.has_global_live_region).toBe(true);
            expect(stats.component_live_regions).toBe(2);
            expect(stats.tracked_focus_states).toBe(1);
            expect(stats.pending_announcements).toBe(1);
        });
    });

    describe('shouldPreserveKeyboardNav', () => {
        it('returns true for interactive elements', () => {
            const button = document.createElement('button');
            expect(manager.shouldPreserveKeyboardNav(button)).toBe(true);

            const input = document.createElement('input');
            expect(manager.shouldPreserveKeyboardNav(input)).toBe(true);

            const link = document.createElement('a');
            expect(manager.shouldPreserveKeyboardNav(link)).toBe(true);
        });

        it('returns true for elements with tabindex', () => {
            const div = document.createElement('div');
            div.setAttribute('tabindex', '0');

            expect(manager.shouldPreserveKeyboardNav(div)).toBe(true);
        });

        it('returns true for elements with interactive roles', () => {
            const div = document.createElement('div');
            div.setAttribute('role', 'button');

            expect(manager.shouldPreserveKeyboardNav(div)).toBe(true);
        });

        it('returns false for non-interactive elements', () => {
            const div = document.createElement('div');
            expect(manager.shouldPreserveKeyboardNav(div)).toBe(false);

            const span = document.createElement('span');
            expect(manager.shouldPreserveKeyboardNav(span)).toBe(false);
        });
    });
});
