import React from 'react';
import { mocked } from 'jest-mock';
import { render, fireEvent, screen, findByRole as findByRoleInContainer } from '@testing-library/react';

import type { SerializedDocument } from '@atlassian/clientside-extensions-schema';
import useDiscovery from './debug/useDiscovery';
import type { ExtensionPointInfoProps } from './ExtensionPointInfo';
import ExtensionPointInfo from './ExtensionPointInfo';

jest.mock('./debug/useDiscovery');

const mockSchemaDocument: SerializedDocument = {
    objectTypes: {
        Schema: {
            name: 'Schema',
            description: '',
            descriptors: [
                {
                    description: 'Extension type',
                    name: 'type',
                    required: true,
                    type: 'AvailableTypes!',
                },
                {
                    description: '',
                    name: 'label',
                    required: true,
                    type: 'String!',
                },
                {
                    description: 'Glyph name',
                    name: 'glyph',
                    required: false,
                    type: 'GlyphType',
                },
                {
                    description: 'Hidden flag to hide web-item',
                    name: 'hidden',
                    required: false,
                    type: 'Boolean',
                },
                {
                    description: 'Callback triggered on user interaction with the Web-item. Signature depends on web-item type.',
                    name: 'onAction',
                    required: false,
                    type: 'Function',
                },
            ],
        },
        FooInput: {
            name: 'FooInput',
            description: '',
            descriptors: [
                {
                    description: '',
                    name: 'foo',
                    required: true,
                    type: 'String!',
                },
            ],
        },
    },
    enumTypes: {
        GlyphType: {
            name: 'GlyphType',
            description: '',
            values: ['cross', 'tick'],
        },
    },
    unionTypes: {
        AvailableTypes: {
            name: 'AvailableTypes',
            description: '',
            types: ['ModalType', 'LinkType', 'ButtonType', 'PanelType'],
        },
    },
    scalars: {
        ModalType: {
            name: 'ModalType',
            description: 'The value must be the constant "modal".',
        },
        LinkType: {
            name: 'LinkType',
            description: 'The value must be the constant "link".',
        },
        ButtonType: {
            name: 'ButtonType',
            description: 'The value must be the constant "button".',
        },
        PanelType: {
            name: 'PanelType',
            description: 'The value must be the constant "panel".',
        },
        String: {
            name: 'String',
            description: 'The `String` type represents textual data.',
        },
        Boolean: {
            name: 'Boolean',
            description: 'The `Boolean` type represents `true` or `false`.',
        },
        Function: {
            name: 'Function',
            description: 'The `Function` type represents any javascript function.',
        },
    },
};

const mockContextSchemaDocument: SerializedDocument = {
    objectTypes: {
        ContextSchema: {
            name: 'ContextSchema',
            description: '',
            descriptors: [
                {
                    description: '',
                    name: 'value',
                    required: false,
                    type: 'Number',
                },
            ],
        },
    },
    enumTypes: {},
    unionTypes: {},
    scalars: {
        Number: {
            name: 'Number',
            description: 'The value should be a number.',
        },
    },
};

const findModalElement = () => screen.findByRole('dialog');

const findModalContentElement = async () => {
    const infoModalElm = await findModalElement();

    return infoModalElm.querySelector('header + div');
};

const openModal = async (container: HTMLElement) => {
    const modalTriggerBtn = await findByRoleInContainer(container, 'button');
    fireEvent.click(modalTriggerBtn);
};

const TestComponent = ({ name, schemaDocuments }: ExtensionPointInfoProps) => {
    return (
        <>
            <p>Test extension point</p>
            <ExtensionPointInfo name={name} schemaDocuments={schemaDocuments} />
        </>
    );
};

TestComponent.defaultProps = {
    name: 'test-extension-point',
    schemaDocuments: { schema: mockSchemaDocument, contextSchema: mockContextSchemaDocument },
} as Partial<ExtensionPointInfoProps>;

describe('ExtensionPointInfo', () => {
    beforeEach(() => {
        mocked(useDiscovery).mockReturnValue([true, () => {}]);
    });

    it('should only show the the extension point icon if dicovery is enabled', () => {
        // discovery disabled
        mocked(useDiscovery).mockReturnValue([false, () => {}]);

        const { asFragment, rerender } = render(<TestComponent />);

        expect(asFragment()).toMatchSnapshot('discovery disabled, icon is hidden');

        // discovery enabled
        mocked(useDiscovery).mockReturnValue([true, () => {}]);

        rerender(<TestComponent />);

        expect(asFragment()).toMatchSnapshot('discovery enabled, icon is shown');
    });

    it('should show the extension info in a modal when the icon is clicked', async () => {
        const { container } = render(<TestComponent />);

        await openModal(container);

        expect(await findModalContentElement()).toMatchSnapshot('extension info modal opened');
    });

    it('should close the extension info modal when clicking the close button', async () => {
        const { container } = render(<TestComponent />);

        await openModal(container);

        const infoModalElm = await findModalElement();
        const closeModalBtn = await findByRoleInContainer(infoModalElm, 'button');
        fireEvent.click(closeModalBtn);

        expect(screen.queryByRole('dialog')).toBeNull();
    });

    it('should render a message if no schema is provided', async () => {
        const { container, findByText } = render(
            // @ts-expect-errors We pass a null value to test the runtime
            <TestComponent schemaDocuments={{ schema: null, contextSchema: mockContextSchemaDocument }} />,
        );

        await openModal(container);

        expect(await findByText('No attributes information provided for this extension point.')).toBeTruthy();
    });

    it('should render a message if no schemas are provided', async () => {
        // @ts-expect-errors We pass a null value to test the runtime
        const { container, findByText } = render(<TestComponent schemaDocuments={{ schema: mockSchemaDocument, contextSchema: null }} />);

        await openModal(container);

        expect(await findByText('No context information provided for this extension point.')).toBeTruthy();
    });
});
