type Override<T, U> = Omit<U, keyof T> & T

interface TooltipProps {
  /**
   * Tooltip content receives node.
   */
  children: React.ReactNode
  /**
   * Target node that triggers the Popover.
   */
  target: React.ReactNode
  /**
   * Tooltip title, only receives string.
   */
  title?: string
  /**
   * Tooltip size, can be "small", "medium" or "large".
   */
  size?: "s" | "m" | "l"
  /**
   * Set `arrow=false` to disable arrow.
   */
  arrow?: boolean
  /**
   * Placement of the Popover with respect to target node.
   *
   * Popover supports multiple placements:
   * * `top` • `top-start` • `top-end`
   * * `bottom` • `bottom-start` • `bottom-end`
   * * `left` • `left-start` • `left-end`
   * * `right` • `right-start` • `right-end`
   */
  placement?:
    | "top"
    | "top-start"
    | "top-end"
    | "bottom"
    | "bottom-start"
    | "bottom-end"
    | "right"
    | "right-start"
    | "right-end"
    | "left"
    | "left-start"
    | "left-end"
  /**
   * Adds padding on all sides.
   */
  bezel?: boolean
}

declare function Tooltip(
  props: Override<TooltipProps, React.HTMLAttributes<HTMLElement>>
): JSX.Element

interface PopoverProps {
  /**
   * React node to render inside Popover.
   */
  children: React.ReactNode
  /**
   * Target node that triggers the Popover.
   */
  target: React.ReactNode
  /**
   * Decides the open state for Popover.
   */
  open?: boolean
  /**
   * Size of the Popover.
   */
  size?: "s" | "m" | "l"
  /**
   * Adds padding on all sides.
   */
  bezel?: boolean
  /**
   * Placement of the Popover with respect to target node.
   *
   * Popover supports multiple placements:
   *
   * * `auto` • `auto-start` • `auto-end`
   * * `top` • `top-start` • `top-end`
   * * `bottom` • `bottom-start` • `bottom-end`
   * * `left` • `left-start` • `left-end`
   * * `right` • `right-start` • `right-end`
   */
  placement?:
    | "auto"
    | "auto-start"
    | "auto-end"
    | "top"
    | "top-start"
    | "top-end"
    | "bottom"
    | "bottom-start"
    | "bottom-end"
    | "right"
    | "right-start"
    | "right-end"
    | "left"
    | "left-start"
    | "left-end"
  /**
   * The order in which focus cycles.
   *
   * * `reference` - Target element which acts as pivot for Popover.
   * * `floating` - Popover wrapper element.
   * * `content` - Content inside Popover wrapper.
   *
   * By default it's value is ['content'] - Subsequent tabs will cycle through the tabbable contents of the Popover content.
   *
   * If the value is set to ['floating', 'content'] - Includes floating element in focus cycle.
   *
   * If the value is set to ['reference', 'content'] - Includes reference element in focus cycle.
   *
   * * If the value is set to ['reference', 'floating', 'content'] - tabs will cycle through the reference, floating & tabbable contents of the Popover content.
   *
   */
  focusOrder?: ("reference" | "floating" | "content")[]
  /**
   * Element to initially focus when Popover is opened. Can be either a number or a ref.
   * Number is decided by the tabbable index as specified by the focusOrder.
   */
  initialFocus?: (...args: any[]) =>
    | any
    | number
    | {
        current?: any
      }
  /**
   * When Popover is added to a container with `position: fixed` and/or `overflow: hidden`,
   * it creates a containing block with a stacking context which the Popover can't skip.
   * In such scenarios, enabling `outOfFlow` will remove Popover from its parent's flow.
   */
  outOfFlow?: boolean
  /**
   * Flips the position of the floating element if there is space constraint
   * in order to make it visible in viewport.
   */
  flip?: boolean
  /**
   * Enables the Popover to stretch and fill the width of its container. By default, the Popover matches the width of its content.
   *
   * Strech works only in a relatively positioned container. Refer to the example below.
   */
  stretch?: boolean
  /**
   * Shifts the Popover horizontally to avoid the content from overflowing the viewport.
   */
  shift?: boolean
  /**
   * Add focus trap in the content, outside content cannot be accessed if enabled.
   */
  focusTrap?: boolean
  /**
   * adds a low spacing between target and Popover.
   */
  lowOffset?: boolean
  /**
   * adds a medium spacing between target and Popover.
   */
  mediumOffset?: boolean
  /**
   * adds a high spacing between target and Popover.
   */
  highOffset?: boolean
  /**
   * removes spacing between target and Popover.
   */
  noOffset?: boolean
  /**
   * callback when open state is changed.
   */
  onOpenChange?: (...args: any[]) => any
  /**
   * determines if focus should be returned to the reference element.
   */
  returnFocus?: boolean
}

