import { mount } from '@vue/test-utils';
import {
    defineComponent,
    h,
    markRaw,
    nextTick,
} from 'vue';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
    KviewDetailTable,
    KviewRowActions,
    KviewTableCell,
} from '@kview/ui-data/vue';
import { KviewUiProvider } from '@kview/ui-core/vue';
import type {
    KviewDetailTableColumn,
    KviewDetailTableRow,
} from '@kview/ui-data/vue';

const flush = async (): Promise<void> => {
    await nextTick();
    await new Promise((resolve) => setTimeout(resolve, 0));
    await nextTick();
};

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

const columns: KviewDetailTableColumn[] = [
    { key: 'fund', label: 'Fund', type: 'entity' },
    { key: 'amount', label: 'Amount', type: 'amount' },
    { key: 'status', label: 'Status', type: 'status' },
];

const rows: KviewDetailTableRow[] = [{
    id: 'investment-1',
    href: '/investments/investment-1',
    cells: {
        fund: { value: 'Alpha Growth', url: '/funds/fund-1' },
        amount: { value: 1250000, formatted: '€1.25m' },
        status: { value: 'active', label: 'Active', variant: 'success' },
    },
    actions: [{
        id: 'preview',
        type: 'preview',
        label: 'Preview investment',
        preview: {
            title: 'Alpha Growth investment',
            subtitle: 'Demo Atlas Pension Trust',
            badges: [{ label: 'Rejected', variant: 'danger' }],
            fields: [{ label: 'Currency', value: 'EUR' }],
            url: '/investments/investment-1',
        },
    }],
}];

