/**
 * NavigationMenu - Primary horizontal navigation with mega-menu dropdowns
 * @countermeasure/web-components/navigation/navigation-menu
 */

import { resolveContainer, uniqueId } from '../../primitives/utils'
import { createFocusTrap, type FocusTrap } from '../../utils/a11y'
import { setSafeIconHtml } from '../../utils/sanitize'
import { type NavChild, type NavItem, type NavMenuConfig } from './types'

export type { NavChild, NavItem, NavMenuConfig } from './types'

/** Breakpoint below which the menu collapses to a hamburger */
const COLLAPSE_BREAKPOINT = 640

export class NavigationMenu {
  private readonly container: HTMLElement
  private readonly className: string | undefined
  private readonly onNavigate: ((href: string) => void) | undefined
  private readonly navId: string

  private items: NavItem[]
  private navEl: HTMLElement | null = null
  private menuListEl: HTMLElement | null = null
  private hamburgerEl: HTMLElement | null = null
  private openIndex = -1
  private dropdownEl: HTMLElement | null = null
  private collapsed = false
  private mobileOpen = false
  private activeKeyIndex = -1
  private destroyed = false
  private mobileFocusTrap: FocusTrap | null = null

  /** Bound event handlers for cleanup */
  private readonly handleDocumentClick: (e: MouseEvent) => void
  private readonly handleKeyDown: (e: KeyboardEvent) => void
  private readonly handleResize: () => void

  constructor(config: NavMenuConfig) {
    this.container = resolveContainer(config.container)
    this.items = [...config.items]
    this.className = config.className
    this.onNavigate = config.onNavigate
    this.navId = uniqueId('cmm-nav-menu')

    this.handleDocumentClick = (e: MouseEvent) => {
      const target = e.target
      if (
        target instanceof Node &&
        this.navEl &&
        !this.navEl.contains(target) &&
        this.dropdownEl?.contains(target) !== true
      ) {
        this.closeDropdown()
        if (this.mobileOpen) {
          this.closeMobileMenu()
        }
      }
    }

    this.handleKeyDown = (e: KeyboardEvent) => {
      this.onKeyDown(e)
    }

    this.handleResize = () => {
      this.checkCollapse()
    }

    this.render()
    this.checkCollapse()

    document.addEventListener('click', this.handleDocumentClick)
    document.addEventListener('keydown', this.handleKeyDown)
    window.addEventListener('resize', this.handleResize)
  }

  /** Update the navigation items and re-render */
  update(items: NavItem[]): void {
    if (this.destroyed) {
      return
    }
    this.items = [...items]
    this.closeDropdown()
    this.render()
    this.checkCollapse()
  }

  /** Remove all DOM elements and clean up */
  destroy(): void {
    if (this.destroyed) {
      return
    }
    this.destroyed = true

    this.closeDropdown()
    document.removeEventListener('click', this.handleDocumentClick)
    document.removeEventListener('keydown', this.handleKeyDown)
    window.removeEventListener('resize', this.handleResize)
    this.container.innerHTML = ''
    this.navEl = null
    this.menuListEl = null
    this.hamburgerEl = null
  }

  /** Close the mega-menu dropdown */
  private closeDropdown(): void {
    if (this.dropdownEl) {
      this.dropdownEl.remove()
      this.dropdownEl = null
    }
    this.openIndex = -1
    this.updateTriggerStates()
  }

  /** Open a mega-menu dropdown for a specific item */
  private openDropdown(index: number): void {
    this.closeDropdown()

    const item = this.items[index]
    if (!item?.children || item.children.length === 0) {
      return
    }

    this.openIndex = index
    this.updateTriggerStates()

    // Get the trigger button position
    const triggers = this.menuListEl?.querySelectorAll<HTMLElement>('.cmm-nav-menu__trigger')
    const triggerEl = triggers?.[index]
    if (!triggerEl) {
      return
    }

    const triggerRect = triggerEl.getBoundingClientRect()

    // Create mega-menu dropdown
    const dropdown = document.createElement('div')
    dropdown.className = 'cmm-nav-menu__dropdown'
    dropdown.setAttribute('role', 'menu')
    dropdown.id = uniqueId('cmm-nav-menu-dropdown')

    const grid = document.createElement('div')
    grid.className = 'cmm-nav-menu__grid'

    for (const child of item.children) {
      grid.appendChild(this.buildChildCard(child))
    }

    dropdown.appendChild(grid)
    document.body.appendChild(dropdown)
    this.dropdownEl = dropdown

    // Position below the trigger
    const dropdownRect = dropdown.getBoundingClientRect()
    let left = triggerRect.left
    let top = triggerRect.bottom + 4

    // Adjust if near edges
    if (left + dropdownRect.width > window.innerWidth) {
      left = window.innerWidth - dropdownRect.width - 8
    }
    if (top + dropdownRect.height > window.innerHeight) {
      top = triggerRect.top - dropdownRect.height - 4
    }

    dropdown.style.left = `${String(Math.max(0, left))}px`
    dropdown.style.top = `${String(Math.max(0, top))}px`
  }

