import React, {
    ReactNode,
    useEffect,
    useLayoutEffect,
    useRef,
    useState,
} from 'react';
import './PopupColumn.scss';
import ListColumn from './ListColumn';
import close from '../../../../assets/images/close.png';

type PopupColumnProps = {
    children: ReactNode[];
    flex?: string;
    right?: boolean;
    disabled?: boolean;
    className?: string;
    empty?: boolean;
    label?: string;
    style?: React.CSSProperties;

    /** Optional: control popup externally */
    isOpen?: boolean;
    onToggle?: (open: boolean) => void;
    closePopup?: () => void;
};

type DropdownPosition = {
    top: number;
    left: number;
};

const VIEWPORT_MARGIN = 15;

const PopupColumn = ({
                         children,
                         label,
                         style,
                         flex = '0 0 25px',
                         right = false,
                         disabled = false,
                         empty = false,
                         className = '',
                         isOpen: controlledOpen,
                         onToggle,
                         closePopup,
                     }: PopupColumnProps) => {
    const [internalOpen, setInternalOpen] = useState(false);

    const [dropdownPosition, setDropdownPosition] =
        useState<DropdownPosition | null>(null);

    const popupRef = useRef<HTMLDivElement>(null);
    const dropdownRef = useRef<HTMLDivElement>(null);

    const isControlled = controlledOpen !== undefined;
    const isOpen = isControlled ? controlledOpen : internalOpen;

    const setIsOpen = (val: boolean) => {
        if (isControlled) {
            onToggle?.(val);

            if (!val) {
                closePopup?.();
            }
        } else {
            setInternalOpen(val);
        }
    };

    /**
     * Calculate the dropdown position.
     *
     * The dropdown remains absolutely positioned relative
     * to its current parent, but we calculate where that
     * parent-relative position needs to be based on the
     * actual viewport position.
     */
    const calculatePosition = () => {
        const toggle = popupRef.current;
        const dropdown = dropdownRef.current;

        if (!toggle || !dropdown) {
            return;
        }

        const toggleRect = toggle.getBoundingClientRect();
        const dropdownRect = dropdown.getBoundingClientRect();

        /*
         * The dropdown is absolutely positioned relative
         * to .popup-column.
         *
         * Get the parent's viewport coordinates so we can
         * convert the desired viewport position back into
         * parent-relative coordinates.
         */
        const parent = toggle.parentElement;

        if (!parent) {
            return;
        }

        const parentRect = parent.getBoundingClientRect();

        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        /*
         * Desired position in viewport coordinates.
         *
         * This preserves your original:
         *
         * left: -15px
         * top: 0px
         */
        let viewportLeft = toggleRect.left - 15;
        let viewportTop = toggleRect.top;

        /*
         * Keep the dropdown inside the viewport horizontally.
         */

        if (
            viewportLeft + dropdownRect.width >
            viewportWidth - VIEWPORT_MARGIN
        ) {
            viewportLeft =
                viewportWidth -
                dropdownRect.width -
                VIEWPORT_MARGIN;
        }

        if (viewportLeft < VIEWPORT_MARGIN) {
            viewportLeft = VIEWPORT_MARGIN;
        }

        /*
         * Keep the dropdown inside the viewport vertically.
         */

        if (
            viewportTop + dropdownRect.height >
            viewportHeight - VIEWPORT_MARGIN
        ) {
            viewportTop =
                viewportHeight -
                dropdownRect.height -
                VIEWPORT_MARGIN;
        }

        if (viewportTop < VIEWPORT_MARGIN) {
            viewportTop = VIEWPORT_MARGIN;
        }

        /*
         * Convert viewport coordinates into coordinates
         * relative to .popup-column.
         */
        const left = viewportLeft - parentRect.left;
        const top = viewportTop - parentRect.top;

        setDropdownPosition({
            left,
            top,
        });
    };

    /**
     * Measure after the dropdown has actually rendered.
     */
    useLayoutEffect(() => {
        if (!isOpen) {
            setDropdownPosition(null);
            return;
        }

        calculatePosition();
    }, [isOpen]);

    /**
     * Recalculate on resize and scroll.
     */
    useEffect(() => {
        if (!isOpen) {
            return;
        }

        const handlePositionChange = () => {
            calculatePosition();
        };

        window.addEventListener('resize', handlePositionChange);

        /*
         * `true` is important because the page may be scrolling
         * inside a container rather than the window itself.
         */
        window.addEventListener(
            'scroll',
            handlePositionChange,
            true
        );

        return () => {
            window.removeEventListener(
                'resize',
                handlePositionChange
            );

            window.removeEventListener(
                'scroll',
                handlePositionChange,
                true
            );
        };
    }, [isOpen]);

    /**
     * Close when clicking outside the toggle or dropdown.
     */
    useEffect(() => {
        const handleClickOutside = (e: MouseEvent) => {
            if (!isOpen) {
                return;
            }

            const target = e.target as Node;

            if (popupRef.current?.contains(target)) {
                return;
            }

            if (dropdownRef.current?.contains(target)) {
                return;
            }

            setIsOpen(false);
        };

        window.addEventListener('click', handleClickOutside);

        return () => {
            window.removeEventListener(
                'click',
                handleClickOutside
            );
        };
    }, [isOpen]);

    return (
        <ListColumn
            label={label}
            right={right}
            flex={flex}
        >
            <div
                className={`popup-column ${isOpen ? 'open' : ''} ${
    right ? 'right' : 'column-border'
} ${disabled ? 'disabled' : ''} ${
    empty ? 'empty' : ''
} ${className}`}
            >
                <span className="label">
                    {label}
                </span>

                <div
                    ref={popupRef}
                    className="popup-column--toggle w-100"
                    onClick={(e) => {
                        e.stopPropagation();

                        // @ts-ignore
                        e.nativeEvent.source = popupRef;

                        setIsOpen(!isOpen);
                    }}
                >
                    {right && label}

                    {children[0]}
                </div>

                {isOpen && (
                    <div
                        ref={dropdownRef}
                        style={{
                            ...style,

                            ...(dropdownPosition
                                ? {
                                    top: `${dropdownPosition.top}px`,
                                    left: `${dropdownPosition.left}px`,
                                    visibility: 'visible',
                                }
                                : {
                                    visibility: 'hidden',
                                }),
                        }}
                        className="popup-column--dropdown"
                        onClick={(e) =>
                            e.stopPropagation()
                        }
                    >
                        <img
                            src={close}
                            alt=""
                            className="close-btn"
                            onClick={() =>
                                setIsOpen(false)
                            }
                        />

                        {children[1]}
                    </div>
                )}
            </div>
        </ListColumn>
    );
};

export default PopupColumn;
