/**
 * Tests for services/pdf.js
 *
 * Currently covers the replaceKatexWithLatex helper: the substitution step
 * that swaps rendered KaTeX elements for their raw LaTeX source before the
 * PDF walker extracts textContent. Regression-protects the fix for
 * top-level `$$\n...\n$$` display math being silently dropped from PDFs.
 */

import '@js/services/pdf.js';

const { replaceKatexWithLatex } = window.pdfService;

// Build a DOM with KaTeX-shaped markup. We construct it by hand (rather than
// driving marked + KaTeX) so the test does not depend on KaTeX's exact
// internal layout — only on the structural contract: `.katex-display` for
// display math, `.katex` for inline, both containing an `<annotation>` with
// the original LaTeX. That matches what marked-katex-extension always emits.
function el(tag, attrs = {}, ...children) {
    const node = document.createElement(tag);
    for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);
    for (const c of children) node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
    return node;
}

function makeKatexInline(latex) {
    return el('span', { class: 'katex' },
        el('span', { class: 'katex-mathml' },
            el('math', {},
                el('semantics', {},
                    el('mrow'),
                    el('annotation', { encoding: 'application/x-tex' }, latex)
                )
            )
        ),
        el('span', { class: 'katex-html', 'aria-hidden': 'true' }, 'rendered')
    );
}

function makeKatexDisplay(latex) {
    return el('span', { class: 'katex-display' }, makeKatexInline(latex));
}

describe('replaceKatexWithLatex', () => {
    let root;

    beforeEach(() => {
        root = document.createElement('div');
        document.body.appendChild(root);
    });

    afterEach(() => {
        root.remove();
    });

    it('replaces inline .katex with `$LATEX$` text node inside its parent', () => {
        const p = document.createElement('p');
        p.append('Energy is ', makeKatexInline('E=mc^2'), ' famous.');
        root.appendChild(p);

        replaceKatexWithLatex(root);

        // The parent <p> still exists and now reads cleanly.
        expect(root.children.length).toBe(1);
        expect(root.children[0].tagName).toBe('P');
        expect(root.children[0].textContent).toBe('Energy is $E=mc^2$ famous.');
        expect(root.querySelector('.katex')).toBeNull();
    });

    it('wraps top-level .katex-display in a <p> so the PDF walker sees it', () => {
        // Regression: previously, display math at top level (the canonical
        // multi-line `$$\n...\n$$` form) was replaced with a bare text node.
        // The PDF walker iterates contentDiv.children (Elements only — text
        // nodes are skipped), so display math was silently dropped.
        root.appendChild(document.createElement('p')).textContent = 'Before';
        root.appendChild(makeKatexDisplay('\\sum_{i=1}^n i'));
        root.appendChild(document.createElement('p')).textContent = 'After';

        replaceKatexWithLatex(root);

        const kids = Array.from(root.children);
        expect(kids.map((k) => k.tagName)).toEqual(['P', 'P', 'P']);
        expect(kids[0].textContent).toBe('Before');
        expect(kids[1].textContent).toBe('$$\\sum_{i=1}^n i$$');
        expect(kids[2].textContent).toBe('After');
    });

    it('handles back-to-back display math blocks', () => {
        root.appendChild(makeKatexDisplay('a'));
        root.appendChild(makeKatexDisplay('b'));

        replaceKatexWithLatex(root);

        const kids = Array.from(root.children);
        expect(kids.length).toBe(2);
        expect(kids[0].textContent).toBe('$$a$$');
        expect(kids[1].textContent).toBe('$$b$$');
    });

    it('handles display math nested inside another element', () => {
        const li = document.createElement('li');
        li.appendChild(makeKatexDisplay('x=1'));
        const ul = document.createElement('ul');
        ul.appendChild(li);
        root.appendChild(ul);

        replaceKatexWithLatex(root);

        // The wrap <p> sits inside the <li>; the walker iterates the <ul>
        // and the <li> textContent recovers the LaTeX.
        expect(root.querySelector('.katex-display')).toBeNull();
        expect(root.querySelector('li').textContent).toBe('$$x=1$$');
    });

    it('leaves elements without an annotation untouched', () => {
        // Defensive: if DOMPurify ever stripped <annotation>, the substitution
        // should skip rather than silently produce $$$$ or $$.
        const broken = el('span', { class: 'katex-display' },
            el('span', { class: 'katex-html' }, 'no annotation here')
        );
        root.appendChild(broken);

        replaceKatexWithLatex(root);

        // Unchanged.
        expect(root.querySelector('.katex-display')).not.toBeNull();
        expect(root.querySelector('.katex-display').textContent).toBe('no annotation here');
    });

    it('is a no-op on a container with no KaTeX', () => {
        const p = document.createElement('p');
        p.textContent = 'Plain text only.';
        root.appendChild(p);

        replaceKatexWithLatex(root);

        expect(root.children.length).toBe(1);
        expect(root.children[0].textContent).toBe('Plain text only.');
    });
});
