import type { CSSProperties, ReactElement, ComponentProps } from 'react';
import React, { useEffect, useRef } from 'react';
import cx from 'classnames';
import { BsOverlayTrigger, BsTooltip } from '../bootstrap';
import { usePageContext } from '../pageContext';
import type { ComponentBaseWithChildrenProps, DatadogContentPrivacy } from '../../types';

const CLASSNAME = 'Tooltip';

type Placement = React.ComponentProps<typeof BsTooltip>['placement'];
type Container = React.ComponentProps<typeof BsOverlayTrigger>['container'];

export interface TooltipProps extends ComponentBaseWithChildrenProps {
    /**
     * Unique identifier of the tooltip
     */
    id: string;

    /**
     * The container used to render the tooltip (only for rare custom implementation)
     */
    container?: Container;

    /**
     * The placement of the tooltip message in relation to the triggering child
     */
    placement?: Placement;

    /**
     * The text shown when the triggering child is hovered (or clicked)
     */
    message: string | ReactElement;

    /**
     * Set manually the visibility of the tooltip.
     */
    show?: boolean;

    /**
     * Styles applied to the trigger element.
     */
    style?: CSSProperties;

    /**
     * Sets the content privacy.
     */
    contentPrivacy?: DatadogContentPrivacy;

    /**
     * Sets the "skid" and offset of the tooltip.
     * Defaults to [0, 5]
     */
    offset?: [number, number] | null;

    /**
     * Changes to this prop triggers repositioning of the tooltip.
     * Can be used in cases where the tooltip trigger changes position.
     */
    popperUpdateTrigger?: unknown;

    /**
     * Advanced configuration options for popper.js, the library used to position tooltips.
     * See https://popper.js.org/docs/v2/constructors/#options
     */
    popperConfig?: ComponentProps<typeof BsOverlayTrigger>['popperConfig'];

    /**
     * Sets the max width of the tooltip.
     */
    maxWidth?: number;

    /**
     * Sets the text alignment of the tooltip content.
     */
    textAlign?: React.CSSProperties['textAlign'];

    /**
     * Controls whether the tooltip can automatically flip to the opposite placement (e.g., top to bottom) if there's not enough space.
     */
    flip?: boolean;

    /**
     * Additional class name for the tooltip overlay container.
     */
    overlayClassName?: string;
}

/**
 * Tooltips are floating containers attached to a specific UI element and triggered on hover. They are used to contextually help describe the term or action they're attached to.
 *
 * @type atom
 * @status live
 * @tags overlays
 */
export const Tooltip = ({
    id,
    className,
    container,
    placement = 'top',
    testId = CLASSNAME,
    message,
    show,
    children,
    style,
    contentPrivacy,
    offset = [0, 5],
    popperUpdateTrigger,
    popperConfig,
    maxWidth,
    flip,
    textAlign,
    overlayClassName,
}: TooltipProps) => {
    const page = usePageContext();
    const popperRef = useRef<{ scheduleUpdate?: () => void } | null>(null);

    // To allow repositioning tooltip based on prop changes
    useEffect(() => {
        popperRef.current?.scheduleUpdate?.();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [popperUpdateTrigger]);

    return (
        <BsOverlayTrigger
            container={container}
            trigger={['hover', 'focus']}
            placement={placement}
            popperConfig={
                popperConfig ?? {
                    modifiers: [
                        {
                            name: 'offset',
                            enabled: Boolean(offset),
                            options: {
                                offset,
                            },
                        },
                    ],
                }
            }
            overlay={({ popper, ...props }) => {
                popperRef.current = popper;

                return (
                    <div
                        className={cx('TooltipOverlay', page.className, overlayClassName)}
                        style={{
                            ...(maxWidth
                                ? ({ '--tooltip-width': `${maxWidth}px` } as React.CSSProperties)
                                : {}),
                            ...(textAlign
                                ? ({ '--tooltip-text-align': textAlign } as React.CSSProperties)
                                : {}),
                        }}
                    >
                        <BsTooltip
                            {...props}
                            className={cx(props.className, `${id}-tooltip-message`)}
                            id={`${id}-TooltipMessage`}
                            data-dd-privacy={contentPrivacy}
                        >
                            {message}
                        </BsTooltip>
                    </div>
                );
            }}
            show={show}
            rootClose={show === undefined}
            flip={flip}
        >
            {({ ref, ...triggerHandler }) => (
                <div
                    id={id}
                    data-testid={testId}
                    className={cx(CLASSNAME, className)}
                    ref={ref}
                    style={style}
                    {...triggerHandler}
                >
                    {children}
                </div>
            )}
        </BsOverlayTrigger>
    );
};
