import React from 'react';
import { fireEvent, render as renderDom, screen } from '@testing-library/react';
import { GlyphIcon } from '@theorchard/suite-icons';
import { testComponent } from 'lib/test-utils/common';
import { SearchDropdown } from '../searchDropdown';
import { SearchDropdownProps } from '../types';

describe('<SearchDropdown>', () => {
    const option1 = { label: 'Option1', value: '1', data: { id: 1 } };
    const option2 = { label: 'Option2', value: '2', data: { id: 2 } };
    const options = [option1, option2];

    const render = (props?: Partial<SearchDropdownProps>) =>
        renderDom(
            <SearchDropdown
                debounceTime={0}
                onLoadOptions={async () => await Promise.resolve(options)}
                {...props}
            />
        );

    const findFirstOption = async () =>
        await screen.findByText(option1.label, {
            selector: '.Dropdown-option-label',
        });

    const search = async () => {
        const input = document.getElementsByTagName('input').item(0);

        if (input) fireEvent.change(input, { target: { value: 'test string' } });

        await findFirstOption();
    };

    testComponent(SearchDropdown, {
        debounceTime: 0,
        onLoadOptions: async () => await Promise.resolve([]),
    });

    test('renders dropdown with items', async () => {
        const { getByText } = render();

        await search();

        options.forEach((option) => expect(getByText(option.label ?? '')).toBeVisible());
    });

    test('renders placeholder', () => {
        const placeholder = 'PLACEHOLDER TEXT';
        const { getByText } = render({ placeholder });

        expect(getByText(placeholder)).toBeVisible();
    });

    test('renders "alignRight" classname', () => {
        const { container } = render({ alignRight: true });

        expect(container.getElementsByClassName('alignRight').item(0)).toBeVisible();
    });

    test('renders "icons"', async () => {
        const { container } = render({
            onLoadOptions: async () =>
                await Promise.resolve([
                    { ...option1, icon: <GlyphIcon name="info" size={16} /> },
                    {
                        ...option2,
                        icon: <GlyphIcon name="warning" size={16} />,
                    },
                ]),
        });

        await search();

        expect(container.getElementsByClassName('InfoGlyphIcon').item(0)).toBeVisible();
        expect(container.getElementsByClassName('WarningGlyphIcon').item(0)).toBeVisible();
    });

    test('renders "separateFirstOption" classname', () => {
        const { container } = render({
            separateFirstOption: true,
        });
        expect(container.getElementsByClassName('separateFirstOption').item(0)).toBeVisible();
    });

    test('renders dropdown with value set', () => {
        const { getByText } = render({ value: option1 });

        expect(getByText(option1.label)).toBeVisible();
    });

    test('invokes "onChange" callback when item selected', async () => {
        const onChange = vi.fn();

        render({
            onChange,
        });

        await search();
        const firstOption = await findFirstOption();

        fireEvent.click(firstOption);

        expect(onChange).toHaveBeenCalledWith(option1, {
            action: 'select-option',
        });
    });

    test('cannot select item if item.isDisabled is true', async () => {
        const onChange = vi.fn();
        const disabledOption = {
            label: 'Option1',
            value: '1',
            disabled: true,
        };

        render({
            onLoadOptions: async () => await Promise.resolve([disabledOption]),
            onChange,
        });

        await search();
        const firstOption = await findFirstOption();

        fireEvent.click(firstOption);

        expect(onChange).not.toHaveBeenCalled();
    });

    test('invokes "onValueChanged" when item is selected', async () => {
        const onValueChanged = vi.fn();
        const name = 'test-search';
        const { getByText } = render({ name, onValueChanged });

        await search();
        const firstOption = await findFirstOption();

        fireEvent.click(firstOption);

        expect(onValueChanged).toHaveBeenCalledWith(name, option1.value, option1);

        expect(getByText(option1.label)).toBeVisible();
    });

    test('does not render clear icon when isClearable is false', async () => {
        const { container } = render({ isClearable: false });

        expect(container.getElementsByClassName('CloseGlyphIcon').length).toEqual(0);

        await search();
        const firstOption = await findFirstOption();

        fireEvent.click(firstOption);

        expect(container.getElementsByClassName('CloseGlyphIcon').length).toEqual(0);
    });

    describe('when isClearable is true', () => {
        test('should render clear icon when there is selected option', async () => {
            const { container } = render();

            expect(container.getElementsByClassName('Select__clear-indicator').length).toEqual(0);

            await search();
            const firstOption = await findFirstOption();

            fireEvent.click(firstOption);

            expect(
                container.querySelector('.Select__single-value .Dropdown-option-label')?.textContent
            ).toEqual(option1.label);
            expect(container.getElementsByClassName('Select__clear-indicator').length).toEqual(1);
        });

        test('should de-select the option on clear icon click', async () => {
            const { container } = render();
            await search();
            const firstOption = await findFirstOption();
            fireEvent.click(firstOption);

            const closeIcon = container.getElementsByClassName('Select__clear-indicator').item(0);
            expect(closeIcon).toBeTruthy();
            fireEvent.mouseDown(closeIcon!);

            expect(
                container.querySelector('.Select__single-value .Dropdown-option-label')
            ).toBeFalsy();
            expect(container.getElementsByClassName('Select__clear-indicator').length).toEqual(0);
        });
    });
});
