import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import GreyRowBox from '@/components/elements/GreyRowBox';

const cx = (...parts: Array<string | false | null | undefined>) => parts.filter(Boolean).join(' ');

export interface FloatingMenuPosition {
    top?: number;
    left?: number;
    right?: number;
    bottom?: number;
}

const samePosition = (a: FloatingMenuPosition, b: FloatingMenuPosition) => (
    a.top === b.top && a.left === b.left && a.right === b.right && a.bottom === b.bottom
);

interface FloatingMenuProps {
    position: FloatingMenuPosition;
    title?: string;
    widthClassName?: string;
    minWidth?: number | string;
    maxWidth?: number | string;
    onClose: () => void;
    children: React.ReactNode;
}

interface FloatingMenuItemProps {
    label: string;
    description?: string;
    icon?: React.ReactNode;
    danger?: boolean;
    mutedDanger?: boolean;
    disabled?: boolean;
    onClick: () => void;
}

export const getFloatingMenuPosition = (
    anchor: HTMLElement,
    align: 'left' | 'right' = 'left',
    menuWidth = 256
): FloatingMenuPosition => {
    const rect = anchor.getBoundingClientRect();
    const gutter = 12;
    const bottom = Math.max(gutter, window.innerHeight - rect.top + 8);
    const left = Math.min(window.innerWidth - menuWidth - gutter, Math.max(gutter, rect.left));
    const right = Math.max(gutter, window.innerWidth - rect.right);

    return align === 'right' ? { bottom, right } : { bottom, left };
};

export const getPointFloatingMenuPosition = (x: number, y: number): FloatingMenuPosition => ({ top: y, left: x });

export const FloatingMenuDivider = () => <div className='my-1 h-px bg-gray-700' />;

export const FloatingMenuItem: React.FC<FloatingMenuItemProps> = ({
    label,
    icon,
    danger,
    mutedDanger,
    disabled,
    onClick,
}) => (
    <button
        type='button'
        disabled={disabled}
        onClick={(e) => {
            e.preventDefault();
            if (!disabled) onClick();
        }}
        className={cx(
            'group flex w-full items-center gap-2 px-3 py-2 text-left text-xs text-neutral-300 transition-colors hover:bg-neutral-800 hover:text-neutral-100 disabled:cursor-not-allowed disabled:opacity-40',
            danger
                ? 'font-semibold text-red-300 hover:text-red-200'
                : mutedDanger
                    ? 'text-neutral-300 hover:text-red-200'
                    : undefined
        )}
    >
        {icon && (
            <span
                className={cx(
                    'flex h-4 w-4 shrink-0 items-center justify-center text-neutral-400 group-hover:text-current',
                    danger && 'text-red-400',
                    mutedDanger && 'group-hover:text-red-300'
                )}
            >
                {icon}
            </span>
        )}
        <span className='min-w-0 flex-1 truncate'>{label}</span>
    </button>
);

const FloatingMenu: React.FC<FloatingMenuProps> = ({
    position,
    title,
    widthClassName = 'w-64',
    minWidth,
    maxWidth,
    onClose,
    children,
}) => {
    const menuRef = useRef<HTMLDivElement>(null);
    const [adjustedPosition, setAdjustedPosition] = useState<FloatingMenuPosition>(position);

    useEffect(() => {
        const handleEscape = (event: KeyboardEvent) => {
            if (event.key === 'Escape') onClose();
        };

        document.addEventListener('keydown', handleEscape);
        return () => document.removeEventListener('keydown', handleEscape);
    }, [onClose]);

    useLayoutEffect(() => {
        const menu = menuRef.current;
        if (!menu) return;

        const rect = menu.getBoundingClientRect();
        const gutter = 10;
        const next = { ...position };

        if (typeof next.left === 'number' && rect.right > window.innerWidth - gutter) {
            next.left = Math.max(gutter, window.innerWidth - rect.width - gutter);
        }

        if (typeof next.top === 'number' && rect.bottom > window.innerHeight - gutter) {
            next.top = Math.max(gutter, window.innerHeight - rect.height - gutter);
        }

        if (typeof next.left === 'number' && next.left < gutter) next.left = gutter;
        if (typeof next.top === 'number' && next.top < gutter) next.top = gutter;

        setAdjustedPosition((previous) => (samePosition(previous, next) ? previous : next));
    }, [position]);

    if (typeof document === 'undefined') return null;

    return createPortal(
        <>
            <div
                className='fixed inset-0 z-[60]'
                onClick={onClose}
                onContextMenu={(e) => {
                    e.preventDefault();
                    onClose();
                }}
            />
            <div
                ref={menuRef}
                className={cx('betterfiles-scope fixed z-[70]', widthClassName)}
                onContextMenu={(e) => {
                    e.preventDefault();
                    e.stopPropagation();
                }}
                style={{
                    ...adjustedPosition,
                    maxWidth,
                    willChange: 'left, top, right, bottom',
                }}
            >
                <GreyRowBox
                    className='shadow-xl overflow-hidden !p-0 flex-col !items-stretch'
                    $hoverable={false}
                    style={{
                        minWidth: minWidth ?? 220,
                        maxWidth,
                        filter: 'none',
                        backdropFilter: 'none',
                        WebkitBackdropFilter: 'none',
                    }}
                >
                    {title && (
                        <div className='border-b border-gray-700 px-3 py-2 text-xs font-medium text-neutral-400'>
                            {title}
                        </div>
                    )}
                    {children}
                </GreyRowBox>
            </div>
        </>,
        document.body
    );
};

export default FloatingMenu;
