import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { TruncatedText, TruncatedTextProps } from '../truncatedText';

describe('<TruncatedText>', () => {
    const defaultText = 'Hey Beautiful';

    const defaultProps: TruncatedTextProps = {
        text: defaultText,
    };

    const renderComponent = (props?: Partial<TruncatedTextProps>) => {
        return render(<TruncatedText {...defaultProps} {...props} />);
    };

    testComponent(TruncatedText);

    test('applies style to the inner text element', () => {
        const { container } = renderComponent({ style: { marginTop: 10 } });
        const inner = container.querySelector('.TruncatedText-inner');
        expect(inner).toHaveStyle({ marginTop: '10px' });
    });

    test('renders dynamic width by default', () => {
        const { getByText, getByTestId } = renderComponent();

        const innerTextElement = getByText(defaultText);
        const componentElement = getByTestId('TruncatedText');

        expect(innerTextElement).toBeInTheDocument();
        expect(componentElement).toHaveClass('dynamic');

        expect(componentElement).not.toHaveClass('multi-line');
        expect(innerTextElement.style).not.toMatchObject({
            WebkitLineClamp: expect.any,
        });
    });

    describe('with "maxWidth"', () => {
        test('renders with max-width', () => {
            const maxWidth = 100;

            const { getByText, getByTestId } = renderComponent({
                maxWidth,
            });

            const innerTextElement = getByText(defaultText);
            const componentElement = getByTestId('TruncatedText');

            expect(innerTextElement).toBeInTheDocument();
            expect(innerTextElement).toHaveStyle(`max-width:${maxWidth}px`);

            expect(componentElement).not.toHaveClass('dynamic');
            expect(componentElement).toHaveStyle(`max-width:${maxWidth}px`);
        });
    });

    describe('with "numberOfLines"', () => {
        test('adds "multi-line" class and line-clamp style', () => {
            const numberOfLines = 3;

            const { getByText, getByTestId } = renderComponent({
                numberOfLines,
            });

            const innerTextElement = getByText(defaultText);
            const componentElement = getByTestId('TruncatedText');

            expect(componentElement).toHaveClass('multi-line');
            expect(innerTextElement.style).toMatchObject({
                WebkitLineClamp: numberOfLines.toString(),
            });
        });
    });

    describe('with "contentPrivacy"', () => {
        test('adds "data-dd-privacy" attribute', async () => {
            const { getByText } = renderComponent({
                contentPrivacy: 'hidden',
            });

            const innerTextElement = getByText(defaultText);

            expect(innerTextElement).toHaveAttribute('data-dd-privacy', 'hidden');
        });

        describe('when truncated', () => {
            test('shows tooltip with content privacy attribute', async () => {
                vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(100);

                const { getByTestId, findByText } = renderComponent({
                    contentPrivacy: 'hidden',
                });

                const componentElement = getByTestId('TruncatedText');

                fireEvent.mouseOver(componentElement);

                const tooltipElement = await findByText(defaultText, {
                    selector: '.TooltipOverlay .tooltip-inner',
                });

                expect(tooltipElement.closest('.tooltip')).toHaveAttribute(
                    'data-dd-privacy',
                    'hidden'
                );
            });
        });
    });

    describe('when truncated', () => {
        test('shows tooltip with full text on hover', async () => {
            vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(100);

            const { findByText, getByTestId } = renderComponent();

            const componentElement = getByTestId('TruncatedText');

            fireEvent.mouseOver(componentElement);

            const tooltipTextElement = await findByText(defaultText, {
                selector: '.TooltipOverlay .tooltip-inner',
            });

            expect(tooltipTextElement).toBeInTheDocument();
        });

        test('shows tooltip with full text for multiline text', async () => {
            vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(0);
            vi.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockReturnValue(100);

            const { findByText, getByTestId } = renderComponent();

            const componentElement = getByTestId('TruncatedText');

            fireEvent.mouseOver(componentElement);

            const tooltipTextElement = await findByText(defaultText, {
                selector: '.TooltipOverlay .tooltip-inner',
            });

            expect(tooltipTextElement).toBeInTheDocument();
        });
    });

    describe('when not truncated', () => {
        test('does not show tooltip on hover', () => {
            vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(0);
            vi.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockReturnValue(0);

            const { queryByText, getByTestId } = renderComponent();

            const componentElement = getByTestId('TruncatedText');

            fireEvent.mouseOver(componentElement);

            const tooltipTextElement = queryByText(defaultText, {
                selector: '.TooltipOverlay .tooltip-inner',
            });

            expect(tooltipTextElement).toBeNull();
        });
    });

    describe('with "tooltipProps"', () => {
        test('passes textAlign to Tooltip', async () => {
            vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(100);

            const { getByTestId, findByRole } = renderComponent({
                tooltipProps: { textAlign: 'center' },
            });

            const componentElement = getByTestId('TruncatedText');
            fireEvent.mouseOver(componentElement);

            const tooltipElement = await findByRole('tooltip');

            const tooltipOverlay = tooltipElement.closest('.TooltipOverlay');

            expect(tooltipOverlay).toHaveStyle('--tooltip-text-align: center');
        });

        test('passes placement to Tooltip', async () => {
            vi.spyOn(HTMLElement.prototype, 'scrollWidth', 'get').mockReturnValue(100);

            const { findByText, getByTestId } = renderComponent({
                tooltipProps: { placement: 'bottom' },
            });

            const componentElement = getByTestId('TruncatedText');
            fireEvent.mouseOver(componentElement);

            const tooltipElement = await findByText(defaultText, {
                selector: '.TooltipOverlay .tooltip-inner',
            });

            expect(tooltipElement.closest('.tooltip')).toHaveClass('bs-tooltip-bottom');
        });
    });
});
