/**
 * Button - Primary interactive button component
 * @countermeasure/web-components/primitives/button
 */

import DOMPurify from 'dompurify'
import { type ButtonConfig, type ButtonVariant, type ButtonSize } from './types'
import { resolveContainer, uniqueId } from '../utils'

export type { ButtonConfig, ButtonVariant, ButtonSize, ButtonGroupConfig } from './types'
export { ButtonGroup } from './button-group'

export class Button {
  public readonly el: HTMLButtonElement

  private labelEl: HTMLSpanElement
  private iconEl: HTMLSpanElement | null = null
  private spinnerEl: HTMLSpanElement | null = null
  private loadingTextEl: HTMLSpanElement | null = null
  private readonly btnId: string
  private currentVariant: ButtonVariant
  private currentSize: ButtonSize
  private isLoading: boolean
  private readonly clickHandler: (() => void) | undefined

  constructor(config: ButtonConfig) {
    this.btnId = uniqueId('cmm-btn')
    this.currentVariant = config.variant ?? 'primary'
    this.currentSize = config.size ?? 'md'
    this.isLoading = config.loading === true
    this.clickHandler = config.onClick

    // Create the button element
    this.el = document.createElement('button')
    this.el.id = this.btnId
    this.el.type = config.type ?? 'button'
    this.el.className = this.buildClassName(config.className)

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

    // Set disabled state
    if (config.disabled === true || this.isLoading) {
      this.el.disabled = true
      this.el.setAttribute('aria-disabled', 'true')
    }

    // Create label span
    this.labelEl = document.createElement('span')
    this.labelEl.className = 'cmm-btn__label'
    if (config.label !== undefined) {
      this.labelEl.textContent = config.label
    }

    // Create icon span if needed
    if (config.icon !== undefined) {
      this.iconEl = document.createElement('span')
      this.iconEl.className = 'cmm-btn__icon'
      this.iconEl.innerHTML = DOMPurify.sanitize(config.icon, { USE_PROFILES: { svg: true } })
    }

    // Build inner content
    this.buildContent(config.iconPosition ?? 'left')

    // Loading state
    if (this.isLoading) {
      this.applyLoadingState()
    }

    // Click handler
    if (this.clickHandler !== undefined) {
      this.el.addEventListener('click', this.clickHandler)
    }

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

  /** Update the button label text */
  setLabel(label: string): void {
    this.labelEl.textContent = label
  }

  /** Toggle the disabled state */
  setDisabled(disabled: boolean): void {
    this.el.disabled = disabled || this.isLoading
    if (disabled) {
      this.el.setAttribute('aria-disabled', 'true')
    } else if (!this.isLoading) {
      this.el.removeAttribute('aria-disabled')
    }
  }

  /** Toggle the loading spinner */
  setLoading(loading: boolean): void {
    this.isLoading = loading

    if (loading) {
      this.el.classList.add('cmm-btn--loading')
      this.el.disabled = true
      this.el.setAttribute('aria-disabled', 'true')
      this.applyLoadingState()
    } else {
      this.el.classList.remove('cmm-btn--loading')
      this.el.disabled = false
      this.el.removeAttribute('aria-disabled')
      this.removeLoadingState()
    }
  }

  /** Change the button variant */
  setVariant(variant: ButtonVariant): void {
    this.el.classList.remove(`cmm-btn--${this.currentVariant}`)
    this.currentVariant = variant
    this.el.classList.add(`cmm-btn--${this.currentVariant}`)
  }

  /** Remove the button from the DOM and clean up event listeners */
  destroy(): void {
    if (this.clickHandler !== undefined) {
      this.el.removeEventListener('click', this.clickHandler)
    }
    this.el.remove()
  }

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

  private buildContent(iconPosition: 'left' | 'right'): void {
    this.el.innerHTML = ''

    if (this.iconEl !== null && iconPosition === 'left') {
      this.el.appendChild(this.iconEl)
    }

    this.el.appendChild(this.labelEl)

    if (this.iconEl !== null && iconPosition === 'right') {
      this.el.appendChild(this.iconEl)
    }
  }

  private applyLoadingState(): void {
    if (this.spinnerEl !== null) {
      return
    }

    // Create spinner element (visual)
    this.spinnerEl = document.createElement('span')
    this.spinnerEl.className = 'cmm-btn__spinner'
    this.spinnerEl.setAttribute('aria-hidden', 'true')
    this.el.insertBefore(this.spinnerEl, this.el.firstChild)

    // Create visually hidden text for screen readers
    this.loadingTextEl = document.createElement('span')
    this.loadingTextEl.className = 'cmm-sr-only'
    this.loadingTextEl.textContent = 'Loading'
    this.loadingTextEl.style.cssText = `
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    `
    this.el.insertBefore(this.loadingTextEl, this.el.firstChild)

    // Set aria-busy to indicate loading state
    this.el.setAttribute('aria-busy', 'true')
  }

  private removeLoadingState(): void {
    if (this.spinnerEl !== null) {
      this.spinnerEl.remove()
      this.spinnerEl = null
    }
    if (this.loadingTextEl !== null) {
      this.loadingTextEl.remove()
      this.loadingTextEl = null
    }
    this.el.removeAttribute('aria-busy')
  }
}
