/**
 * Sidebar Enhancement Module
 *
 * Progressive enhancement for server-rendered sidebar HTML.
 * Attaches collapse/expand, keyboard nav, skeleton timeout, and active state
 * management to existing [data-sidebar] elements.
 *
 * Usage:
 *   import { enhance, setActive } from '@countermeasure/web-components/sidebar/enhance'
 *   const sidebar = enhance('[data-sidebar="chat"]')
 *   // sidebar is idempotent — calling again on same element is a no-op
 *
 * Pattern: Sidebar.enhance('[data-sidebar]') — enhances existing HTML, no rendering.
 */

import { getStoredState, saveState, isMobile } from './index'

export interface EnhanceOptions {
  /** localStorage key prefix (default: 'sidebar-collapsed') */
  storageKey?: string
  /** Viewport width below which sidebar is mobile (default: 900) */
  mobileBreakpoint?: number
  /** Milliseconds before skeleton transitions to empty state (default: 3000) */
  skeletonTimeoutMs?: number
  /** CSS class for collapsed state (default: 'sidebar--collapsed') */
  collapsedClass?: string
}

const DEFAULT_OPTIONS: Required<EnhanceOptions> = {
  storageKey: 'sidebar-collapsed',
  mobileBreakpoint: 900,
  skeletonTimeoutMs: 3000,
  collapsedClass: 'sidebar--collapsed',
}

const ENHANCED_ATTR = 'data-sidebar-enhanced'

export class Sidebar {
  readonly element: HTMLElement
  private readonly options: Required<EnhanceOptions>
  private readonly variant: string
  private readonly cleanupFns: (() => void)[] = []
  private skeletonTimer: ReturnType<typeof setTimeout> | null = null

  constructor(element: HTMLElement, options?: EnhanceOptions) {
    this.element = element
    this.options = { ...DEFAULT_OPTIONS, ...options }
    this.variant = element.getAttribute('data-sidebar') ?? ''
    this.init()
  }

  private init(): void {
    this.setupCollapse()
    this.setupKeyboardNav()
    this.setupSkeletonTimeout()
  }

  /**
   * Collapse/expand toggle with localStorage persistence.
   */
  private setupCollapse(): void {
    const toggle = this.element.querySelector<HTMLButtonElement>('[data-sidebar-toggle]')
    const mobileToggles = Array.from(
      document.querySelectorAll<HTMLButtonElement>('[data-sidebar-mobile-toggle]')
    )
    const backdrop = document.querySelector<HTMLElement>(
      `[data-sidebar-backdrop="${this.variant}"]`
    )

    // Restore persisted state on desktop
    if (!isMobile() && getStoredState(this.variant)) {
      this.element.classList.add(this.options.collapsedClass)
    }

    // Desktop toggle
    if (toggle) {
      const handler = (): void => {
        const isCollapsed = this.element.classList.toggle(this.options.collapsedClass)
        saveState(this.variant, isCollapsed)
      }
      toggle.addEventListener('click', handler)
      this.cleanupFns.push(() => {
        toggle.removeEventListener('click', handler)
      })
    }

    // Mobile toggles
    mobileToggles.forEach(mobileToggle => {
      const handler = (): void => {
        const isOpen = this.element.getAttribute('data-open') === 'true'
        this.element.setAttribute('data-open', String(!isOpen))
        if (backdrop) {
          backdrop.setAttribute('data-visible', String(!isOpen))
        }
      }
      mobileToggle.addEventListener('click', handler)
      this.cleanupFns.push(() => {
        mobileToggle.removeEventListener('click', handler)
      })
    })

    // Backdrop click closes sidebar
    if (backdrop) {
      const handler = (): void => {
        this.element.setAttribute('data-open', 'false')
        backdrop.setAttribute('data-visible', 'false')
      }
      backdrop.addEventListener('click', handler)
      this.cleanupFns.push(() => {
        backdrop.removeEventListener('click', handler)
      })
    }

    // Escape key closes mobile sidebar
    const escHandler = (e: KeyboardEvent): void => {
      if (e.key === 'Escape' && this.element.getAttribute('data-open') === 'true') {
        this.element.setAttribute('data-open', 'false')
        if (backdrop) {
          backdrop.setAttribute('data-visible', 'false')
        }
      }
    }
    document.addEventListener('keydown', escHandler)
    this.cleanupFns.push(() => {
      document.removeEventListener('keydown', escHandler)
    })

    // Resize handler
    const resizeHandler = (): void => {
      if (isMobile()) {
        this.element.setAttribute('data-open', 'false')
        if (backdrop) {
          backdrop.setAttribute('data-visible', 'false')
        }
      }
    }
    window.addEventListener('resize', resizeHandler)
    this.cleanupFns.push(() => {
      window.removeEventListener('resize', resizeHandler)
    })
  }

