import React, { useEffect, useRef } from 'react';
import {
    Button,
    LoadingButton,
    OverlayTrigger,
    Popover
} from '@orchard/frontend-react-components';
import { formatMessage } from '@orchard/frontend-localization';
import messages from './i18n';

const CLASS_NAME = 'PopConfirm';

type Props = {
    /**
     * The target element that the PopConfirm will target
     */
    children?: React.ReactNode;

    /**
     * Define html ID attribute
     */
    id: string;

    /**
     * The message to be shown in the content body
     */
    message?: React.ReactNode | string;

    /**
     * The className used by the Overlay
     */
    overlayClassName?: string;

    /**
     * Specify whether the overlay should trigger onHide when the user clicks outside the overlay
     */
    rootClose?: boolean;

    /**
     * Sets the direction the PopConfirm is positioned towards.
     */
    tooltipPlacement?: React.ComponentProps<typeof OverlayTrigger>['placement'];

    /**
     * Trigger lets you override when it is triggered, so "hover" / "focus" / "click"
     */
    trigger?: React.ComponentProps<typeof OverlayTrigger>['trigger'];

    /**
     * The button text for the cancel button
     */
    cancelLabel?: string;

    /**
     * The button text for the confirm button
     */
    confirmLabel?: string;

    /**
     * The theme of the confirm button
     *
     * @see See [React Bootstrap's Button](https://react-bootstrap.github.io/components/buttons/)
     */
    confirmVariant?: React.ComponentProps<typeof Button>['variant'],

    /**
     * The function to trigger when the cancel button is clicked
     */
    onCancel(): void;

    /**
     * The function to trigger when the confirm button is clicked
     */
    onConfirm(): void;

    /**
     * Whether the confirm button is a loading button or static
     */
    confirmButtonType?: 'default' | 'loading';

    /**
     * If the confirm button is a loading button, this specifies the loading state
     */
    isLoading?: boolean;

    /**
     * If there is an error to display
     */
    hasError?: boolean;

    /**
     * The error message to display if there is an error
     */
    errorMessage?: string;

    /**
     * Whether to show the overlay. If defined, the default behavior is overridden for the popover
     * and the display will need to be explicitly managed as a controlled prop
     */
    show?: boolean;

    /**
     * Optional function that is triggered whenever the user clicks outside of the component
     */
    onClickOutside?(): void
};

/**
 * The Popover Confirmation component. This can be used to initiate confirmation dialogs using a bootstrap popover.
 *
 * Wrap this component around whatever child component you wish to trigger the popover.
 */
const PopConfirm: React.FC<Props> = ({
    children,
    id,
    message,
    overlayClassName = CLASS_NAME,
    rootClose = false,
    tooltipPlacement = 'top',
    trigger = 'click',
    cancelLabel = formatMessage(messages.cancel),
    confirmLabel,
    confirmVariant = 'primary',
    onCancel,
    onConfirm,
    confirmButtonType = 'default',
    isLoading = false,
    hasError,
    errorMessage = formatMessage(messages.genericError),
    show,
    onClickOutside
}) => {
    const overlayRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const handleClickOutside = (event: MouseEvent) => {
            // If onClickOutside is specified, trigger it when the event listener event is
            // triggered outside of the pop-confirm element
            if (
                onClickOutside
                && overlayRef.current
                && event.target
                && !overlayRef.current.contains(event.target as Node)
                && !isLoading
            )
                onClickOutside();
        };
        if (onClickOutside)
            document.addEventListener('mousedown', handleClickOutside);
        return () => {
            if (onClickOutside)
                document.removeEventListener('mousedown', handleClickOutside);
        };
    }, [onClickOutside, overlayRef, isLoading]);

    const renderSaveButton = () => {
        if (confirmButtonType === 'loading')
            return (
                <LoadingButton
                    className={ `${CLASS_NAME}-confirm-button ${overlayClassName}` }
                    type="button"
                    size="sm"
                    variant={ confirmVariant }
                    onClick={ onConfirm }
                    loading={ isLoading }
                    aria-label={ confirmLabel }
                    data-testid={ `${id}-confirm-button` }
                >
                    { confirmLabel }
                </LoadingButton>
            );
        return (
            <Button
                className={ `${CLASS_NAME}-confirm-button ${overlayClassName}` }
                type="button"
                size="sm"
                variant={ confirmVariant }
                onClick={ onConfirm }
                disabled={ isLoading }
                aria-label={ confirmLabel }
                data-testid={ `${id}-confirm-button` }
            >
                { confirmLabel }
            </Button>
        );
    };
    const renderOverlayContent = () => (
        <Popover
            id={ id }
            className={ `${CLASS_NAME} ${overlayClassName}` }
            data-testid={ `${id}-overlay` }
        >
            <div
                className={ `${CLASS_NAME}-content ${overlayClassName}` }
                ref={ overlayRef }
            >
                <div className={ `${CLASS_NAME}-message ${overlayClassName}` }>
                    { message }
                </div>
                <div className={ `${CLASS_NAME}-footer ${overlayClassName}` }>
                    { hasError && (
                        <div className={ `${CLASS_NAME}-error ${overlayClassName}` }>
                            { errorMessage }
                        </div>
                    ) }
                    <div className={ `${CLASS_NAME}-buttons ${overlayClassName}` }>
                        <Button
                            className={ `${CLASS_NAME}-cancel-button ${overlayClassName}` }
                            type="button"
                            size="sm"
                            onClick={ onCancel }
                            aria-label={ cancelLabel }
                            data-testid={ `${id}-cancel-button` }
                            disabled={ isLoading }
                        >
                            { cancelLabel }
                        </Button>
                        { renderSaveButton() }
                    </div>
                </div>
            </div>
        </Popover>
    );

    return (
        // @ts-ignore
        // The show prop is exposed and usable on the OverlayTrigger, but the version
        // of React-Bootstrap in FRC currently has the type set to never.
        <OverlayTrigger
            overlay={ renderOverlayContent() }
            placement={ tooltipPlacement }
            rootClose={ rootClose }
            trigger={ trigger }
            { ...(show !== undefined ? { show } : {}) }
        >
            { children }
        </OverlayTrigger>
    );
};

export default PopConfirm;