declare function Popover(
  props: Override<PopoverProps, React.HTMLAttributes<HTMLElement>>
): JSX.Element

interface FocusManagerProps {
  /**
   * React node to render inside Base Popover.
   */
  children: React.ReactNode
  /**
   * context object used to focus on the floating element, pass the `popoverContext` returned by the `usePopover` hook.
   */
  popoverContext: object
  /**
   * The order in which focus cycles.
   *
   * * `reference` - Target element which acts as pivot for Popover.
   * * `floating` - Popover wrapper element.
   * * `content` - Content inside Popover wrapper.
   *
   * By default it's value is ['content'] - Subsequent tabs will cycle through the tabbable contents of the Popover content.
   *
   * If the value is set to ['floating', 'content'] - Includes floating element in focus cycle.
   *
   * If the value is set to ['reference', 'content'] - Includes reference element in focus cycle.
   *
   * * If the value is set to ['reference', 'floating', 'content'] - tabs will cycle through the reference, floating & tabbable contents of the Popover content.
   *
   */
  focusOrder?: ("reference" | "floating" | "content")[]
  /**
   * Add focus trap in the content, outside content cannot be accessed if enabled.
   */
  focusTrap?: boolean
  /**
   * Element to initially focus when Popover is opened. Can be either a number or a ref.
   * Number is decided by the tabbable index as specified by the focusOrder.
   */
  initialFocus?: (...args: any[]) =>
    | any
    | number
    | {
        current?: any
      }
  /**
   * determines if focus should be returned to the reference element.
   */
  returnFocus?: boolean
}

declare function FocusManager(
  props: Override<FocusManagerProps, React.HTMLAttributes<HTMLElement>>
): JSX.Element

interface BasePopoverProps {
  /**
   * React node to render inside Base Popover.
   */
  children: React.ReactNode
  /**
   * Indicates the open state for Popover.
   */
  open?: boolean
  /**
   * Size of the Popover.
   */
  size?: "s" | "m" | "l"
  /**
   * Adds padding on all sides.
   */
  bezel?: boolean
  /**
   * Enables the Popover to stretch and fill the width of its container. By default, the Popover matches the width of its content.
   */
  stretch?: boolean
  /**
   * Adds styles to position the Popover relative to the target element.
   */
  anchorStyles?: object
  /**
   * Set to true to render BasePopover with inverse background.
   */
  inverse?: boolean
}

declare function BasePopover(
  props: Override<BasePopoverProps, React.HTMLAttributes<HTMLElement>>
): JSX.Element

interface UsePopoverProp {
  /* Indicates whether the Popover is open. */
  open: boolean
  /* Placement of the Popover relative to the target. */
  placement?: string
  /* If true, removes the Popover from its parent's flow. */
  outOfFlow?: boolean
  /* Enables flipping the Popover position if space is constrained. */
  flip?: boolean
  /* Enables shifting the Popover to avoid viewport overflow. */
  shift?: boolean
  /* Size of the Popover ("s", "m", "l"). */
  size?: string
  /* Adds padding around the Popover. */
  bezel?: boolean
  /* Enables the Popover to stretch to fill its container. */
  stretch?: boolean
  /* Order in which focus cycles ("reference", "floating", "content"). */
  focusOrder?: string[]
  /* Determines if focus should return to the reference element. */
  returnFocus?: boolean
  /* Element to initially focus when the Popover opens. */
  initialFocus?: number | object
  /* Enables focus trapping within the Popover. */
  focusTrap?: boolean
  /* Removes spacing between the target and Popover. */
  noOffset?: boolean
  /* Adds low spacing between the target and Popover. */
  lowOffset?: boolean
  /* Adds medium spacing between the target and Popover. */
  mediumOffset?: boolean
  /* Adds high spacing between the target and Popover. */
  highOffset?: boolean
  /* Callback triggered when the open state changes. */
  onOpenChange?: Function
  /* Enables hover interactions for the Popover. */
  isHover?: boolean
  /* Enables click interactions for the Popover. */
  isClick?: boolean
  /* Ref for the arrow element. */
  arrowRef?: object | null
  /* Additional props are spread to the underlying floating-ui hook. */
  [key: string]: any
}

interface UsePopoverReturn {
  /* Returns props for the target element. */
  getTargetProps: Function
  /* Returns props for the Popover element. */
  getPopoverProps: Function
  /* Returns props for the FocusManager component. */
  getFocusProps: Function
}

declare function usePopover(props: UsePopoverProp): UsePopoverReturn

export { BasePopover, type BasePopoverProps, FocusManager, type FocusManagerProps, Popover, type PopoverProps, Tooltip, type TooltipProps, usePopover };
