/**
 * Utility functions do podświetlania wyszukiwanych fraz
 * Używane przez komponenty search
 */

/**
 * Escape RegExp special characters
 */
export function escapeRegExp(str) {
  return String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

/**
 * Podświetlenie w czystym tekście (np. title)
 * 
 * @param {string} text - Tekst do podświetlenia
 * @param {string} query - Fraza do wyszukania
 * @returns {string|Array} - Tekst lub array z elementami JSX
 * 
 * @example
 * highlightText("Hello World", "world")
 * // => ["Hello ", <mark class="f-hl">World</mark>]
 */
export function highlightText(text, query) {
  const trimmedQuery = (query || '').trim();
  if (!trimmedQuery) return text;

  const re = new RegExp(escapeRegExp(trimmedQuery), 'ig');
  const parts = String(text || '').split(re);

  if (parts.length === 1) return text;

  // split() gubi match – więc budujemy przez exec/match
  const matches = String(text || '').match(re) || [];

  const result = [];
  for (let i = 0; i < parts.length; i++) {
    result.push(parts[i]);
    if (i < matches.length) {
      result.push(<mark class="f-hl">{matches[i]}</mark>);
    }
  }
  
  return result;
}

/**
 * Podświetlenie wewnątrz HTML (po markdown)
 * Omija tagi PRE, CODE, SCRIPT, STYLE
 * 
 * @param {string} html - HTML do podświetlenia
 * @param {string} query - Fraza do wyszukania
 * @returns {string} - HTML z podświetleniem
 * 
 * @example
 * const html = "<p>Hello World</p>";
 * highlightHtml(html, "world")
 * // => "<p>Hello <mark class="f-hl">World</mark></p>"
 */
export function highlightHtml(html, query) {
  const trimmedQuery = (query || '').trim();
  if (!trimmedQuery) return html;

  const re = new RegExp(escapeRegExp(trimmedQuery), 'ig');

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

  const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);

  // Funkcja sprawdzająca czy node jest w tagu do pominięcia
  const shouldSkipNode = (node) => {
    const parent = node.parentElement;
    if (!parent) return true;
    
    const tagName = parent.tagName;
    return tagName === 'SCRIPT' || 
           tagName === 'STYLE' || 
           tagName === 'CODE' || 
           tagName === 'PRE';
  };

  // Zbierz wszystkie text nodes
  const textNodes = [];
  let node;
  while ((node = walker.nextNode())) {
    textNodes.push(node);
  }

  // Podświetl każdy text node
  for (const textNode of textNodes) {
    if (shouldSkipNode(textNode)) continue;

    const text = textNode.nodeValue || '';
    if (!re.test(text)) continue;

    // Reset RegExp state (test() z /g/ przesuwa lastIndex)
    re.lastIndex = 0;

    const fragment = doc.createDocumentFragment();
    let lastIndex = 0;
    let match;

    while ((match = re.exec(text))) {
      const matchStart = match.index;
      const matchEnd = matchStart + match[0].length;

      // Tekst przed match
      if (matchStart > lastIndex) {
        fragment.appendChild(
          doc.createTextNode(text.slice(lastIndex, matchStart))
        );
      }

      // Match w <mark>
      const mark = doc.createElement('mark');
      mark.className = 'f-hl';
      mark.textContent = text.slice(matchStart, matchEnd);
      fragment.appendChild(mark);

      lastIndex = matchEnd;
    }

    // Tekst po ostatnim match
    if (lastIndex < text.length) {
      fragment.appendChild(doc.createTextNode(text.slice(lastIndex)));
    }

    // Zastąp text node fragmentem
    textNode.parentNode?.replaceChild(fragment, textNode);
  }

  return root.innerHTML;
}