# Popover — accessible headless popover with anchor positioning
#
# Uses the native Popover API (top-layer + light-dismiss) and CSS anchor
# positioning. Exposes $open, $placement on content. Ships zero CSS.
#
# Usage:
#   Popover placement: "bottom-start"
#     button $trigger: true, "Click me"
#     div $content: true
#       p "Popover content"

export Popover = component
  @placement:: "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "right" := "bottom-start"
  @offset:: number := 4
  @disabled:: boolean := false
  @openOnHover:: boolean := false
  @hoverDelay:: number := 300
  @hoverCloseDelay:: number := 200

  open := false
  _ready := false
  _hoverTimer := null
  _hoverCloseTimer := null
  _id =! "pop-#{Math.random().toString(36).slice(2, 8)}"

  _applyPlacement: ->
    trigger = @_content?.querySelector('[data-trigger]')
    floating = @_content?.querySelector('[data-content]')
    [side, align] = @placement.split('-')
    align ??= 'center'
    ARIA.position trigger, floating, placement: "#{side} #{align}", offset: @offset

  mounted: ->
    _ready = true
    trigger = @_content?.querySelector('[data-trigger]')
    floating = @_content?.querySelector('[data-content]')
    if trigger and floating
      floating.id = _id
      floating.setAttribute 'popover', 'auto'
      trigger.setAttribute 'aria-expanded', false
      trigger.setAttribute 'aria-haspopup', 'dialog'
      trigger.setAttribute 'aria-controls', _id
      trigger.addEventListener 'click', =>
        return if @disabled
        open = not open
      trigger.addEventListener 'keydown', (e) =>
        if e.key in ['Enter', ' ', 'ArrowDown']
          e.preventDefault()
          return if @disabled
          open = not open
      if @openOnHover
        trigger.addEventListener 'mouseenter', =>
          clearTimeout _hoverCloseTimer if _hoverCloseTimer
          _hoverTimer = setTimeout (=> open = true), @hoverDelay
        trigger.addEventListener 'mouseleave', =>
          clearTimeout _hoverTimer if _hoverTimer
          _hoverCloseTimer = setTimeout (=> open = false), @hoverCloseDelay
      @_applyPlacement()

  toggle: ->
    return if @disabled
    open = not open

  openPopover: ->
    open = true

  close: ->
    open = false

  ~>
    return unless _ready
    trigger = @_content?.querySelector('[data-trigger]')
    floating = @_content?.querySelector('[data-content]')
    if trigger
      trigger.setAttribute 'aria-expanded', !!open
    if floating
      floating.setAttribute 'data-placement', @placement
      if open then floating.setAttribute 'data-open', '' else floating.removeAttribute 'data-open'
      ARIA.wireAria floating, _id
      @_applyPlacement()

  ~>
    return unless _ready
    ARIA.bindPopover open, (=> @_content?.querySelector('[data-content]')), ((isOpen) => open = isOpen), (=> @_content?.querySelector('[data-trigger]'))

  onKeydown: (e) ->
    if e.key is 'Escape' and open
      e.preventDefault()
      @close()

  render
    div ref: "_content"
      slot
