import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { SearchInput, Props } from '../searchInput';

vi.mock('lodash.debounce', () => ({ default: vi.fn((fn) => fn) }));

describe('<SearchInput>', () => {
    const onChangeSpy = vi.fn();

    const defaultProps: Props = {
        onChange: onChangeSpy,
    };

    const renderComponent = (customProps?: Partial<Props>) => {
        const props = { ...defaultProps, ...customProps };

        return render(<SearchInput {...props} />);
    };

    testComponent(SearchInput);

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

    beforeAll(() =>
        Object.defineProperty(Element.prototype, 'clientWidth', {
            configurable: true,
            value: 999,
        })
    );
    afterAll(() =>
        Object.defineProperty(Element.prototype, 'clientWidth', {
            configurable: true,
            value: 0,
        })
    );

    test('renders small and without value as default', () => {
        const { getByTestId } = renderComponent();

        const wrapper = getByTestId('SearchInput');
        const input = getByTestId('SearchInput-input');

        expect(wrapper).not.toHaveClass('fixed-size');
        expect(input).toHaveAttribute('value', undefined);
    });

    test('renders default input value', () => {
        const { getByTestId } = renderComponent({
            defaultValue: 'Bad Bunny',
        });

        const wrapper = getByTestId('SearchInput');
        const input = getByTestId('SearchInput-input');

        expect(input).toHaveAttribute('value', 'Bad Bunny');
        expect(wrapper).toHaveClass('applied');
    });

    test('onChange is called on typing', () => {
        const { getByTestId } = renderComponent({ defaultSize: 'medium' });

        const wrapper = getByTestId('SearchInput');
        expect(wrapper).toHaveClass('fixed-size');

        const input = getByTestId('SearchInput-input');

        fireEvent.change(input, { target: { value: 'Bad Bunny' } });

        expect(onChangeSpy).toHaveBeenCalledWith('Bad Bunny');
    });

    test('placeholder is set correctly', () => {
        const { getByTestId } = renderComponent({
            placeholder: 'customPlaceholder',
        });

        const input = getByTestId('SearchInput-input');
        expect(input).toHaveAttribute('placeholder', 'customPlaceholder');
    });

    test('renders with passed value', () => {
        const { getByTestId } = renderComponent({ value: 'Bad Bunny', defaultSize: 'medium' });

        const input = getByTestId('SearchInput-input');

        expect(input).toHaveAttribute('value', 'Bad Bunny');
    });

    describe('expanded', () => {
        test('pins width to placeholder width', async () => {
            const { getByTestId } = renderComponent({
                expanded: true,
                maxWidth: 2000,
            });

            const wrapper = getByTestId('SearchInput');
            expect(wrapper).toHaveClass('fixed-size');
            await waitFor(() =>
                expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '1045px' })
            );
        });
    });

    describe('placeholderWidth', () => {
        test('is set by placeholder width when not defined', async () => {
            const { getByTestId } = renderComponent({
                maxWidth: 2000,
            });

            const wrapper = getByTestId('SearchInput');
            await waitFor(() =>
                expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '1045px' })
            );
        });

        test('is overriden by placeholderWidth prop', async () => {
            const { getByTestId } = renderComponent({ placeholderWidth: 123 });

            const wrapper = getByTestId('SearchInput');
            expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '123px' });
        });
    });

    describe('maxWidth', () => {
        test('defaults to 200px', async () => {
            const { getByTestId } = renderComponent({});

            const wrapper = getByTestId('SearchInput');

            const input = getByTestId('SearchInput-input');
            fireEvent.change(input, { target: { value: 'Bad Bunny' } });

            await waitFor(() => expect(wrapper).toHaveClass('applied'));
            expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '200px' });
        });

        test('can be overriden with maxWidth prop', async () => {
            const { getByTestId } = renderComponent({ maxWidth: 256 });

            const wrapper = getByTestId('SearchInput');

            const input = getByTestId('SearchInput-input');
            fireEvent.change(input, { target: { value: 'Bad Bunny' } });

            await waitFor(() => expect(wrapper).toHaveClass('applied'));
            expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '256px' });
        });
    });

    describe('clear button', () => {
        test('clears field when clicked and calls onChange', () => {
            const onChange = vitest.fn();
            const { getByTestId, getByRole } = renderComponent({
                defaultValue: 'test-123',
                onChange,
            });

            const input = getByTestId('SearchInput-input');
            expect(input).toHaveValue('test-123');

            const wrapper = getByTestId('SearchInput');
            expect(wrapper).toHaveClass('applied');

            const clear = getByRole('button');

            fireEvent.click(clear);
            expect(onChange).toHaveBeenCalledWith('');
            expect(input).toHaveValue('');
            expect(wrapper).not.toHaveClass('applied');
        });
    });

    describe('width', () => {
        test('overrides measured placeholder size', async () => {
            const { getByTestId } = renderComponent({ width: 999 });

            const wrapper = getByTestId('SearchInput');
            expect(wrapper).toHaveStyle({ '--segmented-input-active-width': '999px' });
        });
    });
});
