/**
 * MitreMatrix - MITRE ATT&CK matrix grid visualization
 * @countermeasure/web-components/security/mitre-matrix
 */

import { type MitreMatrixConfig, type MitreTechnique } from './types'
import { resolveContainer, uniqueId } from '../../primitives/utils'

export type { MitreMatrixConfig, MitreTechnique } from './types'

/** Default MITRE ATT&CK tactic columns */
const DEFAULT_TACTICS: readonly string[] = [
  'Initial Access',
  'Execution',
  'Persistence',
  'Privilege Escalation',
  'Defense Evasion',
  'Credential Access',
  'Discovery',
  'Lateral Movement',
  'Collection',
  'Command and Control',
  'Exfiltration',
  'Impact',
]

export class MitreMatrix {
  public readonly el: HTMLDivElement

  private readonly matrixId: string
  private readonly tactics: readonly string[]
  private readonly showCounts: boolean
  private readonly highlightDetected: boolean
  private readonly techniqueClickHandler: ((technique: MitreTechnique) => void) | undefined
  private currentTechniques: MitreTechnique[]
  private readonly boundTechHandlers: { el: HTMLElement; handler: (e: Event) => void }[] = []

  constructor(config: MitreMatrixConfig) {
    this.matrixId = uniqueId('cmm-mitre-matrix')
    this.tactics = config.tactics ?? DEFAULT_TACTICS
    this.showCounts = config.showCounts === true
    this.highlightDetected = config.highlightDetected !== false
    this.techniqueClickHandler = config.onTechniqueClick
    this.currentTechniques = config.techniques

    // Create root element
    this.el = document.createElement('div')
    this.el.id = this.matrixId
    this.el.className = this.buildRootClassName(config.className)
    this.el.setAttribute('role', 'grid')
    this.el.setAttribute('aria-label', 'MITRE ATT&CK Matrix')

    this.renderMatrix()

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

  /** Update the matrix with new technique data */
  update(techniques: MitreTechnique[]): void {
    this.currentTechniques = techniques
    this.cleanupHandlers()
    this.el.innerHTML = ''
    this.renderMatrix()
  }

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

  private renderMatrix(): void {
    // Group techniques by tactic
    const tacticMap = new Map<string, MitreTechnique[]>()
    for (const tactic of this.tactics) {
      tacticMap.set(tactic, [])
    }
    for (const tech of this.currentTechniques) {
      const existing = tacticMap.get(tech.tactic)
      if (existing !== undefined) {
        existing.push(tech)
      }
    }

    // Grid container (horizontal scroll wrapper)
    const grid = document.createElement('div')
    grid.className = 'cmm-mitre-matrix__grid'

    for (const tactic of this.tactics) {
      const column = document.createElement('div')
      column.className = 'cmm-mitre-matrix__column'
      column.setAttribute('role', 'gridcell')

      // Tactic header
      const header = document.createElement('div')
      header.className = 'cmm-mitre-matrix__tactic'
      header.textContent = tactic
      column.appendChild(header)

      // Techniques in this column
      const techniques = tacticMap.get(tactic)
      if (techniques !== undefined) {
        for (const tech of techniques) {
          const techEl = document.createElement('div')
          techEl.className = this.buildTechClassName(tech)
          techEl.title = `${tech.id}: ${tech.name}`

          // Technique name
          const nameEl = document.createElement('span')
          nameEl.className = 'cmm-mitre-matrix__tech-name'
          nameEl.textContent = tech.name
          techEl.appendChild(nameEl)

          // Technique ID
          const idEl = document.createElement('span')
          idEl.className = 'cmm-mitre-matrix__tech-id'
          idEl.textContent = tech.id
          techEl.appendChild(idEl)

          // Count badge
          if (this.showCounts && tech.count !== undefined && tech.count > 0) {
            const countEl = document.createElement('span')
            countEl.className = 'cmm-mitre-matrix__tech-count'
            countEl.textContent = String(tech.count)
            techEl.appendChild(countEl)
          }

          // Tooltip (hover details)
          const tooltip = document.createElement('div')
          tooltip.className = 'cmm-mitre-matrix__tooltip'
          tooltip.setAttribute('role', 'tooltip')

          const tooltipId = document.createElement('div')
          tooltipId.className = 'cmm-mitre-matrix__tooltip-id'
          tooltipId.textContent = tech.id
          tooltip.appendChild(tooltipId)

          const tooltipName = document.createElement('div')
          tooltipName.className = 'cmm-mitre-matrix__tooltip-name'
          tooltipName.textContent = tech.name
          tooltip.appendChild(tooltipName)

          if (tech.detected === true) {
            const detectedLabel = document.createElement('div')
            detectedLabel.className = 'cmm-mitre-matrix__tooltip-detected'
            detectedLabel.textContent = 'Detected'
            tooltip.appendChild(detectedLabel)
          }

          if (tech.count !== undefined && tech.count > 0) {
            const countLabel = document.createElement('div')
            countLabel.className = 'cmm-mitre-matrix__tooltip-count'
            countLabel.textContent = `Detections: ${String(tech.count)}`
            tooltip.appendChild(countLabel)
          }

          techEl.appendChild(tooltip)

          // Click handler
          if (this.techniqueClickHandler !== undefined) {
            techEl.style.cursor = 'pointer'
            const handler = (e: Event) => {
              e.stopPropagation()
              if (this.techniqueClickHandler !== undefined) {
                this.techniqueClickHandler(tech)
              }
            }
            techEl.addEventListener('click', handler)
            this.boundTechHandlers.push({ el: techEl, handler })
          }

          column.appendChild(techEl)
        }
      }

      grid.appendChild(column)
    }

    this.el.appendChild(grid)
  }

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

  private buildTechClassName(tech: MitreTechnique): string {
    const classes = ['cmm-mitre-matrix__technique']
    if (this.highlightDetected && tech.detected === true) {
      classes.push('cmm-mitre-matrix__technique--detected')
    }
    if (tech.severity !== undefined) {
      classes.push(`cmm-mitre-matrix__technique--${tech.severity}`)
    }
    return classes.join(' ')
  }

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