import {
  autoUpdate,
  flip,
  FloatingFocusManager,
  FloatingPortal,
  offset,
  type Placement,
  shift,
  size as floatingSize,
  useDismiss,
  useFloating,
  useInteractions,
  useRole,
} from '@floating-ui/react';
import { Transition } from '@headlessui/react';
import { FocusScope } from '@react-aria/focus';
import { ThemeProvider, useTheme } from '@wise/components-theming';
import { clsx } from 'clsx';
import { useState } from 'react';

import { PreventScroll } from '../../../common/preventScroll/PreventScroll';

export interface SelectInputPopoverProps {
  placement?: Placement;
  open: boolean;
  renderTrigger: (args: {
    ref: React.RefCallback<Element>;
    getInteractionProps: (customEventHandlers?: React.HTMLProps<Element>) => {
      [key: string]: unknown;
    };
  }) => React.ReactNode;
  title?: string;
  size?: 'md' | 'lg';
  padding?: 'none' | 'md';
  children?: React.ReactNode;
  onClose?: () => void;
  onCloseEnd?: () => void;
}

const FLOATING_PADDING = 16;

export function SelectInputPopover({
  placement,
  open,
  renderTrigger,
  title,
  size = 'md',
  padding = 'md',
  children,
  onClose,
  onCloseEnd,
}: SelectInputPopoverProps) {
  const { refs, floatingStyles, context } = useFloating<Element>({
    strategy: 'fixed',
    placement,
    middleware: [
      offset(8),
      flip({ padding: FLOATING_PADDING, crossAxis: false }),
      shift(),
      floatingSize({
        padding: FLOATING_PADDING,
        apply: ({ elements, rects, availableHeight }) => {
          elements.floating.style.setProperty('--max-height', `${availableHeight}px`);
          elements.floating.style.setProperty('--width', `${rects.reference.width}px`);
        },
      }),
    ],
    whileElementsMounted: autoUpdate,
    open,
    onOpenChange: (value) => {
      if (!value) {
        onClose?.();
      }
    },
  });

  const dismiss = useDismiss(context);
  const role = useRole(context);
  const { getReferenceProps, getFloatingProps } = useInteractions([role, dismiss]);

  const [floatingKey, setFloatingKey] = useState(0);

  const { theme, screenMode } = useTheme();

  return (
    <>
      {open ? <PreventScroll /> : null}
      {renderTrigger({
        ref: refs.setReference,
        getInteractionProps: getReferenceProps,
      })}

      <FloatingPortal>
        <ThemeProvider theme="personal" screenMode={theme === 'personal' ? screenMode : 'light'}>
          <Transition
            as="div"
            show={open}
            leave="transition-opacity"
            leaveTo="opacity-0"
            beforeEnter={() => {
              setFloatingKey((prev) => prev + 1);
            }}
            afterLeave={onCloseEnd}
          >
            <FocusScope>
              <FloatingFocusManager context={context}>
                <div
                  key={floatingKey} // Force inner state invalidation on open
                  ref={refs.setFloating}
                  className={clsx('np-popover-v2-container', {
                    'np-popover-v2-container--size-md': size === 'md',
                    'np-popover-v2-container--size-lg': size === 'lg',
                  })}
                  style={floatingStyles}
                  {...getFloatingProps()}
                >
                  <div
                    className={clsx('np-popover-v2', title && 'np-popover-v2--has-title', {
                      'np-popover-v2--padding-md': padding === 'md',
                    })}
                  >
                    {title ? (
                      <h2 className="np-popover-v2-title np-text-title-body">{title}</h2>
                    ) : null}
                    <div className="np-popover-v2-content np-text-body-default">{children}</div>
                  </div>
                </div>
              </FloatingFocusManager>
            </FocusScope>
          </Transition>
        </ThemeProvider>
      </FloatingPortal>
    </>
  );
}
