// Whitelist-based HTML sanitizer for rendering third-party post
// descriptions (e.g. Patreon) via v-html. Strips script/style/iframe
// + event handlers + dangerous hrefs. Tag whitelist covers what
// Patreon, SubscribeStar, and similar platforms ship in normal posts.
//
// Not a substitute for server-side sanitization in higher-stakes
// contexts. This is for FC's single-operator homelab posture where
// the content source is the operator's own subscriptions.

const ALLOWED_TAGS = new Set([
  'a', 'b', 'blockquote', 'br', 'code', 'div', 'em', 'figure',
  'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li',
  'ol', 'p', 'pre', 's', 'span', 'strong', 'sub', 'sup', 'u', 'ul',
])

const ALLOWED_ATTRS = {
  a: new Set(['href', 'title', 'rel', 'target']),
  img: new Set(['src', 'alt', 'title', 'width', 'height']),
  // any tag → these always allowed
  '*': new Set(['class']),
}

const SAFE_URL_RE = /^(https?:|mailto:|#|\/)/i

export function sanitizeHtml (html) {
  if (typeof html !== 'string' || !html) return ''
  const doc = new DOMParser().parseFromString(html, 'text/html')
  _scrubNode(doc.body)
  return doc.body.innerHTML
}

// Strip all tags + decode entities to plain text. Used for titles that
// arrive as stored HTML (e.g. "<strong>Edelgard at the Sex-Arcade</strong>")
// which must render as text, not literal markup. DOMParser yields an inert
// document (no script execution, no resource loads), so reading textContent
// is safe.
export function toPlainText (html) {
  if (typeof html !== 'string' || !html) return ''
  const doc = new DOMParser().parseFromString(html, 'text/html')
  return (doc.body.textContent || '').trim()
}

function _scrubNode (node) {
  const children = Array.from(node.children)
  for (const child of children) {
    const tag = child.tagName.toLowerCase()
    if (!ALLOWED_TAGS.has(tag)) {
      // Strip the tag but keep its text content as a fallback.
      const text = document.createTextNode(child.textContent || '')
      child.replaceWith(text)
      continue
    }
    const allowed = new Set([
      ...(ALLOWED_ATTRS[tag] || []),
      ...(ALLOWED_ATTRS['*'] || []),
    ])
    for (const attr of Array.from(child.attributes)) {
      const name = attr.name.toLowerCase()
      if (name.startsWith('on') || !allowed.has(name)) {
        child.removeAttribute(attr.name)
        continue
      }
      if ((name === 'href' || name === 'src') && !SAFE_URL_RE.test(attr.value)) {
        child.removeAttribute(attr.name)
      }
    }
    if (tag === 'a' && child.getAttribute('target') === '_blank') {
      child.setAttribute('rel', 'noopener noreferrer')
    }
    _scrubNode(child)
  }
}
