/**
 * AttackPath - Horizontal attack chain visualization
 * @countermeasure/web-components/security/attack-path
 */

import { type AttackPathConfig, type AttackStep } from './types'
import { resolveContainer, uniqueId } from '../../primitives/utils'

export type { AttackPathConfig, AttackStep, AttackStepType } from './types'

/** Readable labels for attack step types */
const TYPE_LABELS: Record<AttackStep['type'], string> = {
  initial_access: 'Initial Access',
  execution: 'Execution',
  persistence: 'Persistence',
  privilege_escalation: 'Privilege Escalation',
  defense_evasion: 'Defense Evasion',
  credential_access: 'Credential Access',
  discovery: 'Discovery',
  lateral_movement: 'Lateral Movement',
  collection: 'Collection',
  exfiltration: 'Exfiltration',
  impact: 'Impact',
  command_and_control: 'Command & Control',
}

export class AttackPath {
  public readonly el: HTMLDivElement

  private readonly pathId: string
  private readonly showTechniques: boolean
  private readonly stepClickHandler: ((step: AttackStep) => void) | undefined
  private currentSteps: AttackStep[]
  private readonly boundStepHandlers: { el: HTMLElement; handler: (e: Event) => void }[] = []

  constructor(config: AttackPathConfig) {
    this.pathId = uniqueId('cmm-attack-path')
    this.showTechniques = config.showTechniques === true
    this.stepClickHandler = config.onStepClick
    this.currentSteps = config.steps

    // Create root element
    this.el = document.createElement('div')
    this.el.id = this.pathId
    this.el.className = this.buildRootClassName(config.className)
    this.el.setAttribute('role', 'list')
    this.el.setAttribute('aria-label', config.title ?? 'Attack Path')

    // Title
    if (config.title !== undefined) {
      const titleEl = document.createElement('div')
      titleEl.className = 'cmm-attack-path__title'
      titleEl.textContent = config.title
      this.el.appendChild(titleEl)
    }

    // Steps container
    const stepsContainer = document.createElement('div')
    stepsContainer.className = 'cmm-attack-path__steps'
    this.el.appendChild(stepsContainer)

    this.renderSteps(stepsContainer)

    // Mount
    const parent = resolveContainer(config.container)
    parent.appendChild(this.el)
  }

  /** Update the attack path with new steps */
  update(steps: AttackStep[]): void {
    this.currentSteps = steps
    this.cleanupHandlers()

    const stepsContainer = this.el.querySelector('.cmm-attack-path__steps')
    if (stepsContainer !== null) {
      stepsContainer.innerHTML = ''
      this.renderSteps(stepsContainer as HTMLElement)
    }
  }

  /** Remove the element from the DOM and clean up */
  destroy(): void {
    this.cleanupHandlers()
    this.el.remove()
  }

  private renderSteps(container: HTMLElement): void {
    for (let i = 0; i < this.currentSteps.length; i++) {
      const step = this.currentSteps[i]
      if (step === undefined) {
        continue
      }

      // Step node
      const node = document.createElement('div')
      node.className = this.buildStepClassName(step)
      node.setAttribute('role', 'listitem')

      // Type label
      const typeLabel = document.createElement('div')
      typeLabel.className = 'cmm-attack-path__step-type'
      typeLabel.textContent = TYPE_LABELS[step.type]
      node.appendChild(typeLabel)

      // Step label
      const labelEl = document.createElement('div')
      labelEl.className = 'cmm-attack-path__step-label'
      labelEl.textContent = step.label
      node.appendChild(labelEl)

      // Mitigated shield icon
      if (step.mitigated === true) {
        const shield = document.createElement('span')
        shield.className = 'cmm-attack-path__shield'
        shield.setAttribute('aria-label', 'Mitigated')
        shield.textContent = '\u{1F6E1}'
        node.appendChild(shield)
      }

      // Tooltip popup (description + techniques)
      if (
        step.description !== undefined ||
        (this.showTechniques && step.techniques !== undefined && step.techniques.length > 0)
      ) {
        const popup = document.createElement('div')
        popup.className = 'cmm-attack-path__popup'
        popup.setAttribute('role', 'tooltip')

        if (step.description !== undefined) {
          const descEl = document.createElement('p')
          descEl.className = 'cmm-attack-path__popup-desc'
          descEl.textContent = step.description
          popup.appendChild(descEl)
        }

        if (this.showTechniques && step.techniques !== undefined && step.techniques.length > 0) {
          const techList = document.createElement('ul')
          techList.className = 'cmm-attack-path__popup-techniques'
          for (const techId of step.techniques) {
            const li = document.createElement('li')
            li.className = 'cmm-attack-path__popup-technique'
            li.textContent = techId
            techList.appendChild(li)
          }
          popup.appendChild(techList)
        }

        node.appendChild(popup)
      }

      // Click handler
      if (this.stepClickHandler !== undefined) {
        node.style.cursor = 'pointer'
        const handler = (e: Event) => {
          e.stopPropagation()
          if (this.stepClickHandler !== undefined) {
            this.stepClickHandler(step)
          }
        }
        node.addEventListener('click', handler)
        this.boundStepHandlers.push({ el: node, handler })
      }

      container.appendChild(node)

      // Arrow connector between steps
      if (i < this.currentSteps.length - 1) {
        const arrow = document.createElement('div')
        arrow.className = 'cmm-attack-path__arrow'
        arrow.setAttribute('aria-hidden', 'true')
        arrow.innerHTML = '&#8594;'
        container.appendChild(arrow)
      }
    }
  }

  private cleanupHandlers(): void {
    for (const entry of this.boundStepHandlers) {
      entry.el.removeEventListener('click', entry.handler)
    }
    this.boundStepHandlers.length = 0
  }

  private buildStepClassName(step: AttackStep): string {
    const classes = ['cmm-attack-path__step', `cmm-attack-path__step--${step.type}`]
    if (step.severity !== undefined) {
      classes.push(`cmm-attack-path__step--severity-${step.severity}`)
    }
    if (step.mitigated === true) {
      classes.push('cmm-attack-path__step--mitigated')
    }
    return classes.join(' ')
  }

  private buildRootClassName(extra?: string): string {
    const classes = ['cmm-attack-path']
    if (extra) {
      classes.push(extra)
    }
    return classes.join(' ')
  }
}
