import Button from '@atlaskit/button/new';
import type { KeyboardOrMouseEvent } from '@atlaskit/modal-dialog';
import ModalDialog, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
import { ModalExtension } from '@atlassian/clientside-extensions';
import type { FunctionComponent, ReactNode } from 'react';
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ButtonHandler } from './ButtonHandler';
import type { OnClickHandler } from './types';

const ModalInnerBody: FunctionComponent<{ modalApi: ModalExtension.Api }> = ({ modalApi }) => {
    const ref = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const node = ref.current;

        if (node) {
            modalApi.getRenderCallback()(node);
        }

        return () => {
            if (node) {
                modalApi.getCleanupCallback()(node);
            }
        };
    }, [modalApi]);

    return <div ref={ref} />;
};

export interface ModalHandlerProps {
    render: ModalExtension.ModalRenderExtension;
    isOpen: boolean;
    onClose: () => void;
    __withoutTransition?: boolean;
    __disableFocusLock?: boolean;
}

export const ModalHandler: FunctionComponent<ModalHandlerProps> = ({
    render,
    isOpen,
    onClose,
    __withoutTransition = false,
    __disableFocusLock = false,
}) => {
    const [actions, setActions] = useState<ModalExtension.ModalAction[]>([]);
    const modalApi = useMemo(() => {
        const api = new ModalExtension.Api(onClose, setActions);
        render(api);
        return api;
    }, [render, onClose]);

    const Wrapper = __withoutTransition ? Fragment : ModalTransition;

    // Here will be dragons. That's a fishy solution for programmatically disabling the focus lock of the Atlaskit Modal component.
    // When the stackIndex > 0 this will disable the focus lock of the Modal. That's quite naive implementation since we are abusing
    // the Atlaskit stacking mechanism.
    const stackIndex = __disableFocusLock ? 1 : 0;

    const onCloseHandler = useCallback(
        (e: KeyboardOrMouseEvent) => {
            const closeResult = modalApi.getOnCloseCallback()(e);
            // prevent closing of modal if "closeRequest" is "false"
            if (closeResult !== false) {
                onClose();
            }
        },
        [modalApi, onClose],
    );

    const actionsAsButtons: JSX.Element[] = useMemo<JSX.Element[]>(
        () =>
            actions.map(({ testId, isLoading, text, onClick, isDisabled }, index) => (
                // eslint-disable-next-line react/no-array-index-key
                <Button key={index} isLoading={isLoading} testId={testId} isDisabled={isDisabled} onClick={onClick}>
                    {text}
                </Button>
            )),
        [actions],
    );

    return (
        <Wrapper>
            {isOpen && (
                <ModalDialog testId="client-extensions-modal" width={modalApi.getWidth()} onClose={onCloseHandler} stackIndex={stackIndex}>
                    <ModalHeader hasCloseButton>
                        <ModalTitle appearance={modalApi.getAppearance()}>{modalApi.getTitle()}</ModalTitle>
                    </ModalHeader>
                    <ModalBody>
                        <ModalInnerBody modalApi={modalApi} />
                    </ModalBody>
                    <ModalFooter>{actionsAsButtons}</ModalFooter>
                </ModalDialog>
            )}
        </Wrapper>
    );
};

export interface ModalWithActionHandlerProps extends Omit<OnClickHandler, 'onAction'> {
    children?: ReactNode;
    render: ModalExtension.ModalRenderExtension;
    __withoutTransition?: boolean;
    __disableFocusLock?: boolean;
}

export const ModalWithActionHandler: FunctionComponent<ModalWithActionHandlerProps> = ({
    // Action Handler Properties
    appearance,
    children,
    disabled,
    hidden,
    iconAfter,
    iconBefore,
    spacing,

    // Modal Handler Properties
    render,
    __withoutTransition,
    __disableFocusLock,
}) => {
    const [isOpen, setIsOpen] = useState(false);

    const openModal = useCallback(() => {
        setIsOpen(true);
    }, []);

    const closeModal = useCallback(() => {
        setIsOpen(false);
    }, []);

    if (hidden) {
        return null;
    }

    return (
        <>
            <ButtonHandler
                appearance={appearance}
                iconAfter={iconAfter}
                iconBefore={iconBefore}
                disabled={disabled}
                hidden={hidden}
                onAction={openModal}
                spacing={spacing}
            >
                {children}
            </ButtonHandler>
            <ModalHandler
                isOpen={isOpen}
                onClose={closeModal}
                render={render}
                __disableFocusLock={__disableFocusLock}
                __withoutTransition={__withoutTransition}
            />
        </>
    );
};
