# Tooltip — accessible headless tooltip with delay and positioning
#
# Shows on hover/focus with configurable delay. Uses aria-describedby.
# Exposes $open, $entering, $exiting. Ships zero CSS.
#
# Usage:
#   Tooltip text: "Helpful info", placement: "top"
#     button "Hover me"

lastCloseTime = 0
GROUP_TIMEOUT = 400

export Tooltip = component
  @text := ''
  @placement := 'top'
  @delay := 300
  @offset := 6
  @hoverable := false

  open := false
  entering := false
  exiting := false
  _showTimer := null
  _hideTimer := null
  _id =! "tip-#{Math.random().toString(36).slice(2, 8)}"

  show: ->
    clearTimeout _hideTimer if _hideTimer
    delay = if (Date.now() - lastCloseTime) < GROUP_TIMEOUT then 0 else @delay
    _showTimer = setTimeout =>
      open = true
      entering = true
      setTimeout =>
        entering = false
        @_position()
      , 0
    , delay

  hide: ->
    clearTimeout _showTimer if _showTimer
    exiting = true
    _hideTimer = setTimeout =>
      open = false
      exiting = false
      lastCloseTime = Date.now()
    , 150

  _cancelHide: ->
    clearTimeout _hideTimer if _hideTimer
    exiting = false

  _position: ->
    return unless @_trigger and @_tip
    tr = @_trigger.getBoundingClientRect()
    fl = @_tip.getBoundingClientRect()
    [side, align] = @placement.split('-')
    align ?= 'center'
    gap = @offset

    x = switch side
      when 'bottom', 'top'
        switch align
          when 'start' then tr.left
          when 'end' then tr.right - fl.width
          else tr.left + (tr.width - fl.width) / 2
      when 'right' then tr.right + gap
      when 'left' then tr.left - fl.width - gap

    y = switch side
      when 'bottom' then tr.bottom + gap
      when 'top' then tr.top - fl.height - gap
      when 'left', 'right'
        switch align
          when 'start' then tr.top
          when 'end' then tr.bottom - fl.height
          else tr.top + (tr.height - fl.height) / 2

    if side is 'bottom' and y + fl.height > window.innerHeight
      y = tr.top - fl.height - gap
    if side is 'top' and y < 0
      y = tr.bottom + gap
    if side is 'right' and x + fl.width > window.innerWidth
      x = tr.left - fl.width - gap
    if side is 'left' and x < 0
      x = tr.right + gap

    x = Math.max(4, Math.min(x, window.innerWidth - fl.width - 4))

    @_tip.style.position = 'fixed'
    @_tip.style.left = "#{x}px"
    @_tip.style.top = "#{y}px"

  beforeUnmount: ->
    clearTimeout _showTimer if _showTimer
    clearTimeout _hideTimer if _hideTimer

  render
    .
      div ref: "_trigger"
        aria-describedby: open ? _id : undefined
        @mouseenter: @show
        @mouseleave: @hide
        @focusin: @show
        @focusout: @hide
        slot

      if open
        div ref: "_tip", id: _id, role: "tooltip", style: "position:fixed"
          $open: true
          $entering: entering?!
          $exiting: exiting?!
          $placement: @placement
          @mouseenter: (=> @_cancelHide() if @hoverable)
          @mouseleave: (=> @hide() if @hoverable)
          @text
