import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { KviewCrudTable } from '@kview/ui-data/vue';
import { KviewDropdown } from '@kview/ui-core/vue';
import {
    column, columns, mountTable, pagination, rows,
} from './support/kviewCrudTableFixtures';

/**
 * Core `KviewCrudTable` behavior: rendering, sort, search, and pagination.
 * Column view-state, filters, row-keyboard-activation, selection/export, and
 * i18n each have their own spec file — see
 * KviewCrudTableColumns/Filters/RowInteractions/ExportAndLabels.spec.ts.
 */
afterEach(() => {
    document.body.innerHTML = '';
    vi.restoreAllMocks();
    vi.useRealTimers();
});

describe('KviewCrudTable', () => {
    it('renders only visible server-normalized columns with typed cells', () => {
        const wrapper = mountTable();

        const headers = wrapper.findAll('th');
        expect(headers).toHaveLength(2);
        expect(headers[0].text()).toContain('GP');
        expect(headers[1].classes()).toContain('is-numeric');
        expect(wrapper.text()).not.toContain('hidden-value');
        expect(wrapper.findAll('tbody tr')).toHaveLength(2);
    });

    it('honors an explicit centered alignment for discrete numeric periods', () => {
        const wrapper = mountTable({
            columns: [column({
                key: 'vintage',
                label: 'Vintage',
                type: 'year',
                alignment: 'center',
                numeric: true,
                nowrap: true,
                width: '150px',
                minWidth: '144px',
            })],
            rows: [{
                id: 1,
                cells: {
                    vintage: { type: 'year', value: 2014, formatted: '2014' },
                },
            }],
            pagination: pagination({ total: 1, from: 1, to: 1, lastPage: 1, links: [] }),
        });

        expect(wrapper.get('th').classes()).toContain('is-align-center');
        expect(wrapper.get('tbody td').classes()).toContain('is-align-center');
        expect(wrapper.get('tbody td').classes()).toContain('is-numeric');
        expect(wrapper.get('tbody td').text()).toContain('2014');
    });

    it('exposes aria-sort from the server sort state and cycles asc, desc, none', async () => {
        const wrapper = mountTable({ sort: { key: 'name', direction: 'asc' } });

        expect(wrapper.get('th').attributes('aria-sort')).toBe('ascending');

        await wrapper.get('.erp-table-sort').trigger('click');
        expect(wrapper.emitted('update:sort')?.[0]).toEqual([{ key: 'name', direction: 'desc' }]);

        await wrapper.setProps({ sort: { key: 'name', direction: 'desc' } });
        expect(wrapper.get('th').attributes('aria-sort')).toBe('descending');

        await wrapper.get('.erp-table-sort').trigger('click');
        expect(wrapper.emitted('update:sort')?.[1]).toEqual([{ key: null, direction: null }]);
    });

    it('debounces search input and reports the trimmed intent once', async () => {
        vi.useFakeTimers();
        const wrapper = mountTable();
        const input = wrapper.get('input[type="search"]');

        await input.setValue('alp');
        await input.trigger('input');
        vi.advanceTimersByTime(299);
        expect(wrapper.emitted('update:search')).toBeUndefined();

        vi.advanceTimersByTime(1);
        await nextTick();
        expect(wrapper.emitted('update:search')?.[0]).toEqual(['alp']);
    });

    it('reports page-size and pagination intents without navigating', async () => {
        const wrapper = mountTable();

        const pageSizeDropdown = wrapper.find('.erp-page-size-form').findComponent(KviewDropdown);
        expect(pageSizeDropdown.props('showSelectedIndicator')).toBe(false);

        const nav = wrapper.get('nav.pagination-nav');
        const secondPage = nav.findAll('a').find((link) => link.text() === '2');
        expect(secondPage).toBeDefined();
        await secondPage!.trigger('click');

        expect(wrapper.emitted('paginate')?.[0]).toEqual(['/specimen?page=2']);

        const active = nav.get('[aria-current="page"]');
        expect(active.text()).toBe('1');

        const boundaries = nav.findAll('.erp-pagination-boundary');
        expect(boundaries).toHaveLength(2);
        expect(boundaries.every((boundary) => boundary.find('.kv-icon').exists())).toBe(true);
        expect(boundaries[0].get('.ui-visually-hidden').text()).toBe('Previous page');
        expect(boundaries[1].get('.ui-visually-hidden').text()).toBe('Next page');
        expect(nav.text()).not.toContain('« Previous');
        expect(nav.text()).not.toContain('Next »');
    });

    it('centers the empty state and footnote when the server collection is empty', () => {
        const wrapper = mountTable({
            rows: [],
            emptyTitle: 'No GPs found',
            pagination: pagination({
                total: 0, from: null, to: null, lastPage: 1, links: [],
            }),
        });

        expect(wrapper.find('.erp-table-empty-state').exists()).toBe(true);
        expect(wrapper.text()).toContain('No GPs found');
        expect(wrapper.get('.erp-table-footnote').text()).toBe('Showing 0 of 0');
        expect(wrapper.find('nav.pagination-nav').exists()).toBe(false);
    });

    it('forwards public header actions to the shared resource header', () => {
        const wrapper = mount(KviewCrudTable, {
            props: {
                id: 'specimen-table',
                title: 'General Partners',
                columns,
                rows,
                sort: { key: null, direction: null },
                search: '',
                pagination: pagination(),
            },
            slots: {
                'header-actions': '<button type="button" class="test-header-action">Synchronize</button>',
            },
        });

        const action = wrapper.get('.ui-resource-header__actions .test-header-action');
        expect(action.text()).toBe('Synchronize');
        expect(wrapper.findAll('.test-header-action')).toHaveLength(1);
    });
});
