/**
 * Input - Text input component with wrapper
 * @countermeasure/web-components/primitives/input
 */

import { type InputConfig, type InputSize } from './types'
import { resolveContainer, uniqueId } from '../utils'

export type {
  InputConfig,
  InputType,
  InputSize,
  InputGroupConfig,
  TextareaConfig,
  InputOTPConfig,
  IPInputConfig,
} from './types'
export { InputGroup } from './input-group'
export { Textarea } from './textarea'
export { InputOTP } from './input-otp'
export { IPInput } from './ip-input'

export class Input {
  public readonly el: HTMLDivElement
  public readonly inputEl: HTMLInputElement

  private readonly inputId: string
  private currentSize: InputSize
  private readonly handlers: {
    input: (e: Event) => void
    change: (e: Event) => void
    focus: () => void
    blur: () => void
    keydown: (e: KeyboardEvent) => void
  }

  constructor(config: InputConfig) {
    this.inputId = uniqueId('cmm-input')
    this.currentSize = config.size ?? 'md'

    // Create wrapper
    this.el = document.createElement('div')
    this.el.className = 'cmm-input-wrapper'

    // Create input element
    this.inputEl = document.createElement('input')
    this.inputEl.id = this.inputId
    this.inputEl.type = config.type ?? 'text'
    this.inputEl.className = `cmm-input cmm-input--${this.currentSize}`

    if (config.className) {
      this.el.classList.add(config.className)
    }

    if (config.placeholder !== undefined) {
      this.inputEl.placeholder = config.placeholder
    }

    if (config.value !== undefined) {
      this.inputEl.value = config.value
    }

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

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

    if (config.required === true) {
      this.inputEl.required = true
      this.inputEl.setAttribute('aria-required', 'true')
    }

    if (config.name !== undefined) {
      this.inputEl.name = config.name
    }

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

    if (config.maxLength !== undefined) {
      this.inputEl.maxLength = config.maxLength
    }

    if (config.pattern !== undefined) {
      this.inputEl.pattern = config.pattern
    }

    if (config.autocomplete !== undefined) {
      this.inputEl.setAttribute('autocomplete', config.autocomplete)
    }

    // Event handlers
    this.handlers = {
      input: (_e: Event) => {
        config.onInput?.(this.inputEl.value)
      },
      change: (_e: Event) => {
        config.onChange?.(this.inputEl.value)
      },
      focus: () => {
        config.onFocus?.()
      },
      blur: () => {
        config.onBlur?.()
      },
      keydown: (e: KeyboardEvent) => {
        config.onKeyDown?.(e)
      },
    }

    this.inputEl.addEventListener('input', this.handlers.input)
    this.inputEl.addEventListener('change', this.handlers.change)
    this.inputEl.addEventListener('focus', this.handlers.focus)
    this.inputEl.addEventListener('blur', this.handlers.blur)
    this.inputEl.addEventListener('keydown', this.handlers.keydown)

    this.el.appendChild(this.inputEl)

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

  /** Get the current input value */
  getValue(): string {
    return this.inputEl.value
  }

  /** Set the input value */
  setValue(value: string): void {
    this.inputEl.value = value
  }

  /** Toggle the disabled state */
  setDisabled(disabled: boolean): void {
    this.inputEl.disabled = disabled
  }

  /** Toggle the error state */
  setError(error: boolean): void {
    if (error) {
      this.inputEl.classList.add('cmm-input--error')
      this.inputEl.setAttribute('aria-invalid', 'true')
    } else {
      this.inputEl.classList.remove('cmm-input--error')
      this.inputEl.removeAttribute('aria-invalid')
    }
  }

  /** Focus the input element */
  focus(): void {
    this.inputEl.focus()
  }

  /** Blur the input element */
  blur(): void {
    this.inputEl.blur()
  }

  /** Remove from the DOM and clean up event listeners */
  destroy(): void {
    this.inputEl.removeEventListener('input', this.handlers.input)
    this.inputEl.removeEventListener('change', this.handlers.change)
    this.inputEl.removeEventListener('focus', this.handlers.focus)
    this.inputEl.removeEventListener('blur', this.handlers.blur)
    this.inputEl.removeEventListener('keydown', this.handlers.keydown)
    this.el.remove()
  }
}
