/**
 * Stepper - Multi-step wizard with visual step indicators
 * @countermeasure/web-components/navigation/stepper
 */

import { resolveContainer, uniqueId } from '../../primitives/utils'
import { setSafeIconHtml } from '../../utils/sanitize'
import { type Step, type StepperConfig, type StepperOrientation, type StepStatus } from './types'

export type { Step, StepperConfig, StepperOrientation, StepStatus } from './types'

/** SVG icons for step status indicators */
const STATUS_ICONS: Record<string, string> = {
  complete:
    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 12 2 2 4-4"/></svg>',
  error:
    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',
}

export class Stepper {
  private readonly container: HTMLElement
  private readonly orientation: StepperOrientation
  private readonly allowClick: boolean
  private readonly linear: boolean
  private readonly className: string | undefined
  private readonly onChangeCallback: ((index: number) => void) | undefined
  private readonly stepperId: string

  private steps: Step[]
  private currentIndex: number
  private destroyed = false

  constructor(config: StepperConfig) {
    this.container = resolveContainer(config.container)
    this.steps = [...config.steps]
    this.orientation = config.orientation ?? 'horizontal'
    this.allowClick = config.allowClick !== false
    this.linear = config.linear === true
    this.className = config.className
    this.onChangeCallback = config.onChange
    this.stepperId = uniqueId('cmm-stepper')

    // Determine initial active step
    this.currentIndex = this.steps.findIndex(s => s.status === 'active')
    if (this.currentIndex < 0) {
      this.currentIndex = 0
    }

    this.applyStatuses()
    this.render()
  }

  /** Navigate to a specific step by index */
  goTo(index: number): void {
    if (this.destroyed || index < 0 || index >= this.steps.length) {
      return
    }

    const targetStep = this.steps[index]
    if (targetStep?.status === 'disabled') {
      return
    }

    // In linear mode, only allow navigating to completed steps or the next step
    if (this.linear && index > this.currentIndex + 1) {
      return
    }

    this.setCurrentIndex(index)
  }

  /** Move to the next step */
  next(): void {
    if (this.currentIndex < this.steps.length - 1) {
      this.goTo(this.currentIndex + 1)
    }
  }

  /** Move to the previous step */
  prev(): void {
    if (this.currentIndex > 0) {
      this.goTo(this.currentIndex - 1)
    }
  }

  /** Get the current active step index */
  getCurrentIndex(): number {
    return this.currentIndex
  }

  /** Update the steps and re-render */
  update(steps: Step[]): void {
    if (this.destroyed) {
      return
    }
    this.steps = [...steps]
    this.applyStatuses()
    this.render()
  }

  /** Remove all DOM elements and clean up */
  destroy(): void {
    if (this.destroyed) {
      return
    }
    this.destroyed = true
    this.container.innerHTML = ''
  }

  /** Set the current index and update statuses */
  private setCurrentIndex(index: number): void {
    // Mark steps before current as complete (if not already error/disabled)
    for (let i = 0; i < this.steps.length; i++) {
      const step = this.steps[i]
      if (!step) {
        continue
      }

      if (step.status === 'disabled') {
        continue
      }

      if (i < index) {
        if (step.status !== 'error') {
          step.status = 'complete'
        }
      } else if (i === index) {
        step.status = 'active'
      } else {
        if (step.status !== 'error') {
          step.status = 'pending'
        }
      }
    }

    this.currentIndex = index
    this.render()
    this.onChangeCallback?.(index)
  }

  /** Apply default statuses if not explicitly set */
  private applyStatuses(): void {
    for (let i = 0; i < this.steps.length; i++) {
      const step = this.steps[i]
      if (!step) {
        continue
      }

      if (step.status === undefined) {
        if (i < this.currentIndex) {
          step.status = 'complete'
        } else if (i === this.currentIndex) {
          step.status = 'active'
        } else {
          step.status = 'pending'
        }
      }
    }
  }

  /** Render the stepper */
  private render(): void {
    this.container.innerHTML = ''

    const root = document.createElement('div')
    root.className = 'cmm-stepper'
    root.id = this.stepperId
    root.setAttribute('role', 'navigation')
    root.setAttribute('aria-label', 'Progress')

    if (this.orientation === 'vertical') {
      root.classList.add('cmm-stepper--vertical')
    }

    if (this.className) {
      root.classList.add(this.className)
    }

    const ol = document.createElement('ol')
    ol.className = 'cmm-stepper__list'

    for (let i = 0; i < this.steps.length; i++) {
      const step = this.steps[i]
      if (!step) {
        continue
      }

      const status: StepStatus = step.status ?? 'pending'
      const li = document.createElement('li')
      li.className = `cmm-stepper__step cmm-stepper__step--${status}`

      // Connector line (before each step except the first)
      if (i > 0) {
        const connector = document.createElement('div')
        connector.className = 'cmm-stepper__connector'
        connector.setAttribute('aria-hidden', 'true')

        // Mark connector as complete if previous step is complete
        const prevStep = this.steps[i - 1]
        if (prevStep?.status === 'complete') {
          connector.classList.add('cmm-stepper__connector--complete')
        }

        li.appendChild(connector)
      }

      // Step indicator circle
      const indicator = document.createElement('div')
      indicator.className = 'cmm-stepper__indicator'

      if (step.icon) {
        setSafeIconHtml(indicator, step.icon)
      } else if (status === 'complete' || status === 'error') {
        setSafeIconHtml(indicator, STATUS_ICONS[status] ?? '')
      } else {
        indicator.textContent = String(i + 1)
      }

      // Click handler
      if (this.allowClick && status !== 'disabled') {
        const isClickable = !this.linear || i <= this.currentIndex + 1
        if (isClickable) {
          indicator.classList.add('cmm-stepper__indicator--clickable')
          indicator.setAttribute('tabindex', '0')
          indicator.setAttribute('role', 'button')
          indicator.setAttribute('aria-label', `Go to step ${String(i + 1)}: ${step.label}`)

          const stepIndex = i
          indicator.addEventListener('click', () => {
            this.goTo(stepIndex)
          })
          indicator.addEventListener('keydown', (e: KeyboardEvent) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault()
              this.goTo(stepIndex)
            }
          })
        }
      }

      li.appendChild(indicator)

      // Step text content
      const content = document.createElement('div')
      content.className = 'cmm-stepper__content'

      const label = document.createElement('div')
      label.className = 'cmm-stepper__label'
      label.textContent = step.label

      if (status === 'active') {
        label.setAttribute('aria-current', 'step')
      }

      content.appendChild(label)

      if (step.description) {
        const desc = document.createElement('div')
        desc.className = 'cmm-stepper__description'
        desc.textContent = step.description
        content.appendChild(desc)
      }

      li.appendChild(content)
      ol.appendChild(li)
    }

    root.appendChild(ol)
    this.container.appendChild(root)
  }
}
