/**
 * Switch - Toggle switch component
 * @countermeasure/web-components/primitives/switch
 */

import { type SwitchConfig, type SwitchSize } from './types'
import { resolveContainer, uniqueId } from '../utils'

export type { SwitchConfig, SwitchSize } from './types'

export class Switch {
  public readonly el: HTMLLabelElement

  private readonly inputEl: HTMLInputElement
  private readonly trackEl: HTMLSpanElement
  private readonly thumbEl: HTMLSpanElement
  private labelEl: HTMLSpanElement | null = null
  private readonly switchId: string
  private currentSize: SwitchSize
  private readonly changeHandler: () => void
  private readonly onChangeCallback: ((checked: boolean) => void) | undefined

  constructor(config: SwitchConfig) {
    this.switchId = uniqueId('cmm-switch')
    this.currentSize = config.size ?? 'md'
    this.onChangeCallback = config.onChange

    // Create the wrapper label
    this.el = document.createElement('label')
    this.el.className = this.buildClassName(config.className)

    // Create visually hidden checkbox input
    this.inputEl = document.createElement('input')
    this.inputEl.type = 'checkbox'
    this.inputEl.className = 'cmm-switch__input'
    this.inputEl.id = this.switchId

    if (config.checked === true) {
      this.inputEl.checked = true
    }

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

    // Create track (pill shape)
    this.trackEl = document.createElement('span')
    this.trackEl.className = 'cmm-switch__track'

    // Create thumb (circle)
    this.thumbEl = document.createElement('span')
    this.thumbEl.className = 'cmm-switch__thumb'

    // Assemble: track contains thumb
    this.trackEl.appendChild(this.thumbEl)

    // Assemble: label contains input, then track
    this.el.appendChild(this.inputEl)
    this.el.appendChild(this.trackEl)

    // Optional text label
    if (config.label !== undefined) {
      this.labelEl = document.createElement('span')
      this.labelEl.className = 'cmm-switch__label'
      this.labelEl.textContent = config.label
      this.el.appendChild(this.labelEl)
    }

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

    // Change handler — bind for reliable cleanup
    this.changeHandler = () => {
      this.onChangeCallback?.(this.inputEl.checked)
    }
    this.inputEl.addEventListener('change', this.changeHandler)

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

  /** Returns whether the switch is currently checked */
  isChecked(): boolean {
    return this.inputEl.checked
  }

  /** Programmatically set the checked state */
  setChecked(checked: boolean): void {
    this.inputEl.checked = checked
  }

  /** Toggle the checked state */
  toggle(): void {
    this.inputEl.checked = !this.inputEl.checked
    this.onChangeCallback?.(this.inputEl.checked)
  }

  /** Set the disabled state */
  setDisabled(disabled: boolean): void {
    this.inputEl.disabled = disabled
    if (disabled) {
      this.el.classList.add('cmm-switch--disabled')
    } else {
      this.el.classList.remove('cmm-switch--disabled')
    }
  }

  /** Change the size variant */
  setSize(size: SwitchSize): void {
    this.el.classList.remove(`cmm-switch--${this.currentSize}`)
    this.currentSize = size
    this.el.classList.add(`cmm-switch--${this.currentSize}`)
  }

  /** Remove from DOM and clean up */
  destroy(): void {
    this.inputEl.removeEventListener('change', this.changeHandler)
    this.el.remove()
  }

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