/**
 * ToggleGroup - Groups toggle buttons for single or multiple selection
 * @countermeasure/web-components/primitives/toggle
 */

import { type ToggleGroupConfig, type ToggleGroupItem, type ToggleGroupType } from './types'
import { resolveContainer, uniqueId } from '../utils'
import { setSafeIconHtml } from '../../utils/sanitize'

interface ManagedToggle {
  value: string
  el: HTMLButtonElement
  handler: () => void
}

export class ToggleGroup {
  public readonly el: HTMLDivElement

  private readonly groupId: string
  private readonly groupType: ToggleGroupType
  private readonly orientation: 'horizontal' | 'vertical'
  private readonly toggles: ManagedToggle[] = []
  private selectedValues: Set<string>
  private isDisabled = false
  private readonly onChangeCallback: ((value: string | string[]) => void) | undefined

  constructor(config: ToggleGroupConfig) {
    this.groupId = uniqueId('cmm-toggle-group')
    this.groupType = config.type ?? 'single'
    this.orientation = config.orientation ?? 'horizontal'
    this.onChangeCallback = config.onChange

    // Initialize selected values
    this.selectedValues = new Set<string>()
    if (config.value !== undefined) {
      if (Array.isArray(config.value)) {
        for (const v of config.value) {
          this.selectedValues.add(v)
        }
      } else {
        this.selectedValues.add(config.value)
      }
    }

    // Create container
    this.el = document.createElement('div')
    this.el.id = this.groupId
    this.el.className = this.buildClassName(config.className)
    this.el.setAttribute('role', 'group')

    if (config.ariaLabel !== undefined) {
      this.el.setAttribute('aria-label', config.ariaLabel)
    }

    // Disabled state
    if (config.disabled === true) {
      this.isDisabled = true
    }

    // Mount if container provided
    if (config.container !== undefined) {
      const parent = resolveContainer(config.container)
      parent.appendChild(this.el)
    }
  }

  /** Add a single toggle item to the group */
  addItem(item: ToggleGroupItem): void {
    const btn = document.createElement('button')
    btn.type = 'button'
    btn.className = 'cmm-toggle cmm-toggle--outline'
    btn.dataset.value = item.value
    btn.setAttribute('aria-pressed', String(this.selectedValues.has(item.value)))

    if (item.ariaLabel !== undefined) {
      btn.setAttribute('aria-label', item.ariaLabel)
    }

    if (item.disabled === true || this.isDisabled) {
      btn.disabled = true
    }

    // Build content
    if (item.icon !== undefined) {
      const iconEl = document.createElement('span')
      iconEl.className = 'cmm-toggle__icon'
      setSafeIconHtml(iconEl, item.icon)
      btn.appendChild(iconEl)
    }

    if (item.label !== undefined) {
      const labelEl = document.createElement('span')
      labelEl.className = 'cmm-toggle__label'
      labelEl.textContent = item.label
      btn.appendChild(labelEl)
    }

    // Apply pressed class if initially selected
    if (this.selectedValues.has(item.value)) {
      btn.classList.add('cmm-toggle--pressed')
    }

    // Click handler
    const handler = () => {
      this.handleToggleClick(item.value)
    }
    btn.addEventListener('click', handler)

    const managed: ManagedToggle = { value: item.value, el: btn, handler }
    this.toggles.push(managed)
    this.el.appendChild(btn)
  }

  /** Replace all items */
  setItems(items: ToggleGroupItem[]): void {
    this.clearItems()
    for (const item of items) {
      this.addItem(item)
    }
  }

  /** Get current value(s) */
  getValue(): string | string[] {
    if (this.groupType === 'single') {
      const values = [...this.selectedValues]
      return values[0] ?? ''
    }
    return [...this.selectedValues]
  }

  /** Set current value(s) */
  setValue(value: string | string[]): void {
    this.selectedValues.clear()
    if (Array.isArray(value)) {
      for (const v of value) {
        this.selectedValues.add(v)
      }
    } else {
      this.selectedValues.add(value)
    }
    this.syncToggles()
  }

  /** Set the disabled state for all toggles */
  setDisabled(disabled: boolean): void {
    this.isDisabled = disabled
    for (const managed of this.toggles) {
      managed.el.disabled = disabled
    }
  }

  /** Remove from DOM and clean up */
  destroy(): void {
    for (const managed of this.toggles) {
      managed.el.removeEventListener('click', managed.handler)
    }
    this.toggles.length = 0
    this.el.remove()
  }

  private handleToggleClick(value: string): void {
    if (this.groupType === 'single') {
      // In single mode, if already selected, deselect; otherwise select only this one
      if (this.selectedValues.has(value)) {
        this.selectedValues.delete(value)
      } else {
        this.selectedValues.clear()
        this.selectedValues.add(value)
      }
    } else {
      // In multiple mode, toggle the clicked value
      if (this.selectedValues.has(value)) {
        this.selectedValues.delete(value)
      } else {
        this.selectedValues.add(value)
      }
    }

    this.syncToggles()
    this.onChangeCallback?.(this.getValue())
  }

  private syncToggles(): void {
    for (const managed of this.toggles) {
      const isSelected = this.selectedValues.has(managed.value)
      managed.el.setAttribute('aria-pressed', String(isSelected))
      if (isSelected) {
        managed.el.classList.add('cmm-toggle--pressed')
      } else {
        managed.el.classList.remove('cmm-toggle--pressed')
      }
    }
  }

  private clearItems(): void {
    for (const managed of this.toggles) {
      managed.el.removeEventListener('click', managed.handler)
      managed.el.remove()
    }
    this.toggles.length = 0
  }

  private buildClassName(extra?: string): string {
    const classes = ['cmm-toggle-group', `cmm-toggle-group--${this.orientation}`]
    if (extra) {
      classes.push(extra)
    }
    return classes.join(' ')
  }
}
