/**
 * HTML sanitization utilities
 * @countermeasure/web-components/utils/sanitize
 */

import DOMPurify from 'dompurify'

export interface SanitizeHtmlOptions {
  allowSvg?: boolean
}

function sanitizeConfig(options?: SanitizeHtmlOptions): Record<string, unknown> {
  return {
    USE_PROFILES: options?.allowSvg === true ? { svg: true, html: true } : { html: true },
    ADD_ATTR: ['target', 'rel'],
    FORBID_ATTR: [],
    WHOLE_DOCUMENT: false,
  }
}

let _hooksInstalled = false

function ensureDomPurifyHooks(): void {
  if (_hooksInstalled) return
  _hooksInstalled = true
  DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => {
    if (node.getAttribute('target') === '_blank') {
      node.setAttribute('rel', 'noopener noreferrer')
    }
  })
}

/** Escape HTML special characters to prevent injection in text content */
export function escapeHtml(value: string): string {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}

/** Sanitize HTML to remove dangerous content while preserving safe markup */
export function sanitizeHtml(html: string, options?: SanitizeHtmlOptions): string {
  ensureDomPurifyHooks()
  return DOMPurify.sanitize(html, sanitizeConfig(options))
}

export function createSafeMarkupFragment(
  markup: string,
  options?: SanitizeHtmlOptions
): DocumentFragment {
  ensureDomPurifyHooks()
  return DOMPurify.sanitize(markup, {
    ...sanitizeConfig(options),
    RETURN_DOM_FRAGMENT: true,
  }) as unknown as DocumentFragment
}

export function createSafeHtmlFragment(
  html: string,
  options?: SanitizeHtmlOptions
): DocumentFragment {
  return createSafeMarkupFragment(html, options)
}

export function setSafeMarkup(
  element: Element,
  markup: string,
  options?: SanitizeHtmlOptions
): void {
  element.replaceChildren(createSafeMarkupFragment(markup, options))
}

/** Safely assign sanitized HTML into an element. */
export function setSafeHtml(element: Element, html: string, options?: SanitizeHtmlOptions): void {
  setSafeMarkup(element, html, options)
}

export function setSafeIconMarkup(element: Element, markup: string): void {
  if (
    element.namespaceURI === 'http://www.w3.org/2000/svg' &&
    element.localName.toLowerCase() === 'svg'
  ) {
    const fragment = createSafeMarkupFragment(
      `<svg xmlns="http://www.w3.org/2000/svg">${markup}</svg>`,
      { allowSvg: true }
    )
    const svg = fragment.querySelector('svg')
    element.replaceChildren(...(svg === null ? [] : Array.from(svg.childNodes)))
    return
  }
  setSafeMarkup(element, markup, { allowSvg: true })
}

/** Safely assign icon markup while preserving inline SVG support. */
export function setSafeIconHtml(element: Element, html: string): void {
  setSafeIconMarkup(element, html)
}
