import {
  Drawer,
  Popover,
  type PopoverActions,
  type PopoverOrigin,
  type PopoverProps,
} from '@mui/material'
import {
  useCallback,
  useRef,
  useSyncExternalStore,
  type CSSProperties,
  type ReactNode,
} from 'react'

type ResponsiveDrawerPosition =
  | 'top-left'
  | 'top-right'
  | 'bottom-left'
  | 'bottom-right'

/** Placement of the panel relative to its anchor (desktop popover only). */
export type ResponsiveDrawerPlacement = 'side' | 'overlay'

/**
 * Popover anchor/transform origins. `'overlay'` pins corner-to-corner on the anchor.
 */
export function resolveOrigins(
  position: ResponsiveDrawerPosition,
  placement: ResponsiveDrawerPlacement,
): { anchorOrigin: PopoverOrigin; transformOrigin: PopoverOrigin } {
  const isBottom = position.startsWith('bottom')
  const isRight = position.endsWith('right')
  const overlay = placement === 'overlay'

  return {
    anchorOrigin: {
      vertical: isBottom ? 'bottom' : 'top',
      horizontal: overlay
        ? isRight
          ? 'right'
          : 'left'
        : isRight
          ? 'left'
          : 'right',
    },
    transformOrigin: {
      vertical: isBottom ? 'bottom' : 'top',
      horizontal: isRight ? 'right' : 'left',
    },
  }
}

export function ResponsiveDrawer<
  T extends {
    collapsed: boolean
    onChangeCollapsed: (collapsed: boolean) => void
    position: ResponsiveDrawerPosition
    slotProps?: PopoverProps['slotProps']
  },
>({
  ref,
  children,
  collapsed,
  isMobile = false,
  position,
  sx,
  slotProps,
  container,
  onChangeCollapsed,
  modal = true,
  placement = 'side',
}: Required<Pick<T, 'collapsed' | 'onChangeCollapsed' | 'position'>> &
  Pick<PopoverProps, 'slotProps' | 'sx' | 'container'> & {
    ref: HTMLElement | null
    isMobile: boolean
    children: ReactNode
    /**
     * When `false`, the desktop popover is **non-modal**: no backdrop, clicks
     * pass through to the content behind it (e.g. a map stays interactive), no
     * focus trap, and neither backdrop-click nor Escape closes it (close it via
     * `onChangeCollapsed`). Defaults to `true` (standard dismissable popover).
     * Desktop-only — the mobile drawer is unaffected.
     */
    modal?: boolean
    /**
     * Desktop popover placement relative to the anchor: `'side'` (default) opens
     * beside the trigger; `'overlay'` pins corner-to-corner, sitting on top of
     * the anchor (e.g. a trigger hidden while the panel is open). Desktop-only.
     */
    placement?: ResponsiveDrawerPlacement
  }) {
  // Popover position is frozen on open — re-measure on content resize (callback ref: portal mounts after open).
  const popoverActions = useRef<PopoverActions>(null)
  const observerRef = useRef<ResizeObserver | null>(null)
  const observeContent = useCallback((el: HTMLDivElement | null) => {
    observerRef.current?.disconnect()
    observerRef.current = null
    if (el && typeof ResizeObserver !== 'undefined') {
      const observer = new ResizeObserver(() =>
        popoverActions.current?.updatePosition(),
      )
      observer.observe(el)
      observerRef.current = observer
    }
  }, [])

  // Popover Paper is auto-height — `%` max-height needs a measured px cap from `container`.
  const measurableContainer =
    container && typeof container !== 'function' ? container : null
  const subscribe = useCallback(
    (onStoreChange: () => void) => {
      if (!measurableContainer || typeof ResizeObserver === 'undefined') {
        return () => undefined
      }
      const observer = new ResizeObserver(onStoreChange)
      observer.observe(measurableContainer)
      return () => observer.disconnect()
    },
    [measurableContainer],
  )
  const getSnapshot = useCallback(
    () => measurableContainer?.clientHeight,
    [measurableContainer],
  )
  const containerHeight = useSyncExternalStore(subscribe, getSnapshot)

  if (isMobile) {
    return (
      <Drawer
        anchor='bottom'
        open={!collapsed}
        onClose={() => onChangeCollapsed(true)}
      >
        {children}
      </Drawer>
    )
  }

  const { anchorOrigin, transformOrigin } = resolveOrigins(position, placement)

  const objectSlotProps = slotProps as
    | { root?: Record<string, unknown>; paper?: { style?: CSSProperties } }
    | undefined
  // Capped Paper: disable outer scroll + flex so inner scroll regions can shrink.
  const paperMaxHeightStyle: CSSProperties | undefined =
    containerHeight != null && containerHeight > 0
      ? {
          maxHeight: containerHeight * 0.9,
          overflow: 'hidden',
          display: 'flex',
          flexDirection: 'column',
        }
      : undefined
  const contentWrapperStyle: CSSProperties | undefined = paperMaxHeightStyle
    ? {
        display: 'flex',
        flexDirection: 'column',
        flex: 1,
        minHeight: 0,
        maxHeight: '100%',
        overflow: 'hidden',
        width: '100%',
      }
    : undefined
  const mergedSlotProps: PopoverProps['slotProps'] =
    modal && !paperMaxHeightStyle
      ? slotProps
      : {
          ...(typeof slotProps === 'object' && slotProps != null
            ? slotProps
            : {}),
          ...(!modal && {
            root: {
              style: { pointerEvents: 'none' },
              ...objectSlotProps?.root,
            },
          }),
          paper: {
            ...objectSlotProps?.paper,
            style: {
              ...(!modal && { pointerEvents: 'auto' }),
              ...paperMaxHeightStyle,
              ...objectSlotProps?.paper?.style,
            },
          },
        }

  return (
    <Popover
      action={popoverActions}
      sx={sx}
      container={container}
      anchorEl={ref}
      open={!collapsed && ref != null}
      onClose={modal ? () => onChangeCollapsed(true) : undefined}
      hideBackdrop={!modal}
      disableScrollLock={!modal}
      disableEscapeKeyDown={!modal}
      disableAutoFocus={!modal}
      disableEnforceFocus={!modal}
      disableRestoreFocus={!modal}
      anchorOrigin={anchorOrigin}
      transformOrigin={transformOrigin}
      slotProps={mergedSlotProps}
    >
      <div ref={observeContent} style={contentWrapperStyle}>
        {children}
      </div>
    </Popover>
  )
}
