import React from 'react';
import { queryByTestId, findByTestId } from '@testing-library/dom';

import type { MountableExtension } from '@atlassian/clientside-extensions';
import renderElementAsReact from './renderElementAsReact';

/* eslint-disable react/no-unused-prop-types */
interface TestProps {
    name?: string;
    value?: number;
    children?: React.ReactNode;
}
/* eslint-enable react/no-unused-prop-types */

describe('renderElementAsReact', () => {
    let testContainer: HTMLElement;
    let onMountCallback: (element: HTMLElement) => void;
    let onUnmountCallback: (element: HTMLElement) => void;

    const testRenderAPI: MountableExtension = {
        onMount: (callback) => {
            onMountCallback = callback;
            return testRenderAPI;
        },
        onUnmount: (callback) => {
            onUnmountCallback = callback;
            return testRenderAPI;
        },
    };

    const TestComponent = (props: unknown) => (
        <div data-testid="test-component">
            <h2>A test content</h2>
            <p data-testid="test-props">{JSON.stringify(props)}</p>
        </div>
    );

    beforeEach(() => {
        testContainer = document.createElement('div');
    });

    it('should use the renderApi to mount and unmount the given react component', () => {
        const getTestComponent = () => queryByTestId(testContainer, 'test-component');

        renderElementAsReact(testRenderAPI, TestComponent);

        // expect the component NOT to be rendered until onMount is called by the handler
        expect(getTestComponent()).toBeFalsy();

        onMountCallback(testContainer);

        // rendered using onMount
        expect(getTestComponent()).toBeTruthy();
        expect(getTestComponent()?.textContent).toMatch('A test content');

        onUnmountCallback(testContainer);

        // destroyed using onUnmount
        expect(getTestComponent()).toBeFalsy();
    });

    it('should receive additional props to assing to the given react component', async () => {
        const testProps: TestProps = {
            name: 'Meaning of life',
            value: 42,
        };

        renderElementAsReact(testRenderAPI, TestComponent, testProps);
        onMountCallback(testContainer);

        const testPropsElement = await findByTestId(testContainer, 'test-props');

        // props applied to the component when rendering
        expect(testPropsElement).toBeTruthy();
        expect(JSON.parse(testPropsElement.textContent as string)).toMatchObject(testProps);
    });
});
