import { html, LitElement, type TemplateResult } from 'lit'
import { property, queryAssignedElements } from 'lit/decorators.js'
import type NavigationItem from './NavigationItem.js'
import { AriaCurrent } from '../../../types/aria.js'

/**
 * NavigationList
 *
 * A semantic, accessible navigation container for `ui-navigation-item` elements.
 * Handles keyboard navigation (roving tabindex), focus management, and ARIA roles.
 *
 * ## Usage Example
 *
 * ```html
 * <ui-navigation aria-label="Main navigation" current="page">
 *   <ui-navigation-item selected>
 *     <span slot="icon" aria-hidden="true">🏠</span>
 *     Home
 *   </ui-navigation-item>
 *   <ui-navigation-item>
 *     <span slot="icon" aria-hidden="true">🔍</span>
 *     Search
 *   </ui-navigation-item>
 *   <ui-navigation-item>
 *     <span slot="icon" aria-hidden="true">📁</span>
 *     Files
 *   </ui-navigation-item>
 *   <ui-navigation-item iconOnly disabled aria-disabled="true" tabindex="-1">
 *     <span slot="icon" aria-hidden="true">⚙️</span>
 *   </ui-navigation-item>
 * </ui-navigation>
 * ```
 *
 * @slot - Place `ui-navigation-item` elements as direct children.
 * @fires select - Dispatched when an item is selected via click or keyboard.
 *                 The `detail` object contains the selected `item`.
 */
export default class Navigation extends LitElement {
  /**
   * Orientation of the navigation: 'horizontal' or 'vertical'.
   * @attribute
   */
  @property({ type: String, reflect: true }) accessor orientation: 'horizontal' | 'vertical' = 'vertical'

  /**
   * The current item in the navigation, used for ARIA attributes.
   * Can be 'page', 'step', 'location', 'date', 'time', 'true', 'false', or ''.
   * @attribute
   */
  @property({ type: String, reflect: true }) accessor current: AriaCurrent = ''

  /**
   * Query all ui-navigation-item children
   */
  @queryAssignedElements({ flatten: true }) accessor _items!: NavigationItem[]

  override render(): TemplateResult {
    return html`
      <nav aria-label="${this.getAttribute('aria-label') || 'Navigation'}">
        <slot @slotchange="${this._onSlotChange}"></slot>
      </nav>
    `
  }

  constructor() {
    super()
    this.addEventListener('keydown', this._onKeyDown.bind(this))
    this.addEventListener('click', this._onClick.bind(this))
  }

  override firstUpdated() {
    this._setupItems()
  }

  private _onSlotChange = () => {
    this._setupItems()
  }

  /**
   * Sets the initial `tabindex` for the items. The first selected item,
   * or the first item if none are selected, receives `tabindex="0"`.
   * All other items receive `tabindex="-1"`.
   */
  private _setupItems() {
    const items = this._getEnabledItems()
    if (!items.length) {
      return
    }
    items.forEach((item) => {
      if (item.disabled) {
        return
      }
      item.setAttribute('tabindex', item.selected ? '0' : '-1')
      if (item.selected && this.current) {
        item.setAttribute('aria-current', this.current)
      } else if (!item.selected && item.hasAttribute('aria-current')) {
        item.removeAttribute('aria-current')
      }
    })
  }

  private _getEnabledItems(): NavigationItem[] {
    return (this._items || []).filter((item) => !item.disabled)
  }

  private _onClick = (e: MouseEvent) => {
    const target = e.target as HTMLElement
    const item = target.closest('ui-navigation-item')
    if (item && this._items.includes(item)) {
      this._selectItem(item)
    }
  }

  private _selectItem(itemToSelect: NavigationItem) {
    if (itemToSelect.disabled) {
      return
    }

    this._items.forEach((item) => {
      item.selected = item === itemToSelect
      if (item.hasAttribute('aria-current')) {
        item.removeAttribute('aria-current')
      }
    })

    if (this.current) {
      itemToSelect.setAttribute('aria-current', this.current)
    }
    this._updateFocus(itemToSelect)

    this.dispatchEvent(
      new CustomEvent('select', {
        detail: { item: itemToSelect },
        bubbles: false,
        composed: true,
      })
    )
  }

  private _updateFocus(itemToFocus: NavigationItem) {
    this._setupItems()
    itemToFocus.focus()
  }

  private _onKeyDown(e: KeyboardEvent) {
    const items = this._getEnabledItems()
    if (!items.length) {
      return
    }

    const activeElement = (this.getRootNode() as Document | ShadowRoot).activeElement
    if (!activeElement) {
      return
    }

    // Find which of our items is the host of the active element.
    // This is necessary because of focus delegation in ui-navigation-item.
    const activeItem = items.find((item) => item === activeElement || item.shadowRoot?.contains(activeElement))
    if (!activeItem) {
      return
    }

    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      this._selectItem(activeItem)
      return
    }

    const idx = items.indexOf(activeItem)
    let nextIdx = idx
    if (this.orientation === 'horizontal') {
      if (e.key === 'ArrowRight') nextIdx = (idx + 1) % items.length
      if (e.key === 'ArrowLeft') nextIdx = (idx - 1 + items.length) % items.length
    } else {
      if (e.key === 'ArrowDown') nextIdx = (idx + 1) % items.length
      if (e.key === 'ArrowUp') nextIdx = (idx - 1 + items.length) % items.length
    }
    if (e.key === 'Home') nextIdx = 0
    if (e.key === 'End') nextIdx = items.length - 1
    if (nextIdx !== idx) {
      items[nextIdx].focus()
      items.forEach((item, i) => item.setAttribute('tabindex', i === nextIdx ? '0' : '-1'))
      e.preventDefault()
    }
  }
}
