import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { getListTag, getOption, queryOption } from 'lib/test-utils/select';
import { ListViewItem } from '../../listView';
import { ListBox } from '../listBox';
import { ListBoxProps } from '../types';

describe('<ListBox>', () => {
    const option2 = { label: 'option2', value: '2' };
    const option1 = { label: 'option1', value: '1' };
    const options = [option1, option2];

    function renderComponent<M extends boolean = false>(
        props: Partial<ListBoxProps<ListViewItem, M>> = {}
    ) {
        return render(<ListBox {...props} />);
    }

    testComponent(ListBox, undefined, 'SuiteListBox');

    test('applies style', () => {
        const { getByTestId } = renderComponent({ style: { marginTop: 10 } });
        expect(getByTestId('SuiteListBox')).toHaveStyle({ marginTop: '10px' });
    });

    test('does not have "bordered" class by default', () => {
        const { getByTestId } = renderComponent();

        expect(getByTestId('SuiteListBox')).not.toHaveClass('bordered');
    });

    test('does not have "shadowed" class by default', () => {
        const { getByTestId } = renderComponent();

        expect(getByTestId('SuiteListBox')).not.toHaveClass('shadowed');
    });

    test('does not have "width" defined by default', () => {
        const { getByTestId } = renderComponent();

        expect(getByTestId('SuiteListBox')).toHaveStyle({ width: undefined });
    });

    test('renders list view', () => {
        const onChange = vi.fn();

        renderComponent({
            onChange,
            options,
        });

        const optionElement = getOption(option1);

        expect(optionElement).toBeInTheDocument();
        expect(optionElement).not.toHaveClass('selected');

        fireEvent.click(optionElement);

        expect(onChange).toHaveBeenCalledTimes(1);
        expect(onChange).toHaveBeenCalledWith(option1);
        expect(optionElement).toHaveClass('selected');
    });

    test('accepts initial value', () => {
        renderComponent({
            options,
            initialValue: option2,
        });

        expect(getOption(option2)).toHaveClass('selected');
    });

    describe('with "bordered" true', () => {
        test('has "bordered" class', () => {
            const { getByTestId } = renderComponent({ bordered: true });

            expect(getByTestId('SuiteListBox')).toHaveClass('bordered');
        });
    });

    describe('with "shadowed" true', () => {
        test('has "shadowed" class', () => {
            const { getByTestId } = renderComponent({ shadowed: true });

            expect(getByTestId('SuiteListBox')).toHaveClass('shadowed');
        });
    });

    describe('with "width"', () => {
        test('applies given width', () => {
            const width = '200px';
            const { getByTestId } = renderComponent({ width });

            expect(getByTestId('SuiteListBox')).toHaveStyle({ width });
        });
    });

    describe('with "quickSelections"', () => {
        test('shows quick selection section', async () => {
            const option = { label: 'quick option', value: 'test-quick' };

            const { getByText } = renderComponent({
                quickSelections: [option],
            });

            expect(getByText('Quick selections')).toBeInTheDocument();
            expect(getOption(option)).toBeInTheDocument();
        });
    });

    describe('with "multi" true', () => {
        test('renders multi value list view', () => {
            const onChange = vi.fn();

            renderComponent({
                onChange,
                options,
                multi: true,
            });

            const optionElement = getOption(option2);
            fireEvent.click(optionElement);

            expect(onChange).toHaveBeenCalledTimes(1);
            expect(onChange).toHaveBeenCalledWith([option2]);

            const tag = getListTag(option2);
            expect(tag).toBeInTheDocument();
            expect(queryOption(option2)).toBeNull();
        });

        test('accepts initial value', () => {
            renderComponent({
                options,
                initialValue: [option2],
                multi: true,
            });

            const tag = getListTag(option2);
            expect(tag).toBeInTheDocument();
            expect(queryOption(option2)).toBeNull();
        });

        describe('with "quickSelections"', () => {
            test('shows quick selection section', async () => {
                const label = 'quick-test';
                const option = { label: 'test', value: 'test' };

                const { getByText } = renderComponent({
                    quickSelections: [{ label, options: [option] }],
                    multi: true,
                });

                expect(getByText('Quick selections')).toBeInTheDocument();
                expect(getOption({ label })).toBeInTheDocument();
            });
        });
    });

    test('renders custom components', () => {
        const onChange = vi.fn();

        const labelText = 'LABEL VALUE';

        const { getAllByText } = renderComponent({
            onChange,
            options,
            components: {
                OptionLabel: () => <>{labelText}</>,
            },
        });

        expect(getAllByText(labelText)).toHaveLength(2);
    });
});