  /** Build a child navigation card element */
  private buildChildCard(child: NavChild): HTMLElement {
    const card = document.createElement('a')
    card.className = 'cmm-nav-menu__card'
    card.href = child.href
    card.setAttribute('role', 'menuitem')

    card.addEventListener('click', (e: MouseEvent) => {
      if (this.onNavigate) {
        e.preventDefault()
        this.onNavigate(child.href)
      }
      this.closeDropdown()
    })

    if (child.icon) {
      const iconEl = document.createElement('span')
      iconEl.className = 'cmm-nav-menu__card-icon'
      iconEl.setAttribute('aria-hidden', 'true')
      setSafeIconHtml(iconEl, child.icon)
      card.appendChild(iconEl)
    }

    const textContainer = document.createElement('div')
    textContainer.className = 'cmm-nav-menu__card-text'

    const labelEl = document.createElement('div')
    labelEl.className = 'cmm-nav-menu__card-label'
    labelEl.textContent = child.label
    textContainer.appendChild(labelEl)

    if (child.description) {
      const descEl = document.createElement('div')
      descEl.className = 'cmm-nav-menu__card-desc'
      descEl.textContent = child.description
      textContainer.appendChild(descEl)
    }

    card.appendChild(textContainer)
    return card
  }

  /** Update aria-expanded on trigger buttons */
  private updateTriggerStates(): void {
    const triggers = this.menuListEl?.querySelectorAll<HTMLElement>('.cmm-nav-menu__trigger')
    if (!triggers) {
      return
    }

    triggers.forEach((trigger, i) => {
      const isOpen = i === this.openIndex
      trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false')
      if (isOpen) {
        trigger.classList.add('cmm-nav-menu__trigger--active')
      } else {
        trigger.classList.remove('cmm-nav-menu__trigger--active')
      }
    })
  }

  /** Check if the container is narrow enough to collapse into hamburger mode */
  private checkCollapse(): void {
    if (this.destroyed || !this.navEl) {
      return
    }

    const containerWidth = this.container.getBoundingClientRect().width
    const shouldCollapse = containerWidth < COLLAPSE_BREAKPOINT

    if (shouldCollapse !== this.collapsed) {
      this.collapsed = shouldCollapse

      if (this.hamburgerEl) {
        this.hamburgerEl.style.display = shouldCollapse ? 'flex' : 'none'
      }

      if (this.menuListEl) {
        if (shouldCollapse) {
          this.menuListEl.classList.add('cmm-nav-menu__list--collapsed')
          if (!this.mobileOpen) {
            this.menuListEl.style.display = 'none'
          }
        } else {
          this.menuListEl.classList.remove('cmm-nav-menu__list--collapsed')
          this.menuListEl.style.display = ''
          this.mobileOpen = false
        }
      }
    }
  }

  /** Open the mobile hamburger menu */
  private openMobileMenu(): void {
    this.mobileOpen = true
    if (this.menuListEl) {
      this.menuListEl.style.display = ''
      this.menuListEl.classList.add('cmm-nav-menu__list--mobile-open')

      // Create and activate focus trap for mobile menu
      if (this.mobileFocusTrap === null) {
        this.mobileFocusTrap = createFocusTrap(this.menuListEl)
      } else {
        this.mobileFocusTrap.update()
      }
      this.mobileFocusTrap.activate()
    }
    if (this.hamburgerEl) {
      this.hamburgerEl.setAttribute('aria-expanded', 'true')
    }
  }

  /** Close the mobile hamburger menu */
  private closeMobileMenu(): void {
    this.mobileOpen = false

    // Deactivate focus trap
    if (this.mobileFocusTrap !== null) {
      this.mobileFocusTrap.deactivate()
    }

    if (this.menuListEl && this.collapsed) {
      this.menuListEl.style.display = 'none'
      this.menuListEl.classList.remove('cmm-nav-menu__list--mobile-open')
    }
    if (this.hamburgerEl) {
      this.hamburgerEl.setAttribute('aria-expanded', 'false')
    }
  }

  /** Handle keyboard navigation */
  private onKeyDown(e: KeyboardEvent): void {
    if (this.destroyed) {
      return
    }

    const target = e.target
    if (
      target instanceof Node &&
      this.navEl &&
      !this.navEl.contains(target) &&
      this.dropdownEl?.contains(target) !== true
    ) {
      return
    }

    if (e.key === 'Escape') {
      e.preventDefault()
      this.closeDropdown()
      if (this.mobileOpen) {
        this.closeMobileMenu()
      }
      return
    }

    if (e.key === 'ArrowRight') {
      e.preventDefault()
      this.activeKeyIndex = Math.min(this.activeKeyIndex + 1, this.items.length - 1)
      this.focusTrigger(this.activeKeyIndex)
      return
    }

    if (e.key === 'ArrowLeft') {
      e.preventDefault()
      this.activeKeyIndex = Math.max(this.activeKeyIndex - 1, 0)
      this.focusTrigger(this.activeKeyIndex)
      return
    }

    if (e.key === 'ArrowDown') {
      e.preventDefault()
      if (this.openIndex >= 0 && this.dropdownEl) {
        // Focus first card in the dropdown
        const firstCard = this.dropdownEl.querySelector<HTMLElement>('.cmm-nav-menu__card')
        if (firstCard) {
          firstCard.focus()
        }
      } else if (this.activeKeyIndex >= 0) {
        this.openDropdown(this.activeKeyIndex)
      }
      return
    }

    if (e.key === 'Enter' || e.key === ' ') {
      if (this.activeKeyIndex >= 0 && this.openIndex < 0) {
        e.preventDefault()
        const item = this.items[this.activeKeyIndex]
        if (item !== undefined && item.children && item.children.length > 0) {
          this.openDropdown(this.activeKeyIndex)
        } else if (item !== undefined && item.href && this.onNavigate) {
          this.onNavigate(item.href)
        }
      }
    }
  }

