/**
 * Typography - Semantic text component with styling variants
 * @countermeasure/web-components/display/typography
 */

import { resolveContainer, uniqueId } from '../../primitives/utils'
import {
  type TypographyConfig,
  type TypographyVariant,
  type TypographyWeight,
  type TypographyAlign,
} from './types'

export type {
  TypographyConfig,
  TypographyVariant,
  TypographyWeight,
  TypographyAlign,
} from './types'

/** Map variant to the appropriate HTML tag name */
function variantToTag(
  variant: TypographyVariant
): 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' {
  switch (variant) {
    case 'h1': {
      return 'h1'
    }
    case 'h2': {
      return 'h2'
    }
    case 'h3': {
      return 'h3'
    }
    case 'h4': {
      return 'h4'
    }
    case 'h5': {
      return 'h5'
    }
    case 'h6': {
      return 'h6'
    }
    case 'body1':
    case 'body2': {
      return 'p'
    }
    case 'caption':
    case 'overline': {
      return 'span'
    }
  }
}

export class Typography {
  public readonly el: HTMLElement

  private readonly typographyId: string
  private currentVariant: TypographyVariant
  private currentText: string
  private readonly weight: TypographyWeight | undefined
  private readonly align: TypographyAlign | undefined
  private readonly muted: boolean
  private readonly truncate: boolean
  private readonly clamp: number | undefined
  private readonly className: string | undefined
  private readonly containerRef: HTMLElement | undefined

  constructor(config: TypographyConfig) {
    this.typographyId = uniqueId('cmm-typography')
    this.currentVariant = config.variant ?? 'body1'
    this.currentText = config.text
    this.weight = config.weight
    this.align = config.align
    this.muted = config.muted === true
    this.truncate = config.truncate === true
    this.clamp = config.clamp
    this.className = config.className

    // Create the element with the appropriate tag
    const tag = variantToTag(this.currentVariant)
    this.el = document.createElement(tag)
    this.el.id = this.typographyId
    this.el.className = this.buildClassName()
    this.el.textContent = this.currentText

    if (this.clamp !== undefined) {
      this.el.style.webkitLineClamp = String(this.clamp)
    }

    // Resolve and store container reference
    if (config.container !== undefined) {
      this.containerRef = resolveContainer(config.container)
      this.containerRef.appendChild(this.el)
    }
  }

  /** Update the text content */
  setText(text: string): void {
    this.currentText = text
    this.el.textContent = text
  }

  /** Update the variant (recreates the element with a new tag) */
  setVariant(variant: TypographyVariant): void {
    this.currentVariant = variant
    const newTag = variantToTag(variant)
    const newEl = document.createElement(newTag)
    newEl.id = this.typographyId
    newEl.className = this.buildClassName()
    newEl.textContent = this.currentText

    if (this.clamp !== undefined) {
      newEl.style.webkitLineClamp = String(this.clamp)
    }

    this.el.replaceWith(newEl)
    // Update the readonly reference via Object.defineProperty
    Object.defineProperty(this, 'el', { value: newEl })
  }

  /** Remove the typography element from the DOM */
  destroy(): void {
    this.el.remove()
  }

  private buildClassName(): string {
    const classes = ['cmm-typography', `cmm-typography--${this.currentVariant}`]

    if (this.weight !== undefined) {
      classes.push(`cmm-typography--${this.weight}`)
    }

    if (this.align !== undefined) {
      classes.push(`cmm-typography--${this.align}`)
    }

    if (this.muted) {
      classes.push('cmm-typography--muted')
    }

    if (this.truncate) {
      classes.push('cmm-typography--truncate')
    }

    if (this.clamp !== undefined) {
      classes.push('cmm-typography--clamp')
    }

    if (this.className) {
      classes.push(this.className)
    }

    return classes.join(' ')
  }
}
