import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { DATE_CLASS } from '../constants';
import { DatePickerBase } from '../datePickerBase';
import { t } from '../i18n';
import { DatePickerBaseProps } from '../types';

describe('<DatePickerBase>', () => {
    const onChange = vi.fn();
    const defaultProps: DatePickerBaseProps = {
        onChange,
        options: [
            {
                label: '7 Days',
                value: '7days',
                start: '2022-01-01',
                end: '2022-01-07',
            },
        ],
        customDateRange: {
            start: '2021-01-02',
            end: '2021-08-31',
        },
        isRange: true,
    };

    afterEach(() => vi.resetAllMocks());

    const renderMocked = (props?: Partial<DatePickerBaseProps>) =>
        render(<DatePickerBase {...defaultProps} {...props} />);

    const triggerDropdown = () => {
        const dropdownTrigger = screen.getByTestId('SuiteDatePickerIndicatorCalendar');
        fireEvent.click(dropdownTrigger);
    };

    testComponent(DatePickerBase, undefined, 'SuiteDatePicker');

    test('renders default component', async () => {
        const { findByText } = renderMocked();

        triggerDropdown();
        expect(await findByText('7 Days')).toBeInTheDocument();
    });

    test('clicking calendar button shows dateRangePicker', async () => {
        const { findByTestId } = renderMocked();

        triggerDropdown();
        expect(await findByTestId(DATE_CLASS)).toBeVisible();
    });

    test('calendar button is not visible when `hideCalendarButton` is true', () => {
        const { queryAllByRole } = renderMocked({
            customDateRange: {
                start: '2021-01-02',
                end: '2021-08-31',
            },
            hideCalendarButton: true,
        });

        expect(queryAllByRole('button').length).toBe(0);
    });

    test('calendar button is not visible when customDateRange is undefined', () => {
        const { queryByTestId } = renderMocked({ customDateRange: undefined });
        expect(queryByTestId('SuiteDatePickerIndicatorsCalendar')).toBeNull();
    });

    test('clicking dropdown shows dateRangePicker if no options are passed', async () => {
        const { getByText, findByTestId } = renderMocked({ options: [] });

        const dropdown = getByText(t('select_period'));
        expect(dropdown).toBeVisible();
        fireEvent.click(dropdown);
        expect(await findByTestId(DATE_CLASS)).toBeVisible();
    });

    test('clicking option fires onChange', () => {
        const { getByText } = renderMocked({ requireApply: false });

        triggerDropdown();
        fireEvent.click(getByText('7 Days'));
        expect(onChange).toHaveBeenCalledWith({
            label: '7 Days',
            start: '2022-01-01',
            end: '2022-01-07',
            value: '7days',
        });
    });

    test('selecting custom dates and clicking apply fires onChange', async () => {
        const { getByText, getByTestId } = renderMocked({
            customDateRange: {
                start: '2021-01-02',
                end: '2021-01-28',
            },
        });

        triggerDropdown();
        expect(getByTestId(DATE_CLASS)).toBeInTheDocument();

        fireEvent.mouseDown(getByText('16'));
        fireEvent.mouseDown(getByText('28'));
        fireEvent.click(getByText(t('apply')));

        expect(onChange).toHaveBeenCalledWith({
            start: '2021-01-16',
            end: '2021-01-28',
            value: 'CUSTOM',
        });
    });

    test('setting selectedValue to option value selects that option', () => {
        const { getByText } = renderMocked({
            selectedValue: {
                value: '7days',
            },
        });

        expect(getByText(/Jan 1.*?Jan 7, 2022/)).toBeVisible();
    });

    test('setting selectedValue to date range selects custom range', () => {
        const { getByText } = renderMocked({
            selectedValue: {
                start: '2021-01-16',
                end: '2021-01-28',
            },
        });

        expect(getByText(/Jan 16.*?Jan 28, 2021/)).toBeVisible();
    });

    test('with `showOptionLabel` enabled, labels are show instead of period duration', () => {
        const { getByText } = renderMocked({
            selectedValue: {
                value: '7days',
            },
            showOptionLabel: true,
        });

        expect(getByText('7 Days')).toBeVisible();
    });

    test('clicking calendar closes custom menu', () => {
        const { getByText, queryByText, queryByTestId, getAllByRole, getByTestId } = renderMocked();

        const dropdown = getByText(t('select_period'));
        expect(dropdown).toBeVisible();

        triggerDropdown();
        expect(getByTestId(DATE_CLASS)).toBeInTheDocument();

        fireEvent.click(getAllByRole('button')[0]);
        expect(queryByTestId(DATE_CLASS)).toBeNull();
        expect(queryByText('7 Days')).toBeNull();
    });

    test('clicking select box closes custom menu', () => {
        const { getByText, queryByTestId, getByTestId } = renderMocked();

        const dropdown = getByText(t('select_period')).closest('.SuiteSelect-input')!;
        expect(dropdown).toBeVisible();

        triggerDropdown();
        expect(getByTestId(DATE_CLASS)).toBeInTheDocument();

        fireEvent.click(dropdown);
        expect(queryByTestId(DATE_CLASS)).toBeNull();
    });

    test('controlId is passed through to input element', async () => {
        const controlId = 'TEST_CONTROL_ID';
        const { getByRole } = renderMocked({ controlId });

        const input = getByRole('combobox', { hidden: true });
        expect(input).toHaveAttribute('id', controlId);
    });

    test('when clicking calendar icon the first input field is focused', async () => {
        const { findByTestId, getAllByRole } = renderMocked();

        triggerDropdown();

        expect(await findByTestId(DATE_CLASS)).toBeVisible();
        expect(getAllByRole('textbox')?.[0]).toHaveFocus();
    });

    test('with `mixed` option selected, `Mixed` is shown instead of date range', () => {
        const { getByText, queryByText } = renderMocked({
            options: [
                {
                    label: 'A mixed period',
                    value: 'mixedOption',
                    start: '2022-01-01',
                    end: '2022-01-07',
                    mixed: true,
                },
            ],
            selectedValue: {
                value: 'mixedOption',
            },
            showOptionLabel: true,
        });

        expect(getByText('A mixed period')).toBeVisible();
        expect(getByText(t('mixed'))).toBeVisible();
        expect(queryByText(/Jan 1.*?Jan 7, 2022/)).toBeNull();
    });

    test('with a mixed option selected dateSelect opens in mixed mode', async () => {
        const { findByTestId, getAllByRole, getAllByPlaceholderText } = renderMocked({
            options: [
                {
                    label: 'A mixed period',
                    value: 'mixedOption',
                    start: '2022-01-01',
                    end: '2022-01-07',
                    mixed: true,
                },
            ],
            selectedValue: {
                value: 'mixedOption',
            },
        });

        triggerDropdown();

        expect(await findByTestId(DATE_CLASS)).toBeInTheDocument();
        expect(getAllByRole('textbox')?.[0]).not.toHaveFocus();
        expect(getAllByPlaceholderText(t('mixed'))).toHaveLength(2);
    });

    test('with mixed option selected, no calendar dates are selected', async () => {
        const { container, findByTestId } = renderMocked({
            options: [
                {
                    label: 'A mixed period',
                    value: 'mixedOption',
                    start: '2022-01-01',
                    end: '2022-01-07',
                    mixed: true,
                },
            ],
            selectedValue: {
                value: 'mixedOption',
            },
        });

        triggerDropdown();

        expect(await findByTestId(DATE_CLASS)).toBeVisible();
        expect(container.getElementsByClassName('DateSelect-calendar-date-selected')).toHaveLength(
            0
        );
    });

    test('with compact enabled, renders in compact mode', () => {
        const placeholder = 'SOMETHING';
        const { getByText, getByTestId } = renderMocked({
            options: [
                {
                    label: 'A mixed period',
                    value: 'mixedOption',
                    start: '2022-01-01',
                    end: '2022-01-07',
                    mixed: true,
                },
            ],
            selectedValue: {
                value: 'mixedOption',
            },
            compact: true,
            isRange: true,
            placeholder,
        });

        expect(getByTestId('SuiteSelectInput')).toHaveClass('SuiteSelect-input-compact');
        expect(getByText(placeholder)).toBeInTheDocument();
        expect(getByText('A mixed period')).toBeInTheDocument();
    });

    describe('menuHeight', () => {
        test('is set on ListView component', async () => {
            const { findByTestId } = renderMocked({ menuHeight: 200 });

            triggerDropdown();
            const menu = await findByTestId('SuiteListView');
            expect(menu).toBeInTheDocument();
            expect(menu).toHaveStyle({ height: '200px' });
        });

        test('is passed to DateSelect', () => {
            const { container } = renderMocked({ menuHeight: 456 });

            triggerDropdown();

            const list = container.querySelector('.DateSelect-container > div');
            expect(list).toHaveStyle({ height: `319px` });
        });

        describe('listHeight', () => {
            test('is calculated from 580px default menuHeight for date ranges', () => {
                const { container } = renderMocked({ isRange: true, requireApply: false });

                triggerDropdown();

                const list = container.querySelector('.DateSelect-container > div');
                expect(list).toHaveStyle({ height: `484px` });
            });

            test('is calculated from 380px default menuHeight for date ranges', () => {
                const { container } = renderMocked({ isRange: false });

                triggerDropdown();

                const list = container.querySelector('.DateSelect-container > div');
                expect(list).toHaveStyle({ height: `284px` });
            });

            test('takes requireApply into account', () => {
                const { container } = renderMocked({ isRange: true, requireApply: true });

                triggerDropdown();

                const list = container.querySelector('.DateSelect-container > div');
                expect(list).toHaveStyle({ height: `443px` });
            });
        });
    });
});
