import React from 'react';
import { fireEvent, act } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { testComponent } from 'lib/test-utils/common';
import { Alert, AlertProps, CLASS_NAME } from '../alert';

describe('<Alert>', () => {
    const defaultText = 'alert text here';
    const defaultVariant = 'information';
    const defaultProps: AlertProps = {
        text: defaultText,
        variant: defaultVariant,
    };

    const renderComponent = (props: Partial<AlertProps> = {}) =>
        renderInAppContext(<Alert {...defaultProps} {...props} />);

    testComponent(Alert, defaultProps);

    test('renders text', () => {
        const wrapper = renderComponent();
        expect(wrapper.getByText(defaultText)).toBeVisible();
    });

    test('renders text element', () => {
        const testId = 'text-test-id';
        const wrapper = renderComponent({
            text: <div data-testid={testId}>{defaultText}</div>,
        });
        expect(wrapper.getByText(defaultText)).toBeVisible();
        expect(wrapper.getByTestId(testId)).toBeVisible();
    });

    test('renders title', () => {
        const titleText = 'some title text';
        const wrapper = renderComponent({ title: titleText });
        expect(wrapper.getByText(titleText)).toBeVisible();
    });

    describe('renders variants correctly', () => {
        const variants = [
            ['error', 'error', 'Warning'],
            ['warn', 'warn', 'Warning'],
            ['success', 'success', 'Success'],
            ['information', 'information', 'Info'],
            ['flag', 'error', 'Flag'],
        ];
        variants.forEach(([variant, color, glyphName]) => {
            test(`${variant}`, () => {
                const wrapper = renderComponent({ variant } as AlertProps);
                expect(wrapper.getByTestId(`${glyphName}GlyphIcon`)).toBeVisible();
                expect(wrapper.getByTestId(CLASS_NAME)).toHaveClass(`${CLASS_NAME}-${color}`);
            });
        });
    });

    test('renders dismiss button', () => {
        const wrapper = renderComponent({ dismissible: true });
        const button = wrapper.getByRole('button');
        expect(button).toBeVisible();
        expect(button).toHaveClass(`${CLASS_NAME}-close`);
        expect(wrapper.container.querySelector(`.${CLASS_NAME}-controls`)).toBeVisible();

        fireEvent.click(button);
        expect(wrapper.queryByTestId(CLASS_NAME)).toBeNull();
    });

    test('renders link', () => {
        const text = 'link text';
        const wrapper = renderComponent({ link: { text, to: '/' } });
        const link = wrapper.getByText(text);
        expect(link).toBeVisible();
        expect(link.getAttribute('href')).toBe('/');
    });

    test('renders button', () => {
        const text = 'button text';
        const mock = vi.fn();
        const onClick = (dismiss: () => void) => {
            dismiss();
            mock();
        };
        const wrapper = renderComponent({ button: { text, onClick } });
        const button = wrapper.getByText(text);
        expect(button).toBeVisible();

        fireEvent.click(button);
        expect(mock).toHaveBeenCalled();
        expect(wrapper.queryByTestId(CLASS_NAME)).toBeNull();
    });

    test('renders button with custom extra properties', () => {
        const text = 'custom button text';
        const mock = vi.fn();
        const onClick = (dismiss: () => void) => {
            dismiss();
            mock();
        };
        const wrapper = renderComponent({
            button: {
                text,
                onClick,
                variant: 'primary',
                size: 'sm',
                disabled: false,
                className: 'custom-button-class',
            },
        });
        const button = wrapper.getByText(text);
        expect(button).toBeVisible();
        expect(button).toHaveClass('custom-button-class');

        fireEvent.click(button);
        expect(mock).toHaveBeenCalled();
        expect(wrapper.queryByTestId(CLASS_NAME)).toBeNull();
    });

    describe('onDismiss', () => {
        test('called when button is clicked', () => {
            const onDismiss = vi.fn();
            const wrapper = renderComponent({ dismissible: true, onDismiss });
            const button = wrapper.getByRole('button');
            expect(button).toBeVisible();
            fireEvent.click(button);
            expect(button).toBeVisible();
            expect(onDismiss).toHaveBeenCalledTimes(1);
        });

        test('passes callback which closes alert when called', () => {
            vi.useFakeTimers();
            const onDismiss = vi
                .fn()
                .mockImplementation((dismiss: () => void) => setTimeout(() => dismiss(), 1000));
            const wrapper = renderComponent({ dismissible: true, onDismiss });
            const button = wrapper.getByRole('button');
            expect(button).toBeVisible();
            fireEvent.click(button);
            expect(button).toBeVisible();
            expect(onDismiss).toHaveBeenCalledTimes(1);
            act(() => {
                vi.runAllTimers();
            });

            expect(wrapper.queryByTestId(CLASS_NAME)).toBeNull();
        });
    });

    describe('expandable', () => {
        test('does not show toggle button when expandable but no title', () => {
            const wrapper = renderComponent({ expandable: true });

            expect(wrapper.queryByTestId(`${CLASS_NAME}-toggle`)).toBeNull();
        });

        test('shows toggle button when expandable and title is provided', () => {
            const wrapper = renderComponent({ expandable: true, title: 'Test Title' });

            expect(wrapper.getByTestId(`${CLASS_NAME}-toggle`)).toBeVisible();
        });

        test('starts expanded by default', () => {
            const wrapper = renderComponent({ expandable: true, title: 'Test Title' });

            expect(wrapper.getByText(defaultText)).toBeVisible();
            expect(wrapper.getByTestId('ChevronUpGlyphIcon')).toBeVisible();
        });

        test('starts collapsed when defaultExpanded is false', () => {
            const wrapper = renderComponent({
                expandable: true,
                title: 'Test Title',
                defaultExpanded: false,
            });

            expect(wrapper.container.querySelector('.ExpandableBody-collapsed')).toBeTruthy();
            expect(wrapper.getByTestId('ChevronDownGlyphIcon')).toBeVisible();
        });

        test('toggles content visibility when toggle button is clicked', () => {
            const wrapper = renderComponent({ expandable: true, title: 'Test Title' });

            // Initially expanded
            expect(wrapper.getByText(defaultText)).toBeVisible();
            expect(wrapper.container.querySelector('.ExpandableBody-collapsed')).toBeFalsy();

            // Click to collapse
            fireEvent.click(wrapper.getByTestId(`${CLASS_NAME}-toggle`));
            expect(wrapper.container.querySelector('.ExpandableBody-collapsed')).toBeTruthy();
            expect(wrapper.getByTestId('ChevronDownGlyphIcon')).toBeVisible();

            // Click to expand again
            fireEvent.click(wrapper.getByTestId(`${CLASS_NAME}-toggle`));
            expect(wrapper.container.querySelector('.ExpandableBody-collapsed')).toBeFalsy();
            expect(wrapper.getByTestId('ChevronUpGlyphIcon')).toBeVisible();
        });

        test('hides actions when collapsed', () => {
            const wrapper = renderComponent({
                expandable: true,
                title: 'Test Title',
                link: { text: 'Link', to: '/' },
            });

            expect(wrapper.getByText('Link')).toBeVisible();

            fireEvent.click(wrapper.getByTestId(`${CLASS_NAME}-toggle`));

            expect(wrapper.container.querySelector('.ExpandableBody-collapsed')).toBeTruthy();
        });

        test('dismissible prop shows icon next to expandable toggle', () => {
            const wrapper = renderComponent({
                expandable: true,
                dismissible: true,
                title: 'Test Title',
            });

            expect(wrapper.getByTestId(`${CLASS_NAME}-toggle`)).toBeVisible();
            expect(wrapper.getAllByRole('button')).toHaveLength(2);
        });
    });
});
