/**
 * Shared test harness for pages/news.js render-path specs.
 *
 * Both news-xss.test.js and news-impact-clamp.test.js drive the same render
 * cycle (loadNewsFeed -> safeRenderHTML -> window.DOMPurify.sanitize) and
 * assert on the *pre-sanitized* htmlString the template produced. This module
 * owns the scaffold they previously cloned: the item factory, the DOMPurify
 * spy + fetch stub + DOM fixture installer, and the render/capture helpers.
 *
 * Vitest isolates modules per test file, so each spec that calls
 * installNewsRenderHarness() in beforeAll gets its own fresh news.js import
 * and spy — no cross-file state leaks.
 */

import { vi } from 'vitest';

// Minimal DOM the news page wires up in initializeNewsPage(); without these
// ids/classes the render path throws before producing any sanitize() call.
const NEWS_FIXTURE_HTML = `
    <div id="news-feed-content"></div>
    <input id="table-view-toggle" type="checkbox">
    <tbody id="news-table-body"></tbody>
    <input id="news-search" type="text">
    <button id="search-btn"></button>
    <button id="create-subscription-btn"></button>
    <button id="run-template-btn"></button>
    <input id="impact-filter" type="range" min="0" max="10" value="0">
    <span class="ldr-impact-value"></span>
    <div id="sidebar-subscriptions-list"></div>
    <div id="news-semantic-results"></div>
    <div id="news-query"></div>
    <div id="newsSubscriptionModal"></div>
    <div id="news-subscription-query"></div>
    <div class="ldr-time-filter-group"><button class="ldr-filter-btn"></button></div>
    <input id="auto-refresh" type="checkbox">
    <button id="refresh-feed-btn"></button>
`;

/** A well-formed news item; pass overrides to control the field under test. */
export function makeNewsItem(overrides = {}) {
    return {
        id: 'news-item',
        headline: 'Safe headline',
        category: 'General',
        impact_score: 5,
        upvotes: 0,
        downvotes: 0,
        created_at: new Date().toISOString(),
        time_ago: 'just now',
        topics: [],
        links: [],
        ...overrides,
    };
}

/**
 * Install the news render harness. Call once from a spec's beforeAll.
 *
 * Stubs window.DOMPurify.sanitize with a spy that returns a real
 * DocumentFragment (so safeRenderHTML can append it) while capturing the
 * incoming pre-sanitized htmlString, stubs fetch so only /news/api/feed sees
 * the per-render payload, installs the DOM fixture, and imports news.js.
 *
 * Returns:
 *   - renderWith(items): drive one loadNewsFeed() round-trip with `items`.
 *   - capturedHtmlFor(substring): the pre-sanitized htmlString containing
 *     `substring` (typically a unique headline), or null.
 *   - sanitizeSpy: the underlying vi.fn for assertions that need raw calls.
 */
export async function installNewsRenderHarness() {
    // xss-protection.js provides window.escapeHtml, which news.js relies on as
    // a global (loaded by base.html before pages/news.js in production).
    await import('@js/security/xss-protection.js');

    const sanitizeSpy = vi.fn((htmlString) => {
        const template = document.createElement('template');
        // eslint-disable-next-line no-unsanitized/property -- test-only spy: htmlString is the pre-sanitized template output we explicitly capture for assertions; runs only under Vitest, never in production.
        template.innerHTML = htmlString;
        return template.content;
    });
    window.DOMPurify = { sanitize: sanitizeSpy };

    // Swapped per render to control the payload; only /news/api/feed returns
    // it so the other endpoints initializeNewsPage touches no-op.
    let feedResponse = { news_items: [] };
    globalThis.fetch = vi.fn((url) => {
        const body = String(url).includes('/news/api/feed') ? feedResponse : {};
        return Promise.resolve({
            ok: true,
            status: 200,
            json: () => Promise.resolve(body),
            text: () => Promise.resolve(''),
        });
    });

    // eslint-disable-next-line no-unsanitized/property -- test-only: NEWS_FIXTURE_HTML is a static fixture constant defined in this file, not user input; runs only under Vitest.
    document.body.innerHTML = NEWS_FIXTURE_HTML;
    await import('@js/pages/news.js');

    async function renderWith(items) {
        sanitizeSpy.mockClear();
        feedResponse = { news_items: items };
        await window.loadNewsFeed();
        // loadVotesForNewsItems fires unconditionally after render; let it
        // settle so it doesn't pollute the next render's spy captures.
        await new Promise((resolve) => setTimeout(resolve, 10));
    }

    function capturedHtmlFor(substring) {
        for (const [htmlString] of sanitizeSpy.mock.calls) {
            if (
                typeof htmlString === 'string' &&
                htmlString.includes(substring)
            ) {
                return htmlString;
            }
        }
        return null;
    }

    return { sanitizeSpy, renderWith, capturedHtmlFor };
}
