import React, { useEffect, useState } from 'react';
import { fireEvent, render, act, renderHook } from '@testing-library/react';
import { ToastProvider, useToast } from '../toastProvider';

const TestComponent = () => {
    const addToast = useToast();
    const [trigger, setTrigger] = useState(0);

    const onClick = () => {
        addToast(<div>Toast Contents</div>);
    };

    const toastWidthClassName = () => {
        addToast('This is a text with className', { classNameProp: 'testClassName' });
    };

    const addPersistentToast = () => {
        addToast('Persistent message', { variant: 'error', persistent: true });
    };

    useEffect(() => {
        if (trigger > 0) addToast(<div>Toast Effect Contents</div>);
    }, [addToast, trigger]);

    return (
        <>
            <div
                role="button"
                data-testid="TestButton"
                tabIndex={0}
                className="Button"
                onClick={onClick}
                onKeyDown={onClick}
            >
                Toast Context Example
            </div>
            <div
                role="button"
                data-testid="TestEffectButton"
                className="Button"
                onClick={() => {
                    setTrigger(trigger + 1);
                }}
            >
                Toast Effect Example
            </div>
            <div
                role="button"
                data-testid="TestOptionalProp"
                className="Button"
                onClick={toastWidthClassName}
            ></div>
            <div
                role="button"
                data-testid="PersistentButton"
                className="Button"
                onClick={addPersistentToast}
            ></div>
        </>
    );
};

describe('<ToastProvider>', () => {
    beforeEach(() => {
        vi.useFakeTimers();
    });

    const renderComponent = () =>
        render(
            <ToastProvider>
                <TestComponent />
            </ToastProvider>
        );

    describe('without messages', () => {
        test('does not render toast container', () => {
            const { queryByTestId } = renderComponent();

            expect(queryByTestId('ToastContainer')).toBeNull();
        });

        test('renders children', () => {
            const { getByText } = renderComponent();

            expect(getByText('Toast Context Example')).toBeVisible();
        });
    });

    describe('when toast is added', () => {
        test('renders toasts and hides them after timeout', async () => {
            const { getByTestId, queryByText, getByText } = renderComponent();
            const btn = getByTestId('TestButton');
            fireEvent.click(btn);

            expect(getByText('Toast Contents')).toBeVisible();

            act(() => vitest.runAllTimers());

            expect(queryByText('Toast Contents')).toBeNull();
        });

        test('renders and closes toast on close button click', async () => {
            const { getByTestId, queryByText, getByText } = renderComponent();
            const btn = getByTestId('TestButton');
            fireEvent.click(btn);

            expect(getByText('Toast Contents')).toBeVisible();

            const closeBtn = getByTestId('CloseGlyphIcon');
            fireEvent.click(closeBtn);

            act(() => vi.runAllTimers());

            expect(queryByText('Toast Contents')).toBeNull();
        });
    });

    describe('when used in a "useEffect" hook', () => {
        test('renders toasts and hides them after timeout', async () => {
            const { getByTestId, getByText, queryByText } = renderComponent();
            const btn = getByTestId('TestEffectButton');
            fireEvent.click(btn);

            expect(getByText('Toast Effect Contents')).toBeVisible();

            act(() => vi.runAllTimers());

            expect(queryByText('Toast Effect Contents')).toBeNull();
        });
    });

    describe('when toast have optional classname', () => {
        test('toast have optional classname', async () => {
            const { getByTestId } = renderComponent();
            const btn = getByTestId('TestOptionalProp');
            fireEvent.click(btn);

            expect(getByTestId('Toast')).toHaveClass('testClassName');
        });
    });

    describe('when persistent prop is true', () => {
        test('remains visible after more than the default 8 seconds', async () => {
            const { getByTestId, getByText } = renderComponent();
            const btn = getByTestId('PersistentButton');
            fireEvent.click(btn);

            expect(getByText('Persistent message')).toBeVisible();

            act(() => {
                vi.advanceTimersByTime(20000);
            });

            expect(getByText('Persistent message')).toBeVisible();
        });
    });
});

describe('useToast', () => {
    test('throws error if no provider', () => {
        const spy = vitest.spyOn(console, 'error').mockImplementation(() => {});
        expect(() => {
            renderHook(() => useToast());
        }).toThrow('No parent "ToastProvider".');
        spy.mockRestore();
    });
});