  /**
   * Arrow key navigation within sidebar nav items.
   */
  private setupKeyboardNav(): void {
    const handler = (e: KeyboardEvent): void => {
      if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return

      const items = Array.from(
        this.element.querySelectorAll<HTMLElement>(
          '[data-nav-item], .sidebar__item, .sidebar__nav-link'
        )
      ).filter(el => !el.hidden && el.offsetParent !== null)

      if (items.length === 0) return

      const currentIndex = items.indexOf(document.activeElement as HTMLElement)
      if (currentIndex === -1) return

      e.preventDefault()
      const nextIndex =
        e.key === 'ArrowDown'
          ? (currentIndex + 1) % items.length
          : (currentIndex - 1 + items.length) % items.length

      items[nextIndex]?.focus()
    }
    this.element.addEventListener('keydown', handler)
    this.cleanupFns.push(() => {
      this.element.removeEventListener('keydown', handler)
    })
  }

  /**
   * After skeletonTimeoutMs, hide skeleton and show empty state.
   */
  private setupSkeletonTimeout(): void {
    const skeletons = this.element.querySelectorAll<HTMLElement>('[data-sidebar-skeleton]')
    if (skeletons.length === 0) return

    this.skeletonTimer = setTimeout(() => {
      skeletons.forEach(skeleton => {
        // Only transition if skeleton is still visible
        if (skeleton.offsetParent === null) return

        skeleton.hidden = true

        // Find sibling empty state
        const empty = skeleton.parentElement?.querySelector<HTMLElement>(
          '[data-sidebar-empty], .sidebar__empty'
        )
        if (empty) {
          empty.hidden = false
        }
      })
    }, this.options.skeletonTimeoutMs)

    this.cleanupFns.push(() => {
      if (this.skeletonTimer !== null) {
        clearTimeout(this.skeletonTimer)
      }
    })
  }

  /**
   * Cancel skeleton timeout (call when data arrives before timeout).
   */
  cancelSkeletonTimeout(): void {
    if (this.skeletonTimer !== null) {
      clearTimeout(this.skeletonTimer)
      this.skeletonTimer = null
    }
  }

  /**
   * Clean up all event listeners and timers.
   */
  destroy(): void {
    this.cleanupFns.forEach(fn => {
      fn()
    })
    this.cleanupFns.length = 0
    this.element.removeAttribute(ENHANCED_ATTR)
    delete (this.element as unknown as Record<string, unknown>).__sidebar
  }
}

/**
 * Set a nav item as active, removing active from all siblings.
 * Works with both unified .sidebar__item and legacy module-specific classes.
 */
export function setActive(item: HTMLElement, activeClass = 'sidebar__item--active'): void {
  // Find the nav container (closest section or nav body)
  const container =
    item.closest('.sidebar__section') ??
    item.closest('.sidebar__body') ??
    item.closest('nav') ??
    item.parentElement ??
    null

  if (container !== null) {
    container.querySelectorAll<HTMLElement>(`.${activeClass}`).forEach(el => {
      el.classList.remove(activeClass)
    })
  }
  item.classList.add(activeClass)
}

/**
 * Idempotent enhancement entry point.
 * Returns existing Sidebar instance if element was already enhanced.
 */
export function enhance(selector: string, options?: EnhanceOptions): Sidebar | null {
  const el = document.querySelector<HTMLElement>(selector)
  if (!el) return null

  // Idempotent check
  if (el.hasAttribute(ENHANCED_ATTR)) {
    return ((el as unknown as Record<string, unknown>).__sidebar as Sidebar | undefined) ?? null
  }

  const sidebar = new Sidebar(el, options)
  el.setAttribute(ENHANCED_ATTR, '')
  ;(el as unknown as Record<string, unknown>).__sidebar = sidebar
  return sidebar
}

/**
 * Enhance all [data-sidebar] elements on the page.
 * Returns cleanup function that destroys all enhanced sidebars.
 */
export function enhanceAll(options?: EnhanceOptions): () => void {
  const sidebars: Sidebar[] = []

  document.querySelectorAll<HTMLElement>('[data-sidebar]').forEach(el => {
    const variant = el.getAttribute('data-sidebar') ?? ''
    const instance = enhance(`[data-sidebar="${variant}"]`, options)
    if (instance) {
      sidebars.push(instance)
    }
  })

  return () => {
    sidebars.forEach(s => {
      s.destroy()
    })
  }
}
