import React from 'react';
import { render } from '@testing-library/react';
import { PageContext } from '../..';
import { Modal } from '../index';
import { CustomModalProps } from '../types';

describe('<Modal>', () => {
    const defaultProps = {
        onRequestClose: () => false,
        isOpen: true,
        header: 'Im a Modal!',
        closeTimeoutMS: 0,
    };

    const page = {
        className: 'TestPage',
    };

    const renderComponent = (props?: Partial<CustomModalProps>) =>
        render(<Modal.Custom {...defaultProps} {...(props as CustomModalProps)} />, {
            wrapper: ({ children }) => (
                <div className="TestPage PageContainer">
                    <PageContext.Provider value={page}>{children}</PageContext.Provider>
                </div>
            ),
        });

    test('portal has page container class name', () => {
        const { baseElement } = renderComponent();
        const portal = baseElement.querySelector('.ReactModalPortal');
        expect(portal).toHaveClass(page.className);
    });

    describe('when closed', () => {
        test('renders nothing', () => {
            const { baseElement } = renderComponent({
                ...defaultProps,
                isOpen: false,
            });
            expect(baseElement).toMatchSnapshot();
        });
    });

    describe('when open', () => {
        test('renders modal', () => {
            const { baseElement } = renderComponent();
            expect(baseElement).toMatchSnapshot();
        });
    });

    describe('footer', () => {
        test('does not render by default', () => {
            const { queryByTestId } = renderComponent();
            expect(queryByTestId('ModalFooter')).toBeNull();
        });

        test('renders when confirm callback is passed', () => {
            const { getByTestId, getByText } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
            });
            expect(getByTestId('ModalFooter')).toBeInTheDocument();
            expect(getByText('save')).toBeVisible();
            expect(getByText('cancel')).toBeVisible();
        });

        test('hides cancel button when hideCancelButton is enabled', () => {
            const { getByTestId, queryByText } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
                hideCancelButton: true,
            });
            expect(getByTestId('ModalFooter')).toBeInTheDocument();
            expect(queryByText('cancel')).toBeNull();
        });

        test('changes confirm text when `confirmTitle` is passed', () => {
            const confirmTitle = 'CONFIRM_TEXT';
            const { getByTestId, getByText } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
                confirmTitle,
            });
            expect(getByTestId('ModalFooter')).toBeInTheDocument();
            expect(getByText(confirmTitle)).toBeVisible();
        });

        test('changes cancel text when `cancelTitle` is passed', () => {
            const cancelTitle = 'CANCEL_TEXT';
            const { getByTestId, getByText } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
                cancelTitle,
            });
            expect(getByTestId('ModalFooter')).toBeInTheDocument();
            expect(getByText(cancelTitle)).toBeVisible();
        });

        test('renders custom footer', () => {
            const custom = 'CUSTOM FOOTER';
            const { getByText } = renderComponent({
                customFooter: <div>{custom}</div>,
            });
            expect(getByText(custom)).toBeVisible();
        });

        test('disables confirm button when `confirmDisabled` is passed', () => {
            const { getByText } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
                confirmDisabled: true,
            });
            expect(getByText('save')).toBeVisible();
            expect(getByText('save')).toBeDisabled();
        });

        test('changes confirm button variant when `confirmVariant` is passed', () => {
            const confirmTitle = 'CONFIRM_TEXT';
            const { getByRole } = renderComponent({
                ...defaultProps,
                onConfirm: () => null,
                hideCancelButton: true,
                confirmTitle,
                confirmVariant: 'danger',
            });

            const btn = getByRole('button', { name: confirmTitle });
            expect(btn).toBeVisible();
            expect(btn).toHaveClass('btn-danger');
        });

        describe('footer buttons', () => {
            test('renders footer when only confirmButtonProps is passed', () => {
                const r = renderComponent({
                    ...defaultProps,
                    confirmButtonProps: { title: 'Custom Save' },
                });
                expect(r.getByTestId('ModalFooter')).toBeInTheDocument();
                expect(r.getByText('Custom Save')).toBeVisible();
                expect(r.getByText('cancel')).toBeVisible();
            });

            test('customizes confirm button with confirmButtonProps', () => {
                const r = renderComponent({
                    ...defaultProps,
                    confirmButtonProps: {
                        title: 'Apply Changes',
                        variant: 'quartenary',
                        disabled: true,
                    },
                });

                const btn = r.getByRole('button', { name: 'Apply Changes' });
                expect(btn).toBeVisible();
                expect(btn).toHaveClass('btn-quartenary');
                expect(btn).toBeDisabled();
            });

            test('customizes cancel button with cancelButtonProps', () => {
                const r = renderComponent({
                    ...defaultProps,
                    onConfirm: () => {},
                    cancelButtonProps: {
                        title: 'Discard',
                        variant: 'danger',
                    },
                });
                const btn = r.getByRole('button', { name: 'Discard' });
                expect(btn).toBeVisible();
                expect(btn).toHaveClass('btn-danger');
            });

            test('buttonProps override deprecated props', () => {
                const r = renderComponent({
                    ...defaultProps,
                    confirmTitle: 'Old Title',
                    confirmVariant: 'primary',
                    confirmDisabled: false,
                    confirmButtonProps: {
                        title: 'New Title',
                        variant: 'quartenary',
                        disabled: true,
                    },
                });

                const btn = r.getByRole('button', { name: 'New Title' });
                expect(btn).toBeVisible();
                expect(btn).toHaveClass('btn-quartenary');
                expect(btn).toBeDisabled();
                expect(r.queryByText('Old Title')).toBeNull();
            });

            test('deprecated props work as fallback when buttonProps missing properties', () => {
                const r = renderComponent({
                    ...defaultProps,
                    confirmTitle: 'Fallback Title',
                    confirmVariant: 'danger',
                    confirmDisabled: true,
                    confirmButtonProps: {
                        variant: 'quartenary',
                    },
                });

                const btn = r.getByRole('button', { name: 'Fallback Title' });
                expect(btn).toBeVisible();
                expect(btn).toHaveClass('btn-quartenary');
                expect(btn).toBeDisabled();
                expect(r.getByText('Fallback Title')).toBeVisible();
            });

            test('handles onClick in buttonProps', () => {
                const handleConfirm = vi.fn();
                const handleCancel = vi.fn();

                const r = renderComponent({
                    ...defaultProps,
                    confirmButtonProps: {
                        title: 'Save',
                        onClick: handleConfirm,
                    },
                    cancelButtonProps: {
                        title: 'Cancel',
                        onClick: handleCancel,
                    },
                });

                const confirmBtn = r.getByRole('button', { name: 'Save' });
                const cancelBtn = r.getByRole('button', { name: 'Cancel' });

                confirmBtn.click();
                cancelBtn.click();

                expect(handleConfirm).toHaveBeenCalledTimes(1);
                expect(handleCancel).toHaveBeenCalledTimes(1);
            });
        });
    });

    describe('header', () => {
        test('is rendered by default', () => {
            const { getByText } = renderComponent();
            expect(getByText(defaultProps.header)).toBeVisible();
        });

        test('hides default header when custom header is passed', () => {
            const custom = 'CUSTOM HEADER';
            const { queryByText, getByText } = renderComponent({
                customHeader: <div>{custom}</div>,
            });
            expect(queryByText(defaultProps.header)).toBeNull();
            expect(getByText(custom)).toBeVisible();
        });

        test('accepts subtitle prop', () => {
            const subtitle = 'SOME SUBTITLE';
            const { getByText } = renderComponent({ subtitle });
            expect(getByText(subtitle)).toBeVisible();
        });
    });

    describe('contentStyle', () => {
        test('is not set by default', () => {
            const { getByTestId } = renderComponent();
            expect(getByTestId('Modal')).not.toHaveAttribute('style');
        });

        test('passes styles to the Modal content element', () => {
            const { getByTestId } = renderComponent({
                contentStyle: { color: '#123abc', fontSize: '20px' },
            });
            expect(getByTestId('Modal')).toHaveStyle({
                color: '#123abc',
                fontSize: '20px',
            });
        });
    });
});