describe('@kview/ui-data/vue', () => {
    it('renders server-formatted typed values without duplicating finance formatting', () => {
        const wrapper = mount(KviewTableCell, {
            props: {
                column: { key: 'amount', type: 'amount' },
                cell: { value: 1250000, formatted: 'EUR 1.25m' },
            },
        });

        expect(wrapper.text()).toBe('EUR 1.25m');
        expect(wrapper.get('.erp-cell-number').classes()).toContain('erp-cell-number');
    });

    it('normalizes serialized boolean values without relying on JavaScript truthiness', async () => {
        const wrapper = mount(KviewTableCell, {
            props: {
                column: { key: 'active', type: 'boolean' },
                cell: { value: 'false' },
            },
        });

        expect(wrapper.get('.erp-boolean').attributes('aria-label')).toBe('No');
        await wrapper.setProps({ cell: { value: '1' } });
        expect(wrapper.get('.erp-boolean').attributes('aria-label')).toBe('Yes');
    });

    it('keeps row activation keyboard accessible and ignores interactive descendants', async () => {
        const onRowActivate = vi.fn();
        const wrapper = mount(KviewDetailTable, {
            props: {
                id: 'investments-table',
                title: 'Investments',
                columns,
                rows,
                onRowActivate,
            },
            attachTo: document.body,
        });
        const row = wrapper.get<HTMLTableRowElement>('tbody tr');

        expect(wrapper.get('[role="region"]').attributes('aria-label')).toBe('Investments');
        expect(row.attributes('tabindex')).toBe('0');
        expect(wrapper.text()).toContain('€1.25m');

        await row.trigger('click');
        expect(onRowActivate).toHaveBeenCalledTimes(1);

        await row.trigger('keydown', { key: 'Enter' });
        await row.trigger('keydown', { key: ' ' });
        expect(onRowActivate).toHaveBeenCalledTimes(3);

        const cellLink = wrapper.get('a.erp-cell-link');
        cellLink.element.addEventListener('click', (event) => event.preventDefault());
        await cellLink.trigger('click');
        expect(onRowActivate).toHaveBeenCalledTimes(3);
        wrapper.unmount();
    });

    it('never swallows a keystroke aimed at a real control nested in the row', async () => {
        const onRowActivate = vi.fn();
        const wrapper = mount(KviewDetailTable, {
            props: { id: 'investments-table', title: 'Investments', columns, rows, onRowActivate },
            attachTo: document.body,
        });

        // A keydown that bubbles from a nested link must not be treated as
        // row activation, and the row handler must not preventDefault() it —
        // otherwise the browser's native Enter-follows-link behavior breaks.
        const cellLink = wrapper.get('a.erp-cell-link').element;
        const enterOnLink = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true });
        cellLink.dispatchEvent(enterOnLink);
        expect(enterOnLink.defaultPrevented).toBe(false);
        expect(onRowActivate).not.toHaveBeenCalled();

        // Same guarantee for Space on a nested action button (must still be
        // able to activate/toggle it natively).
        const actionButton = wrapper.get('.erp-detail-table__actions-cell button').element;
        const spaceOnButton = new KeyboardEvent('keydown', { key: ' ', bubbles: true, cancelable: true });
        actionButton.dispatchEvent(spaceOnButton);
        expect(spaceOnButton.defaultPrevented).toBe(false);
        expect(onRowActivate).not.toHaveBeenCalled();

        wrapper.unmount();
    });

    it('uses the host link seam for an Inertia row instead of forcing a document exit', async () => {
        const navigate = vi.fn();
        const LinkStub = defineComponent({
            props: {
                href: { type: String, required: true },
                inertia: { type: Boolean, default: false },
            },
            setup: (linkProps, { slots }) => () => h('a', {
                href: linkProps.href,
                'data-inertia': String(linkProps.inertia),
                onClick: (event: MouseEvent) => {
                    event.preventDefault();
                    navigate(linkProps.href, linkProps.inertia);
                },
            }, slots.default?.()),
        });
        const wrapper = mount({
            components: { KviewDetailTable, KviewUiProvider },
            data: () => ({ link: markRaw(LinkStub) }),
            template: `
                <KviewUiProvider :link-component="link">
                    <KviewDetailTable
                        id="inertia-table"
                        :columns="[{ key: 'name', label: 'Name' }]"
                        :rows="[{
                            id: 'row-1',
                            href: '/investments/row-1',
                            inertia: true,
                            cells: { name: 'Investment one' },
                        }]"
                    />
                </KviewUiProvider>
            `,
        }, { attachTo: document.body });

        await wrapper.get('tbody tr').trigger('click');

        expect(navigate).toHaveBeenCalledWith('/investments/row-1', true);
        expect(wrapper.get('.erp-detail-table__row-link').attributes('data-inertia')).toBe('true');
        wrapper.unmount();
    });

    it('never turns a delete endpoint into a GET link', async () => {
        const wrapper = mount(KviewRowActions, {
            props: {
                inline: true,
                actions: [{
                    type: 'delete',
                    label: 'Delete investment',
                    url: '/investments/investment-1',
                }],
            },
        });
        const control = wrapper.get('button[aria-label="Delete investment"]');

        expect(wrapper.find('a[href="/investments/investment-1"]').exists()).toBe(false);
        await control.trigger('click');
        expect(wrapper.emitted('action')).toHaveLength(1);
    });

    it('opens a primitive-backed preview drawer and restores the row command focus', async () => {
        const wrapper = mount(KviewDetailTable, {
            props: {
                id: 'preview-table',
                columns,
                rows,
            },
            attachTo: document.body,
        });
        const trigger = wrapper.get<HTMLButtonElement>('button[aria-label="Preview investment"]');

        trigger.element.focus();
        await trigger.trigger('click');
        await flush();

        const drawer = document.body.querySelector<HTMLElement>('#preview-table_preview_drawer');

        expect(drawer).not.toBeNull();
        expect(drawer?.getAttribute('role')).toBe('dialog');
        expect(drawer?.textContent).toContain('Alpha Growth investment');
        expect(drawer?.textContent).toContain('Currency');
        expect(drawer?.querySelector('.erp-preview-badge--danger')?.textContent).toContain('Rejected');
        expect(document.activeElement?.getAttribute('aria-label')).toBe('Close');

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

        expect(document.body.querySelector('#preview-table_preview_drawer')).toBeNull();
        expect(document.activeElement).toBe(trigger.element);
        wrapper.unmount();
    });

    it('renders a centered shared empty state inside the horizontal table region', () => {
        const wrapper = mount(KviewDetailTable, {
            props: {
                id: 'empty-table',
                tableLabel: 'Capital accounts',
                columns,
                rows: [],
                emptyTitle: 'No capital accounts found',
                emptyText: 'Create a capital account to get started.',
            },
        });

        expect(wrapper.get('.erp-detail-table__empty-row td').attributes('colspan')).toBe('3');
        expect(wrapper.get('.ui-empty-state').text()).toContain('No capital accounts found');
        expect(wrapper.get('[role="region"]').attributes('tabindex')).toBe('0');
    });
});
