/**
 * Sanitizer für Übungs-Rich-HTML inkl. §11 Platzhalter (span data-shinkan-exercise-media).
 * Restriktiver als sanitizeTrainerHtml: Allowlist für XSS-Minimierung.
 */

import { sanitizeInlineMediaCaption } from './inlineMediaCaption'

const ALLOWED_BLOCK = new Set(['p', 'div', 'br', 'ul', 'ol', 'li', 'h3'])
const ALLOWED_INLINE = new Set(['b', 'strong', 'i', 'em', 'u', 'span', 'a'])

function isHttpsUrl(val) {
  if (!val || typeof val !== 'string') return false
  const s = val.trim()
  return s.startsWith('http://') || s.startsWith('https://')
}

/** Nur für unsere Embed-Markierung: erlaubt data-attribut und optionale Marker-Klasse + Größe. */
const _SIZE_OK = new Set(['small', 'medium', 'full'])
function isInlineExerciseMediaPlaceholderSpan(el) {
  if (!el?.getAttribute || el.tagName.toLowerCase() !== 'span') return false
  const raw = el.getAttribute('data-shinkan-exercise-media')
  if (!raw || !String(raw).trim().match(/^\d+$/)) return false
  return true
}

function sanitizeAttributes(el, tagLower) {
  if (tagLower === 'a') {
    const href = el.getAttribute('href')
    if (href && isHttpsUrl(href)) {
      const out = document.createElement('a')
      out.setAttribute('href', href.trim())
      return out.attributes
    }
    return []
  }
  if (tagLower === 'span' && isInlineExerciseMediaPlaceholderSpan(el)) {
    const out = document.createElement('span')
    out.setAttribute('data-shinkan-exercise-media', el.getAttribute('data-shinkan-exercise-media').trim())
    const sz = (el.getAttribute('data-shinkan-exercise-media-size') || '').trim().toLowerCase()
    if (sz && _SIZE_OK.has(sz)) {
      out.setAttribute('data-shinkan-exercise-media-size', sz)
    }
    const capRaw = el.getAttribute('data-shinkan-exercise-media-caption')
    if (capRaw != null && String(capRaw).trim()) {
      const cap = sanitizeInlineMediaCaption(String(capRaw))
      if (cap) out.setAttribute('data-shinkan-exercise-media-caption', cap)
    }
    const cls = (el.getAttribute('class') || '').trim().split(/\s+/).filter(Boolean)
    const keep = cls.filter((c) => c === 'shinkan-inline-media')
    if (keep.length) out.setAttribute('class', keep.join(' '))
    return out.attributes
  }
  return []
}

function cloneAsAllowed(tagLower, el) {
  const fresh = document.createElement(tagLower)
  const attrs = sanitizeAttributes(el, tagLower)
  for (const a of attrs) {
    fresh.setAttribute(a.name, a.value)
  }
  return fresh
}

function cleanTree(parent) {
  const nodes = Array.from(parent.childNodes)
  for (const node of nodes) {
    if (node.nodeType === Node.TEXT_NODE) continue
    if (node.nodeType === Node.COMMENT_NODE) {
      parent.removeChild(node)
      continue
    }
    if (node.nodeType !== Node.ELEMENT_NODE) {
      parent.removeChild(node)
      continue
    }
    const tag = node.tagName.toLowerCase()
    if (tag === 'script' || tag === 'iframe' || tag === 'object' || tag === 'embed') {
      parent.removeChild(node)
      continue
    }
    if (ALLOWED_BLOCK.has(tag) || ALLOWED_INLINE.has(tag)) {
      const repl = cloneAsAllowed(tag, node)
      while (node.firstChild) {
        repl.appendChild(node.firstChild)
      }
      cleanTree(repl)
      parent.replaceChild(repl, node)
      continue
    }
    while (node.firstChild) {
      parent.insertBefore(node.firstChild, node)
    }
    parent.removeChild(node)
  }
}

/**
 * @param {string|null|undefined} html
 * @returns {string}
 */
export function sanitizeExerciseRichDisplayHtml(html) {
  if (html == null || typeof html !== 'string') return ''
  const trimmed = html.trim()
  if (!trimmed) return ''

  const tpl = document.createElement('template')
  tpl.innerHTML = trimmed
  cleanTree(tpl.content)
  return tpl.innerHTML
}
