import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import { GlyphIcon } from '@theorchard/suite-icons';
import { testComponent } from 'lib/test-utils/common';
import { SuiteBadge, getIndicatorIcon } from '../suiteBadge';
import { SuiteBadgeProps } from '../types';

describe('<SuiteBadge>', () => {
    const defaultProps: SuiteBadgeProps = {
        text: 'TheText OfTheBadge',
        type: 'tag',
    };

    const renderComponent = (extraProps: Partial<SuiteBadgeProps> = {}) => {
        const props = { ...defaultProps, ...extraProps } as SuiteBadgeProps;
        return render(<SuiteBadge {...props} />);
    };

    const getSections = (container: HTMLElement) => {
        const brandElement = container.querySelector('.SuiteBadge-brand');
        const iconElement = container.querySelector('.SuiteBadge-icon');
        const labelElement = container.querySelector('.SuiteBadge-label');
        const textElement = container.querySelector('.SuiteBadge-text');
        const childrenElement = container.querySelector('.SuiteBadge-children');
        const indicatorElement = container.querySelector('.SuiteBadge-indicator');

        return {
            brandElement,
            iconElement,
            labelElement,
            textElement,
            indicatorElement,
            childrenElement,
        };
    };

    testComponent(SuiteBadge);

    test('applies style merged with width/maxWidth', () => {
        const { getByTestId } = renderComponent({ style: { marginTop: 10 } });
        expect(getByTestId('SuiteBadge')).toHaveStyle({ marginTop: '10px', maxWidth: '100%' });
    });

    test('renders just the badge text', () => {
        const { container, getByText, getByTestId } = renderComponent();

        const { brandElement, iconElement, indicatorElement, textElement, childrenElement } =
            getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(childrenElement).toBeNull();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeInTheDocument();

        expect(getByTestId('SuiteBadge')).not.toHaveClass('active', 'disabled');
        expect(getByText('TheText OfTheBadge')).toBeInTheDocument();
    });

    test('renders just children render property', () => {
        // "tag.spec" has more tests for children
        const { container, queryByText, getByText } = renderComponent({
            children: <div>HiAmAChild</div>,
            text: undefined,
        });

        const { brandElement, iconElement, indicatorElement, textElement, childrenElement } =
            getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(childrenElement).toBeInTheDocument();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeNull();

        expect(queryByText('TheText OfTheBadge')).not.toBeInTheDocument();
        expect(getByText('HiAmAChild')).toBeInTheDocument();
    });

    test('renders the text along with the children', () => {
        const { container, getByText } = renderComponent({
            children: <div>HiAmAChild</div>,
        });

        const { brandElement, iconElement, textElement, childrenElement, indicatorElement } =
            getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(textElement).toBeInTheDocument();
        expect(childrenElement).toBeInTheDocument();
        expect(indicatorElement).toBeNull();

        expect(getByText('TheText OfTheBadge')).toBeInTheDocument();
        expect(getByText('HiAmAChild')).toBeInTheDocument();
    });

    test('renders the text and a brand icon', () => {
        const { container, getByTestId } = renderComponent({
            brand: 'orchard',
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeInTheDocument();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeInTheDocument();

        expect(getByTestId('SuiteBadgeBrand')).toBeInTheDocument();
        expect(getByTestId('OrchardBrandIcon')).toBeInTheDocument();
    });

    test('renders without text, just the brand icon', () => {
        const { container, getByTestId } = renderComponent({
            brand: 'orchard',
            text: undefined,
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeInTheDocument();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeNull();

        expect(getByTestId('SuiteBadgeBrand')).toBeInTheDocument();
        expect(getByTestId('OrchardBrandIcon')).toBeInTheDocument();
    });

    test('renders given icon', () => {
        const { container, getByTestId } = renderComponent({
            icon: <GlyphIcon name="info" size={16} />,
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeInTheDocument();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeInTheDocument();

        const brandIcon = getByTestId('InfoGlyphIcon');

        expect(brandIcon).toBeInTheDocument();
    });

    test('renders the text and a 12px green check indicator on type tag', () => {
        const { container, getByTestId } = renderComponent({
            indicator: 'success',
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeInTheDocument();
        expect(textElement).toBeInTheDocument();

        const warnIndicator = getByTestId('CheckGlyphIcon');
        expect(warnIndicator).toHaveClass('success');
        expect(warnIndicator).toHaveAttribute('width', '12');
    });

    test('renders the text and a 16px warning indicator on type pill', () => {
        const { container, getByTestId } = renderComponent({
            indicator: 'warning',
            type: 'pill',
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeInTheDocument();
        expect(textElement).toBeInTheDocument();

        const warnIndicator = getByTestId('WarningGlyphIcon');
        expect(warnIndicator).toHaveClass('warning');
        expect(warnIndicator).toHaveAttribute('width', '16');
    });

    test('renders the text and a 16px revision indicator on type pill', () => {
        const { container, getByTestId } = renderComponent({
            indicator: 'revision',
            type: 'pill',
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeInTheDocument();
        expect(textElement).toBeInTheDocument();

        const revisionIndicator = getByTestId('RevisionGlyphIcon');
        expect(revisionIndicator).toHaveClass('revision');
        expect(revisionIndicator).toHaveAttribute('width', '16');
    });

    test('renders the label along with the text', () => {
        const { container, getByTestId } = renderComponent({
            label: 'LabelMe',
        });

        const { brandElement, iconElement, indicatorElement, textElement, labelElement } =
            getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(indicatorElement).not.toBeInTheDocument();
        expect(labelElement).toBeInTheDocument();
        expect(textElement).toBeInTheDocument();

        const label = getByTestId('SuiteBadgeLabel');
        expect(label).toHaveTextContent('LabelMe');
        expect(getByTestId('SuiteBadgeText')).toHaveTextContent('TheText OfTheBadge');
    });

    test('renders the text and the X (close) icon and applies the active styling', () => {
        const onRemoveHandler = vi.fn();
        const { container, getByTestId } = renderComponent({
            onClose: onRemoveHandler,
        });

        const { brandElement, iconElement, indicatorElement, textElement } = getSections(container);

        expect(brandElement).toBeNull();
        expect(iconElement).toBeNull();
        expect(indicatorElement).toBeNull();
        expect(textElement).toBeInTheDocument();

        const closeButton = getByTestId('CloseGlyphIcon');

        expect(getByTestId('SuiteBadge')).toHaveClass('active');
        expect(closeButton).toBeInTheDocument();

        fireEvent.click(closeButton);

        expect(onRemoveHandler).toHaveBeenCalled();
    });

    describe('with "disabled" true', () => {
        test('adds disabled class', () => {
            const { getByTestId } = renderComponent({ disabled: true });

            expect(getByTestId('SuiteBadge')).toHaveClass('disabled');
        });

        test('does not invoke onClose callback', () => {
            const onClose = vi.fn();
            const { getByTestId } = renderComponent({
                onClose,
                disabled: true,
            });

            const closeButton = getByTestId('SuiteBadgeCloseButton');
            fireEvent.click(closeButton);

            expect(onClose).not.toHaveBeenCalled();
        });
    });

    describe('with "width"', () => {
        test('applies the width to the outer element', () => {
            const width = 102;
            const { getByTestId } = renderComponent({ width });

            expect(getByTestId('SuiteBadge')).toHaveStyle(`width:${width}px`);
            expect(getByTestId('SuiteBadgeText')).toHaveClass('dynamic');
        });
    });

    describe('with "maxWidth"', () => {
        test('applies the max-width to the text element', () => {
            const maxWidth = 102;
            const { getByTestId } = renderComponent({ maxWidth });

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

        test('defaults to 100%', () => {
            const { getByTestId } = renderComponent();

            expect(getByTestId('SuiteBadgeText')).not.toHaveClass('dynamic');
            expect(getByTestId('SuiteBadge')).toHaveStyle(`max-width:100%`);
        });
    });

    describe('with "size"', () => {
        test('applies the size class when size is small', () => {
            const { getByTestId } = renderComponent({ size: 'small', type: 'pill' });

            expect(getByTestId('SuiteBadge')).toHaveClass('small');
        });

        test('does not applies "default" size class when not specified', () => {
            const { getByTestId } = renderComponent();

            expect(getByTestId('SuiteBadge')).not.toHaveClass('default');
        });

        test('applies size class for tags', () => {
            const { getByTestId } = renderComponent({ size: 'small', type: 'tag' });

            expect(getByTestId('SuiteBadge')).toHaveClass('small');
        });
    });
});

describe('getIndicatorIcon', () => {
    const indicators = [
        ['success', 'check'],
        ['warning', 'warning'],
        ['error', 'warning'],
        ['revision', 'revision'],
    ] as const;

    test.each(indicators)('%s -> %s', (indicator, icon) => {
        expect(getIndicatorIcon(indicator)).toBe(icon);
    });
});