  /** Focus a specific trigger button by index */
  private focusTrigger(index: number): void {
    const triggers = this.menuListEl?.querySelectorAll<HTMLElement>('.cmm-nav-menu__trigger')
    const triggerEl = triggers?.[index]
    if (triggerEl) {
      triggerEl.focus()
    }
  }

  /** Render the full navigation menu */
  private render(): void {
    this.container.innerHTML = ''

    const nav = document.createElement('nav')
    nav.className = 'cmm-nav-menu'
    nav.id = this.navId
    nav.setAttribute('aria-label', 'Main navigation')

    if (this.className) {
      nav.classList.add(this.className)
    }

    // Hamburger button (hidden by default, shown on collapse)
    const hamburger = document.createElement('button')
    hamburger.className = 'cmm-nav-menu__hamburger'
    hamburger.type = 'button'
    hamburger.setAttribute('aria-label', 'Toggle navigation')
    hamburger.setAttribute('aria-expanded', 'false')
    hamburger.style.display = 'none'
    hamburger.innerHTML = '<span></span><span></span><span></span>'

    hamburger.addEventListener('click', (e: MouseEvent) => {
      e.stopPropagation()
      if (this.mobileOpen) {
        this.closeMobileMenu()
      } else {
        this.openMobileMenu()
      }
    })

    nav.appendChild(hamburger)
    this.hamburgerEl = hamburger

    // Menu list
    const list = document.createElement('div')
    list.className = 'cmm-nav-menu__list'
    list.setAttribute('role', 'menubar')

    for (let i = 0; i < this.items.length; i++) {
      const item = this.items[i]
      if (!item) {
        continue
      }

      const hasChildren = item.children !== undefined && item.children.length > 0

      if (hasChildren) {
        const trigger = document.createElement('button')
        trigger.className = 'cmm-nav-menu__trigger'
        trigger.type = 'button'
        trigger.setAttribute('aria-haspopup', 'true')
        trigger.setAttribute('aria-expanded', 'false')

        if (item.active === true) {
          trigger.classList.add('cmm-nav-menu__trigger--current')
        }

        if (item.icon) {
          const iconSpan = document.createElement('span')
          iconSpan.className = 'cmm-nav-menu__icon'
          iconSpan.setAttribute('aria-hidden', 'true')
          setSafeIconHtml(iconSpan, item.icon)
          trigger.appendChild(iconSpan)
        }

        const labelSpan = document.createElement('span')
        labelSpan.textContent = item.label
        trigger.appendChild(labelSpan)

        // Chevron indicator
        const chevron = document.createElement('span')
        chevron.className = 'cmm-nav-menu__chevron'
        chevron.setAttribute('aria-hidden', 'true')
        chevron.textContent = '\u25BE'
        trigger.appendChild(chevron)

        const index = i
        trigger.addEventListener('click', (e: MouseEvent) => {
          e.stopPropagation()
          if (this.openIndex === index) {
            this.closeDropdown()
          } else {
            this.openDropdown(index)
          }
        })

        trigger.addEventListener('mouseenter', () => {
          if (this.openIndex >= 0 && this.openIndex !== index && !this.collapsed) {
            this.openDropdown(index)
          }
        })

        list.appendChild(trigger)
      } else {
        const link = document.createElement('a')
        link.className = 'cmm-nav-menu__trigger'

        if (item.href) {
          link.href = item.href
        }

        if (item.active === true) {
          link.classList.add('cmm-nav-menu__trigger--current')
          link.setAttribute('aria-current', 'page')
        }

        if (item.icon) {
          const iconSpan = document.createElement('span')
          iconSpan.className = 'cmm-nav-menu__icon'
          iconSpan.setAttribute('aria-hidden', 'true')
          setSafeIconHtml(iconSpan, item.icon)
          link.appendChild(iconSpan)
        }

        const labelSpan = document.createElement('span')
        labelSpan.textContent = item.label
        link.appendChild(labelSpan)

        link.addEventListener('click', (e: MouseEvent) => {
          if (this.onNavigate && item.href) {
            e.preventDefault()
            this.onNavigate(item.href)
          }
        })

        list.appendChild(link)
      }
    }

    nav.appendChild(list)
    this.container.appendChild(nav)
    this.navEl = nav
    this.menuListEl = list
  }
}
