import React from 'react';
import { formatMessage } from '@theorchard/suite-i18n';
import { renderInAppContext } from '@theorchard/suite-testing';
import { testComponent } from 'lib/test-utils/common';
import { ErrorMessage, ErrorMessageProps } from '../errorMessage';

describe('<ErrorMessage>', () => {
    const error = new Error('oops');
    const code = 'fatal';
    const description = 'oops';

    const renderComponent = (props: Partial<ErrorMessageProps> = {}) =>
        renderInAppContext(<ErrorMessage {...props} />);

    testComponent(ErrorMessage, {});

    test('applies style', () => {
        const r = renderComponent({ style: { marginTop: 10 } });
        expect(r.getByTestId('ErrorMessage')).toHaveStyle({ marginTop: '10px' });
    });

    describe('icon', () => {
        test('accepts custom icon', () => {
            const dummyIcon = 'hi';
            const { getByText } = renderComponent({ icon: () => <>{dummyIcon}</> });

            expect(getByText(dummyIcon)).toBeVisible();
        });
    });

    describe('no arguments', () => {
        test('renders default message', () => {
            const { getByText } = renderComponent();

            expect(getByText(formatMessage('error_generic_title'))).toBeVisible();
            expect(getByText(formatMessage('error_generic_message'))).toBeVisible();
        });

        test('renders error illustration', () => {
            const { getByTestId } = renderComponent();

            expect(getByTestId('FatalErrorIllustration')).toBeVisible();
        });
    });

    describe('with "message"', () => {
        const message = 'some-message';
        test('renders message', () => {
            const { getByText } = renderComponent({ message, error });

            expect(getByText(formatMessage('error_generic_title'))).toBeVisible();
            expect(getByText(message)).toBeVisible();
        });

        describe('plus "code"', () => {
            test('renders title based on code', () => {
                const { getByText } = renderComponent({ message, error, code });

                expect(getByText(`error_${code}_title`)).toBeVisible();
                expect(getByText(message)).toBeVisible();
            });
        });
    });

    describe('with "error"', () => {
        test('renders error.message', () => {
            const { getByText } = renderComponent({ error });

            expect(getByText(formatMessage('error_generic_title'))).toBeVisible();
            expect(getByText(error.message)).toBeVisible();
        });

        describe('plus "code"', () => {
            test('renders title based on code', () => {
                const { getByText } = renderComponent({ error, code });

                expect(getByText(`error_${code}_title`)).toBeVisible();
                expect(getByText(error.message)).toBeVisible();
            });
        });
    });

    describe('with "code"', () => {
        test('renders message based on code', () => {
            const { getByText } = renderComponent({ code });

            expect(getByText(`error_${code}_title`)).toBeVisible();
            expect(getByText(`error_${code}_message`)).toBeVisible();
        });
    });

    describe('with children', () => {
        test('renders children alongside description', () => {
            const childText = 'subtext';
            const { getByText } = renderComponent({
                children: <span>{childText}</span>,
                description,
            });
            expect(getByText(childText)).toBeVisible();
            expect(getByText(description)).toBeVisible();
        });
    });

    describe('variant prop', () => {
        test('full-page variant', () => {
            const { getByTestId, getByRole } = renderComponent({ variant: 'full-page' });

            expect(getByTestId('ErrorMessage')).toHaveClass('ErrorMessage-full-page');
            expect(getByTestId('FatalErrorIllustration')).toHaveAttribute('width', '200');
            expect(getByRole('heading', { level: 1 })).toBeVisible();
        });

        test('large variant', () => {
            const { getByTestId, getByRole } = renderComponent({ variant: 'large' });

            expect(getByTestId('ErrorMessage')).toHaveClass('ErrorMessage-large');
            expect(getByTestId('FatalErrorIllustration')).toHaveAttribute('width', '100');
            expect(getByRole('heading', { level: 4 })).toBeVisible();
        });

        test('medium variant', () => {
            const { getByTestId, getByRole } = renderComponent({ variant: 'medium' });

            expect(getByTestId('ErrorMessage')).toHaveClass('ErrorMessage-medium');
            expect(getByTestId('FatalErrorIllustration')).toHaveAttribute('width', '100');
            expect(getByRole('heading', { level: 4 })).toBeVisible();
        });

        test('small variant', () => {
            const { getByTestId, getByRole } = renderComponent({ variant: 'small' });

            expect(getByTestId('ErrorMessage')).toHaveClass('ErrorMessage-small');
            expect(getByTestId('FatalErrorIllustration')).toHaveAttribute('width', '50');
            expect(getByRole('heading', { level: 4 })).toBeVisible();
        });
    });

    describe('title', () => {
        test('renders title', () => {
            const title = 'Test title';
            const wrapper = renderComponent({ title });
            expect(wrapper.getByText(title)).toBeVisible();
        });

        test('title prop takes precedence over code-based title', () => {
            const title = 'Custom title';
            const wrapper = renderComponent({ title, code: 'fatal' });
            expect(wrapper.getByText(title)).toBeVisible();
            expect(wrapper.queryByText('error_fatal_title')).toBeNull();
        });
    });

    describe('description', () => {
        test('renders description', () => {
            const description = 'Test description';
            const wrapper = renderComponent({ description });
            expect(wrapper.getByText(description)).toBeVisible();
        });

        test('description takes precedence over message', () => {
            const description = 'Custom description';
            const wrapper = renderComponent({ description, message: 'Old message' });
            expect(wrapper.getByText(description)).toBeVisible();
            expect(wrapper.queryByText('Old message')).toBeNull();
        });

        test('description takes precedence over error.message', () => {
            const description = 'Custom description';
            const error = new Error('Error message');
            const wrapper = renderComponent({ description, error });
            expect(wrapper.getByText(description)).toBeVisible();
            expect(wrapper.queryByText('Error message')).toBeNull();
        });

        test('message takes precedence over error.message', () => {
            const customMessage = 'Custom message';
            const error = new Error('Error message');
            const wrapper = renderComponent({ message: customMessage, error });
            expect(wrapper.getByText(customMessage)).toBeVisible();
            expect(wrapper.queryByText('Error message')).toBeNull();
        });
    });

    describe('width prop', () => {
        test('applies custom width', () => {
            const { getByTestId } = renderComponent({ width: 600 });
            expect(getByTestId('ErrorMessage')).toHaveStyle({ width: '600px' });
        });

        test('applies default width when not specified', () => {
            const { getByTestId } = renderComponent();
            expect(getByTestId('ErrorMessage')).toHaveStyle({ width: undefined });
        });
    });

    describe('button', () => {
        test('renders a basic button', () => {
            const text = 'some button';
            const { getByRole } = renderComponent({ button: { text } });
            const btn = getByRole('button');
            expect(btn).toBeInTheDocument();
            expect(btn).toHaveTextContent(text);
            expect(btn).toHaveClass('btn-lg');
        });

        test('renders sm button in small InfoMessage', () => {
            const text = 'some button';
            const { getByRole } = renderComponent({ button: { text }, variant: 'small' });
            const btn = getByRole('button');
            expect(btn).toBeInTheDocument();
            expect(btn).toHaveClass('btn');
            expect(btn).not.toHaveClass('btn-sm');
            expect(btn).not.toHaveClass('btn-lg');
        });
    });
});
