const REMOVABLE_TAGS = new Set([
  'style',
  'script',
  'meta',
  'link',
  'title',
  'xml',
  'head',
  'iframe',
  'object',
  'embed',
  'noscript',
]);

const KEEP_CLASS_PATTERNS = [
  /^ql-align-(left|center|right|justify)$/,
  /^ql-indent-\d+$/,
  /^page-break-marker$/,
  /^var-pill$/,
];

const SAFE_STYLE_PROPERTIES = new Set([
  'color',
  'background-color',
  'font-size',
  'font-family',
  'font-weight',
  'font-style',
  'line-height',
  'text-decoration',
  'text-align',
  'margin-top',
  'margin-bottom',
  'margin-left',
  'padding-left',
  'text-indent',
]);

const SAFE_LINK_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
const SAFE_IMAGE_PATTERN = /^(https?:|data:image\/|blob:|\/)/i;
const OFFICE_TAG_PATTERN = /^[ovwmr]:/i;

function keepClassName(token) {
  return KEEP_CLASS_PATTERNS.some((pattern) => pattern.test(token));
}

function sanitizeStyle(styleText = '') {
  return String(styleText)
    .split(';')
    .map((rule) => rule.trim())
    .filter(Boolean)
    .map((rule) => {
      const [rawProperty, ...rawValueParts] = rule.split(':');
      if (!rawProperty || rawValueParts.length === 0) return null;

      const property = rawProperty.trim().toLowerCase();
      const value = rawValueParts.join(':').trim();

      if (!SAFE_STYLE_PROPERTIES.has(property)) return null;
      if (!value || /expression\s*\(|javascript:/i.test(value) || /mso-/i.test(value)) return null;

      return `${property}: ${value}`;
    })
    .filter(Boolean)
    .join('; ');
}

function createPageBreakMarker(doc) {
  const marker = doc.createElement('div');
  marker.className = 'page-break-marker';
  marker.setAttribute('data-page-break', 'true');

  const label = doc.createElement('span');
  label.textContent = '— PAGE BREAK —';
  marker.appendChild(label);

  return marker;
}

function unwrapElement(element) {
  const parent = element.parentNode;
  if (!parent) {
    element.remove();
    return;
  }

  while (element.firstChild) {
    parent.insertBefore(element.firstChild, element);
  }

  element.remove();
}

function sanitizeElementAttributes(element) {
  const tag = element.tagName.toLowerCase();
  const pageBreakBefore = `${element.style?.pageBreakBefore || ''} ${element.style?.breakBefore || ''}`.trim();
  const shouldInsertPageBreak = /always|page/i.test(pageBreakBefore);

  if (tag === 'br' && shouldInsertPageBreak) {
    element.replaceWith(createPageBreakMarker(element.ownerDocument));
    return false;
  }

  if (shouldInsertPageBreak) {
    element.parentNode?.insertBefore(createPageBreakMarker(element.ownerDocument), element);
    element.style.removeProperty('page-break-before');
    element.style.removeProperty('break-before');
  }

  [...element.attributes].forEach((attribute) => {
    const name = attribute.name.toLowerCase();
    const value = attribute.value.trim();

    if (name === 'class') {
      const className = value
        .split(/\s+/)
        .filter(Boolean)
        .filter(keepClassName)
        .join(' ');

      if (className) element.setAttribute('class', className);
      else element.removeAttribute(attribute.name);
      return;
    }

    if (name === 'style') {
      const cleanedStyle = sanitizeStyle(value);
      if (cleanedStyle) element.setAttribute('style', cleanedStyle);
      else element.removeAttribute(attribute.name);
      return;
    }

    if (name === 'href' && tag === 'a') {
      if (SAFE_LINK_PATTERN.test(value)) {
        element.setAttribute('href', value);
        if (/^https?:/i.test(value)) {
          element.setAttribute('target', '_blank');
          element.setAttribute('rel', 'noopener noreferrer');
        }
      } else {
        element.removeAttribute(attribute.name);
      }
      return;
    }

    if (name === 'src' && tag === 'img') {
      if (SAFE_IMAGE_PATTERN.test(value)) element.setAttribute('src', value);
      else element.removeAttribute(attribute.name);
      return;
    }

    if ((name === 'colspan' || name === 'rowspan' || name === 'width' || name === 'height' || name === 'start' || name === 'value') && /^\d+$/i.test(value)) {
      return;
    }

    if (name === 'alt' && tag === 'img') return;
    if (name === 'align' && /^(left|center|right|justify)$/i.test(value)) return;
    if (name === 'data-page-break' && value === 'true') return;

    element.removeAttribute(attribute.name);
  });

  return true;
}

function sanitizeNodeTree(root) {
  [...root.childNodes].forEach((child) => {
    if (child.nodeType === Node.COMMENT_NODE) {
      child.remove();
      return;
    }

    if (child.nodeType !== Node.ELEMENT_NODE) return;

    const element = child;
    const tag = element.tagName.toLowerCase();

    if (tag === 'html' || tag === 'body') {
      sanitizeNodeTree(element);
      unwrapElement(element);
      return;
    }

    if (REMOVABLE_TAGS.has(tag) || OFFICE_TAG_PATTERN.test(tag)) {
      element.remove();
      return;
    }

    const shouldKeep = sanitizeElementAttributes(element);
    if (!shouldKeep || !element.isConnected) return;

    sanitizeNodeTree(element);

    if (tag === 'span' && !element.attributes.length) {
      unwrapElement(element);
    }
  });
}

export function sanitizeRichTextHtml(html = '') {
  if (!html || typeof html !== 'string') return '';

  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/html');
  const body = doc.body;

  if (!body) return html;

  sanitizeNodeTree(body);

  return body.innerHTML
    .replace(/<!--[\s\S]*?-->/g, '')
    .replace(/\uFEFF/g, '')
    .trim();
}

export default sanitizeRichTextHtml;