import { beforeEach, describe, expect, it, vi } from 'vitest'

import { SidebarComponent } from './component'

function createContainer(): HTMLElement {
  const container = document.createElement('div')
  container.style.height = '320px'
  document.body.appendChild(container)
  return container
}

describe('SidebarComponent', () => {
  beforeEach(() => {
    localStorage.clear()
    window.history.replaceState({}, '', '/')
  })

  it('renders brand header, scroll body, and edge collapse control when configured', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      variant: 'product',
      brand: { label: 'CounterMeasure' },
      collapsible: true,
      collapseButtonVisible: true,
      sections: [
        {
          id: 'main',
          label: 'Main',
          items: [
            {
              id: 'dashboard',
              label: 'Dashboard',
              icon: 'dashboard',
              badge: 3,
              badgeTone: 'amber',
            },
          ],
        },
      ],
    })

    expect(container.querySelector('.sidebar')?.classList.contains('sidebar--product')).toBe(true)
    expect(container.querySelector('.sidebar')?.getAttribute('data-sidebar')).toBe('product')
    expect(container.querySelector('.sidebar')?.getAttribute('data-open')).toBe('false')
    expect(container.querySelector('.sidebar__header .cmm-brand-lockup')).toBeTruthy()
    expect(container.querySelector('.sidebar__body .sidebar__section')).toBeTruthy()
    expect(container.querySelector('.sidebar__badge--amber')?.textContent).toBe('3')
    expect(container.querySelector('.sidebar__item')?.getAttribute('data-badge-visible')).toBe(
      'true'
    )
    expect(container.querySelector('.sidebar__item')?.getAttribute('data-badge-tone')).toBe('amber')

    const collapseButton = container.querySelector<HTMLButtonElement>(
      '.sidebar__collapse-btn--edge'
    )
    expect(collapseButton).toBeTruthy()
    expect(collapseButton?.getAttribute('aria-label')).toBe('Collapse sidebar')
    expect(collapseButton?.getAttribute('aria-expanded')).toBe('true')
    expect(collapseButton?.getAttribute('data-visible')).toBe('true')

    sidebar.destroy()
    container.remove()
  })

  it('updates collapse control state and emits collapse callbacks', () => {
    const container = createContainer()
    const onCollapse = vi.fn()
    const sidebar = new SidebarComponent({
      container,
      collapsible: true,
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard' }],
        },
      ],
      onCollapse,
    })

    const collapseButton = container.querySelector<HTMLButtonElement>(
      '.sidebar__collapse-btn--edge'
    )
    collapseButton?.click()

    expect(container.querySelector('.sidebar')?.classList.contains('sidebar--collapsed')).toBe(true)
    expect(collapseButton?.getAttribute('aria-label')).toBe('Expand sidebar')
    expect(collapseButton?.getAttribute('aria-expanded')).toBe('false')
    expect(onCollapse).toHaveBeenCalledWith(true)

    sidebar.expand()
    expect(collapseButton?.getAttribute('aria-label')).toBe('Collapse sidebar')
    expect(collapseButton?.getAttribute('aria-expanded')).toBe('true')
    expect(onCollapse).toHaveBeenLastCalledWith(false)

    sidebar.destroy()
    container.remove()
  })

  it('reflects controlled mobile open state without changing desktop collapse state', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      open: true,
      collapsed: true,
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard' }],
        },
      ],
    })

    const aside = container.querySelector<HTMLElement>('.sidebar')
    expect(aside?.getAttribute('data-open')).toBe('true')
    expect(aside?.classList.contains('sidebar--collapsed')).toBe(true)

    sidebar.setOpen(false)
    expect(aside?.getAttribute('data-open')).toBe('false')
    expect(aside?.classList.contains('sidebar--collapsed')).toBe(true)

    sidebar.expand()
    expect(aside?.getAttribute('data-open')).toBe('false')
    expect(aside?.classList.contains('sidebar--collapsed')).toBe(false)

    sidebar.destroy()
    container.remove()
  })

  it('updates compact badge-dot visibility state when badge counts change', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      sections: [
        {
          id: 'main',
          items: [{ id: 'alerts', label: 'Alerts', badge: 5, badgeTone: 'rose' }],
        },
      ],
    })

    const item = container.querySelector<HTMLElement>('[data-item-id="alerts"]')
    expect(item?.getAttribute('data-badge-visible')).toBe('true')
    expect(item?.getAttribute('data-badge-tone')).toBe('rose')

    sidebar.updateBadge('alerts', 0)
    expect(item?.getAttribute('data-badge-visible')).toBe('false')
    expect(container.querySelector('[data-badge="alerts"]')?.textContent).toBe('')

    sidebar.updateBadge('alerts', 9)
    expect(item?.getAttribute('data-badge-visible')).toBe('true')
    expect(container.querySelector('[data-badge="alerts"]')?.textContent).toBe('9')

    sidebar.destroy()
    container.remove()
  })

  it('renders route links with derived active state and aria-current', () => {
    window.history.replaceState({}, '', '/dashboards/library')
    const container = createContainer()
    const onNavigate = vi.fn()
    const preventDefault = vi.fn((event: MouseEvent) => {
      event.preventDefault()
    })
    const sidebar = new SidebarComponent({
      container,
      sections: [
        {
          id: 'dashboards',
          label: 'Dashboards',
          items: [
            { id: 'library', label: 'Library', href: '/dashboards/library', icon: 'library' },
            {
              id: 'capacity',
              label: 'Capacity',
              href: '/dashboards/capacity',
              icon: 'capacity',
              onClick: preventDefault,
            },
          ],
        },
      ],
      onNavigate,
    })

    const active = container.querySelector<HTMLAnchorElement>('[data-item-id="library"]')
    const inactive = container.querySelector<HTMLAnchorElement>('[data-item-id="capacity"]')

    expect(active?.tagName).toBe('A')
    expect(active?.getAttribute('href')).toBe('/dashboards/library')
    expect(active?.classList.contains('sidebar__item--active')).toBe(true)
    expect(active?.getAttribute('aria-current')).toBe('page')
    expect(inactive?.classList.contains('sidebar__item--active')).toBe(false)

    inactive?.click()
    expect(onNavigate).toHaveBeenCalledWith(expect.objectContaining({ id: 'capacity' }))
    expect(preventDefault).toHaveBeenCalledWith(expect.any(MouseEvent))

    sidebar.destroy()
    container.remove()
  })

  it('derives active state from an explicit pathname when provided', () => {
    window.history.replaceState({}, '', '/')
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      pathname: '/app/jobs?filter=active',
      sections: [
        {
          id: 'main',
          items: [
            { id: 'library', label: 'Library', href: '/app', end: true },
            { id: 'jobs', label: 'Jobs', href: '/app/jobs' },
          ],
        },
      ],
    })

    const active = container.querySelector<HTMLElement>('[data-item-id="jobs"]')
    const library = container.querySelector<HTMLElement>('[data-item-id="library"]')

    expect(active?.classList.contains('sidebar__item--active')).toBe(true)
    expect(active?.getAttribute('aria-current')).toBe('page')
    expect(library?.classList.contains('sidebar__item--active')).toBe(false)

    sidebar.destroy()
    container.remove()
  })

  it('recomputes route-derived active state when pathname changes', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      pathname: '/app',
      sections: [
        {
          id: 'main',
          items: [
            { id: 'library', label: 'Library', href: '/app', end: true },
            { id: 'jobs', label: 'Jobs', href: '/app/jobs' },
          ],
        },
      ],
    })

    expect(container.querySelector('[data-item-id="library"]')?.getAttribute('aria-current')).toBe(
      'page'
    )

    sidebar.setPathname('/app/jobs')

    expect(container.querySelector('[data-item-id="library"]')?.getAttribute('aria-current')).toBe(
      null
    )
    expect(container.querySelector('[data-item-id="jobs"]')?.getAttribute('aria-current')).toBe(
      'page'
    )

    sidebar.destroy()
    container.remove()
  })

  it('creates a badge when a count appears after initial render', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      sections: [
        {
          id: 'main',
          items: [{ id: 'alerts', label: 'Alerts' }],
        },
      ],
    })

    expect(container.querySelector('[data-badge="alerts"]')).toBeNull()

    sidebar.updateBadge('alerts', 7, 'cyan')

    const item = container.querySelector<HTMLElement>('[data-item-id="alerts"]')
    expect(item?.getAttribute('data-badge-visible')).toBe('true')
    expect(item?.getAttribute('data-badge-tone')).toBe('cyan')
    expect(container.querySelector('[data-badge="alerts"]')?.textContent).toBe('7')

    sidebar.destroy()
    container.remove()
  })

  it('restores and persists collapsed state when a storage key is supplied', () => {
    localStorage.setItem('cm-sidebar', 'true')
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      collapsible: true,
      storageKey: 'cm-sidebar',
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard' }],
        },
      ],
    })

    const aside = container.querySelector<HTMLElement>('.sidebar')
    const collapseButton = container.querySelector<HTMLButtonElement>(
      '.sidebar__collapse-btn--edge'
    )

    expect(aside?.classList.contains('sidebar--collapsed')).toBe(true)
    collapseButton?.click()
    expect(aside?.classList.contains('sidebar--collapsed')).toBe(false)
    expect(localStorage.getItem('cm-sidebar')).toBe('false')

    sidebar.destroy()
    container.remove()
  })

  it('controls mobile backdrop state and closes from the backdrop', () => {
    const container = createContainer()
    const onOpenChange = vi.fn()
    const sidebar = new SidebarComponent({
      container,
      mobileBackdrop: true,
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard' }],
        },
      ],
      onOpenChange,
    })

    const aside = container.querySelector<HTMLElement>('.sidebar')
    const backdrop = container.querySelector<HTMLButtonElement>('.sidebar__backdrop')

    expect(backdrop?.getAttribute('data-visible')).toBe('false')
    sidebar.setOpen(true)
    expect(aside?.getAttribute('data-open')).toBe('true')
    expect(backdrop?.getAttribute('data-visible')).toBe('true')
    expect(onOpenChange).toHaveBeenCalledWith(true)

    backdrop?.click()
    expect(aside?.getAttribute('data-open')).toBe('false')
    expect(backdrop?.getAttribute('data-visible')).toBe('false')
    expect(onOpenChange).toHaveBeenLastCalledWith(false)

    sidebar.destroy()
    container.remove()
  })

  it('renders and updates the scope selector', () => {
    const container = createContainer()
    const onChange = vi.fn()
    const sidebar = new SidebarComponent({
      container,
      scope: {
        label: 'Tenant',
        icon: 'building',
        value: 'all',
        options: [
          { id: 'all', label: 'All Tenants', status: 'neutral' },
          { id: 'gold', label: 'Gold Tenant', status: 'active' },
        ],
        onChange,
      },
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard' }],
        },
      ],
    })

    const trigger = container.querySelector<HTMLButtonElement>('.sidebar__scope-trigger')
    const menu = container.querySelector<HTMLElement>('.sidebar__scope-menu')
    expect(trigger?.textContent).toContain('All Tenants')
    expect(menu?.hidden).toBe(true)

    trigger?.click()
    expect(trigger?.getAttribute('aria-expanded')).toBe('true')
    expect(menu?.hidden).toBe(false)

    container.querySelector<HTMLButtonElement>('[data-option-id="gold"]')?.click()
    expect(trigger?.textContent).toContain('Gold Tenant')
    expect(menu?.hidden).toBe(true)
    expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ id: 'gold' }))

    sidebar.destroy()
    container.remove()
  })

  it('renders rich product navigation attributes, footer chrome, and active match variants', () => {
    const container = createContainer()
    const onUser = vi.fn()
    const onDanger = vi.fn()
    const sidebar = new SidebarComponent({
      container,
      pathname: '/app/custom/deep',
      brand: { label: 'Core', onClick: vi.fn(), showWordmark: false, decorative: true },
      user: {
        name: 'Solo',
        detail: 'Analyst',
        avatarUrl: '/avatar.png',
        presence: 'online',
        label: 'Open user menu',
        onClick: onUser,
      },
      footerActions: [
        { id: 'settings', icon: 'settings', label: 'Settings' },
        { id: 'delete', icon: 'trash', label: 'Delete', danger: true, onClick: onDanger },
      ],
      sections: [
        {
          id: 'group',
          label: 'Group',
          collapsible: true,
          defaultExpanded: false,
          icon: 'folder',
          items: [
            {
              id: 'custom',
              label: 'Custom',
              href: '/app/custom',
              activeMatch: '/app/custom',
              target: '_blank',
              tag: 'New',
              badge: { label: '', tone: 'warning', dot: true, title: 'Warning' },
              ariaLabel: 'Custom route',
            },
            {
              id: 'regex',
              label: 'Regex',
              href: '/regex',
              activeMatch: /regex/,
            },
            {
              id: 'function',
              label: 'Function',
              activeMatch: pathname => pathname.includes('function'),
            },
            {
              id: 'disabled',
              label: 'Disabled',
              href: '/disabled',
              disabled: true,
            },
          ],
        },
      ],
    })

    const groupHeader = container.querySelector<HTMLButtonElement>('.sidebar__group-header')
    const custom = container.querySelector<HTMLAnchorElement>('[data-item-id="custom"]')
    const disabled = container.querySelector<HTMLElement>('[data-item-id="disabled"]')
    const user = container.querySelector<HTMLButtonElement>('.sidebar__user-trigger')
    const danger = container.querySelector<HTMLButtonElement>(
      '.sidebar__footer-icon[aria-label="Delete"]'
    )

    expect(groupHeader?.getAttribute('aria-expanded')).toBe('false')
    groupHeader?.click()
    expect(groupHeader?.getAttribute('aria-expanded')).toBe('true')
    expect(custom?.getAttribute('aria-current')).toBe('page')
    expect(custom?.target).toBe('_blank')
    expect(custom?.rel).toBe('noreferrer noopener')
    expect(custom?.getAttribute('aria-label')).toBe('Custom route')
    expect(custom?.querySelector('.sidebar__tag')?.textContent).toBe('New')
    expect(custom?.getAttribute('data-badge-visible')).toBe('true')
    expect(custom?.getAttribute('data-badge-tone')).toBe('warning')
    expect(disabled?.tagName).toBe('SPAN')
    expect(disabled?.getAttribute('aria-disabled')).toBe('true')
    expect(disabled?.getAttribute('tabindex')).toBe('-1')

    sidebar.setPathname('/regex')
    expect(container.querySelector('[data-item-id="regex"]')?.getAttribute('aria-current')).toBe(
      'page'
    )

    sidebar.setPathname('/function')
    expect(container.querySelector('[data-item-id="function"]')?.getAttribute('aria-current')).toBe(
      'page'
    )

    expect(user?.getAttribute('aria-label')).toBe('Open user menu')
    expect(container.querySelector('.sidebar__avatar img')?.getAttribute('src')).toBe('/avatar.png')
    expect(container.querySelector('.sidebar__presence--online')).toBeTruthy()
    user?.click()
    expect(onUser).toHaveBeenCalled()
    danger?.click()
    expect(onDanger).toHaveBeenCalled()
    expect(danger?.classList.contains('sidebar__footer-icon--danger')).toBe(true)

    sidebar.destroy()
    container.remove()
  })

  it('keeps scope menus and mobile drawers in sync with outside events', () => {
    const container = createContainer()
    const sidebar = new SidebarComponent({
      container,
      open: true,
      mobileBackdrop: true,
      mobileBreakpoint: 10_000,
      closeOnNavigate: true,
      scope: {
        placeholder: 'Pick a tenant',
        options: [
          { id: 'all', label: 'All Tenants' },
          { id: 'disabled', label: 'Disabled Tenant', disabled: true },
        ],
      },
      sections: [
        {
          id: 'main',
          items: [{ id: 'dashboard', label: 'Dashboard', href: '/dashboard' }],
        },
      ],
    })

    const aside = container.querySelector<HTMLElement>('.sidebar')
    const trigger = container.querySelector<HTMLButtonElement>('.sidebar__scope-trigger')
    const menu = container.querySelector<HTMLElement>('.sidebar__scope-menu')

    expect(trigger?.textContent).toContain('All Tenants')
    expect(
      container.querySelector<HTMLButtonElement>('[data-option-id="disabled"]')?.disabled
    ).toBe(true)

    trigger?.click()
    expect(menu?.hidden).toBe(false)
    document.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }))
    expect(menu?.hidden).toBe(true)

    aside
      ?.querySelector<HTMLAnchorElement>('[data-item-id="dashboard"]')
      ?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
    expect(aside?.getAttribute('data-open')).toBe('false')

    sidebar.setOpen(true)
    document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
    expect(aside?.getAttribute('data-open')).toBe('false')

    sidebar.destroy()
    container.remove()
  })
})
