import { t as tryGetDocument } from './try-get-document-bef0f526.js';

/**
 * Serializes a DocumentFragment into HTML string.
 *
 * If fragment contains no element nodes,
 * returns textContent to avoid encoding issues.
 */
/**
 * @param {DocumentFragment} fragment - fragment to serialize.
 * @param {Document} doc - document reference.
 * @returns {string} - Serialized string
 */
function serializeFragment(fragment, doc) {
  var _a;
  const container = doc.createElement('div');
  container.appendChild(fragment);
  return container.children.length === 0 ? (_a = container.textContent) !== null && _a !== void 0 ? _a : '' : container.innerHTML;
}

/**
 * Main sanitization engine.
 *
 * Responsible for:
 * - Parsing HTML
 * - Delegating sanitization to strategy
 * - Serializing safe output
 */
class HtmlSanitizer {
  /**
   * Sanitizes the given string using the provided sanitization strategy.
   *
   * This is the single entry point for HTML sanitization.
   * Always returns a safe string for DOM insertion.
   *
   * @param {string} input - Raw string input to be sanitized.
   * @param {SanitizeStrategy} strategy - Sanitization strategy to apply.
   * @returns { string } Sanitized string safe for rendering.
   */
  sanitize(input, strategy) {
    if (!input || input.trim() === '') {
      return '';
    }
    const doc = tryGetDocument();
    if (!doc)
      return '';
    const parser = new DOMParser();
    const parsed = parser.parseFromString(input, 'text/html');
    const fragment = doc.createDocumentFragment();
    parsed.body.childNodes.forEach((node) => {
      const clean = strategy.execute(node, doc);
      if (clean)
        fragment.appendChild(clean);
    });
    return serializeFragment(fragment, doc);
  }
}

/**
 * Base class for all sanitization strategies.
 *
 * Strategy defines:
 * - Allowed tags
 * - Allowed attributes per tag
 * - Text preservation behavior
 */
class SanitizeStrategy {
  constructor() {
    this.preserveTextContent = true;
  }
  /**
   * @param {Node} node - Node to sanitize.
   * @param {Document} doc - document reference.
   * @returns {Nullable<Node>} - Sanitized node
   */
  execute(node, doc) {
    var _a;
    switch (node.nodeType) {
      case Node.TEXT_NODE:
        return this.preserveTextContent ? doc.createTextNode((_a = node.textContent) !== null && _a !== void 0 ? _a : '') : null;
      case Node.ELEMENT_NODE:
        return this.sanitizeElement(node, doc);
      default:
        return null;
    }
  }
  /**
   * @param {HTMLElement} element - HTMLElement to sanitize.
   * @param {Document} doc - document reference.
   * @returns {Nullable<Node>} - Sanitized node
   */
  sanitizeElement(element, doc) {
    var _a;
    const tagName = element.tagName;
    if (!this.allowedTags.has(tagName)) {
      const fragment = doc.createDocumentFragment();
      element.childNodes.forEach((child) => {
        const clean = this.execute(child, doc);
        if (clean)
          fragment.appendChild(clean);
      });
      return fragment;
    }
    const cleanElement = doc.createElement(tagName.toLowerCase());
    const allowedAttrs = (_a = this.allowedAttributes) === null || _a === void 0 ? void 0 : _a.get(tagName);
    if (allowedAttrs) {
      Array.from(element.attributes).forEach((attr) => {
        if (allowedAttrs.has(attr.name)) {
          cleanElement.setAttribute(attr.name, attr.value);
        }
      });
    }
    element.childNodes.forEach((child) => {
      const clean = this.execute(child, doc);
      if (clean)
        cleanElement.appendChild(clean);
    });
    return cleanElement;
  }
}

/**
 * Set of allowed HTML tags for label rendering.
 * Tags must be uppercase to match DOMParser element.tagName output.
 */
const LABEL_ALLOWED_TAGS = new Set(['B', 'I', 'STRONG', 'EM']);

/**
 * Label HTML sanitization strategy.
 *
 * Allows minimal formatting tags only.
 */
class LabelSanitizeStrategy extends SanitizeStrategy {
  constructor() {
    super(...arguments);
    this.allowedTags = LABEL_ALLOWED_TAGS;
  }
}

/**
 * CSS / style sanitization strategy.
 *
 * - Strips all HTML elements
 * - Preserves text nodes
 * - Blocks dangerous CSS patterns (expression(), javascript:)
 */
class StyleSanitizeStrategy extends SanitizeStrategy {
  constructor() {
    super(...arguments);
    this.allowedTags = new Set();
    this.preserveTextContent = true;
  }
  /**
   * @param {Node} node - Node to sanitize.
   * @param {Document} doc - document reference.
   * @returns {Nullable<Node>} - Sanitized node
   */
  execute(node, doc) {
    var _a;
    switch (node.nodeType) {
      case Node.TEXT_NODE: {
        const css = (_a = node.textContent) !== null && _a !== void 0 ? _a : '';
        // Strip dangerous CSS patterns
        return this.isSafeCss(css) ? doc.createTextNode(css) : null;
      }
      case Node.ELEMENT_NODE: {
        // Recurse into children but discard the element itself
        const fragment = doc.createDocumentFragment();
        node.childNodes.forEach((child) => {
          const clean = this.execute(child, doc);
          if (clean)
            fragment.appendChild(clean);
        });
        return fragment;
      }
      default:
        return null;
    }
  }
  /**
   * @param {string} css - CSS string.
   * @returns {boolean} - True or false
   */
  isSafeCss(css) {
    // Block known XSS patterns
    return !/expression\s*\(|javascript:|url\s*\(/i.test(css);
  }
}

/**
 * Strategy that always returns empty output.
 */
class EmptySanitizeStrategy extends SanitizeStrategy {
  constructor() {
    super(...arguments);
    this.allowedTags = new Set();
    this.preserveTextContent = false;
  }
  /**
   * @returns {Nullable<Node>} - Sanitized node
   */
  execute() {
    return null;
  }
}

/**
 * Public Sanitization API
 */
const htmlSanitizer = new HtmlSanitizer();
const labelSanitizeStrategy = new LabelSanitizeStrategy();
const styleSanitizeStrategy = new StyleSanitizeStrategy();
const emptySanitizeStrategy = new EmptySanitizeStrategy();

export { emptySanitizeStrategy as e, htmlSanitizer as h, labelSanitizeStrategy as l, styleSanitizeStrategy as s };
