#!/usr/bin/env node
/**
 * News Feed & Subscriptions UI Tests
 *
 * Tests for the news feed page including filters, templates,
 * and subscription management.
 *
 * Run: node test_news_feed_ci.js
 */

const { setupTest, teardownTest, TestResults, log, delay, navigateTo, withTimeout, clickAndWaitForNavigation } = require('./test_lib');

// ============================================================================
// News Feed Page Tests
// ============================================================================
const NewsFeedPageTests = {
    async newsFeedPageLoads(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const hasContent = document.body.textContent.length > 100;
            const title = document.title.toLowerCase();
            const hasNewsContent = title.includes('news') ||
                                  !!document.querySelector('.news, #news, [class*="news-feed"]');

            return {
                hasContent,
                hasNewsContent,
                title,
                url: window.location.href
            };
        });

        return {
            passed: result.hasContent,
            message: `News feed page loads (title: "${result.title}")`
        };
    },

    async searchFunctionality(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const searchInput = document.querySelector(
                'input[type="search"], ' +
                'input[placeholder*="search"], ' +
                '#news-search, ' +
                '.search-input'
            );

            const searchBtn = document.querySelector(
                'button[type="submit"], ' +
                '.search-btn, ' +
                '[class*="search-button"]'
            );

            return {
                hasSearchInput: !!searchInput,
                hasSearchBtn: !!searchBtn,
                placeholder: searchInput?.placeholder
            };
        });

        return {
            passed: result.hasSearchInput,
            message: `Search: input=${result.hasSearchInput}, button=${result.hasSearchBtn}, placeholder="${result.placeholder}"`
        };
    },

    async refreshFeedButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn'));
            const refreshBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const title = btn.getAttribute('title')?.toLowerCase() || '';
                return text.includes('refresh') || text.includes('reload') ||
                       title.includes('refresh') || btn.className.includes('refresh');
            });

            return {
                hasRefreshBtn: !!refreshBtn,
                buttonText: refreshBtn?.textContent?.trim()
            };
        });

        if (!result.hasRefreshBtn) {
            return { passed: null, skipped: true, message: 'No refresh feed button found' };
        }

        return {
            passed: true,
            message: `Refresh button: "${result.buttonText}"`
        };
    },

    async autoRefreshToggle(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const toggle = document.querySelector(
                'input[name*="auto-refresh"], ' +
                '#auto-refresh, ' +
                '.auto-refresh-toggle, ' +
                '[class*="auto-refresh"]'
            );

            // Also check for checkbox with label
            const checkboxes = document.querySelectorAll('input[type="checkbox"]');
            let autoRefreshCheckbox = null;
            for (const cb of checkboxes) {
                const label = document.querySelector(`label[for="${cb.id}"]`);
                const text = (label?.textContent || cb.parentElement?.textContent || '').toLowerCase();
                if (text.includes('auto') && text.includes('refresh')) {
                    autoRefreshCheckbox = cb;
                    break;
                }
            }

            return {
                hasToggle: !!toggle || !!autoRefreshCheckbox,
                isCheckbox: autoRefreshCheckbox?.type === 'checkbox'
            };
        });

        if (!result.hasToggle) {
            return { passed: null, skipped: true, message: 'No auto-refresh toggle found' };
        }

        return {
            passed: true,
            message: `Auto-refresh toggle: checkbox=${result.isCheckbox}`
        };
    }
};

// ============================================================================
// News Filters Tests
// ============================================================================
const NewsFiltersTests = {
    async subscriptionFilter(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const filter = document.querySelector(
                'select[name*="subscription"], ' +
                '#subscription-filter, ' +
                '.subscription-filter, ' +
                '[class*="subscription-dropdown"]'
            );

            if (!filter) return { hasFilter: false };

            const options = filter.tagName === 'SELECT'
                ? Array.from(filter.options).map(o => o.text)
                : [];

            return {
                hasFilter: true,
                optionCount: options.length,
                options: options.slice(0, 5)
            };
        });

        if (!result.hasFilter) {
            return { passed: null, skipped: true, message: 'No subscription filter found' };
        }

        return {
            passed: true,
            message: `Subscription filter: ${result.optionCount} options (${result.options.join(', ')})`
        };
    },

    async timeFilterButtons(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, .filter-btn'));

            const timeButtons = {
                all: buttons.find(b => b.textContent?.toLowerCase().includes('all')),
                today: buttons.find(b => b.textContent?.toLowerCase().includes('today')),
                week: buttons.find(b => b.textContent?.toLowerCase().includes('week')),
                month: buttons.find(b => b.textContent?.toLowerCase().includes('month'))
            };

            return {
                foundButtons: Object.entries(timeButtons).filter(([_k, v]) => v).map(([k]) => k)
            };
        });

        if (result.foundButtons.length === 0) {
            return { passed: null, skipped: true, message: 'No time filter buttons found' };
        }

        return {
            passed: true,
            message: `Time filters: ${result.foundButtons.join(', ')}`
        };
    },

    async impactSlider(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const slider = document.querySelector(
                'input[type="range"][name*="impact"], ' +
                '#impact-filter, ' +
                '.impact-slider, ' +
                '[class*="impact-range"]'
            );

            return {
                hasSlider: !!slider,
                min: slider?.min,
                max: slider?.max,
                value: slider?.value
            };
        });

        if (!result.hasSlider) {
            return { passed: null, skipped: true, message: 'No impact slider found' };
        }

        return {
            passed: true,
            message: `Impact slider: min=${result.min}, max=${result.max}, value=${result.value}`
        };
    }
};

// ============================================================================
// News Cards Tests
// ============================================================================
const NewsCardsTests = {
    async newsCardsDisplay(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const cards = document.querySelectorAll(
                '.news-card, ' +
                '.news-item, ' +
                '.story-card, ' +
                '[class*="news-entry"], ' +
                '.feed-item'
            );

            if (cards.length === 0) {
                const emptyState = document.querySelector('.empty-state, .no-news, [class*="empty"]');
                return {
                    hasCards: false,
                    cardCount: 0,
                    hasEmptyState: !!emptyState,
                    emptyText: emptyState?.textContent?.trim()?.substring(0, 50)
                };
            }

            const firstCard = cards[0];
            const hasHeadline = !!firstCard.querySelector('h2, h3, h4, .headline, .title');
            const hasSummary = !!firstCard.querySelector('p, .summary, .description');
            const hasSource = !!firstCard.querySelector('.source, .attribution');
            const hasDate = !!firstCard.querySelector('.date, time, [class*="date"]');
            const hasImpact = !!firstCard.querySelector('.impact, .score, [class*="impact"]');

            return {
                hasCards: true,
                cardCount: cards.length,
                hasHeadline,
                hasSummary,
                hasSource,
                hasDate,
                hasImpact
            };
        });

        if (!result.hasCards && result.hasEmptyState) {
            return {
                passed: true,
                message: `Empty state: "${result.emptyText}"`
            };
        }

        // Skip instead of fail if no cards and no empty state - fresh CI may not have news data
        if (!result.hasCards && !result.hasEmptyState) {
            return { passed: null, skipped: true, message: 'No news cards found (fresh CI environment has no news data)' };
        }

        return {
            passed: result.hasCards,
            message: `${result.cardCount} news cards (headline=${result.hasHeadline}, summary=${result.hasSummary}, impact=${result.hasImpact})`
        };
    },

    async loadMoreButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a.btn'));
            const loadMoreBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('load more') || text.includes('more stories') ||
                       text.includes('show more') || text.includes('next');
            });

            return {
                hasButton: !!loadMoreBtn,
                buttonText: loadMoreBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No load more button found' };
        }

        return {
            passed: true,
            message: `Load more button: "${result.buttonText}"`
        };
    }
};

// ============================================================================
// News Templates Tests
// ============================================================================
const NewsTemplatesTests = {
    async templatesSidebarExists(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const sidebar = document.querySelector(
                '.sidebar, ' +
                '.templates-sidebar, ' +
                'aside, ' +
                '[class*="sidebar"]'
            );

            const templates = document.querySelectorAll(
                '.template, ' +
                '.template-btn, ' +
                '[class*="template-item"], ' +
                '.query-template'
            );

            return {
                hasSidebar: !!sidebar,
                templateCount: templates.length,
                templateTexts: Array.from(templates).slice(0, 5).map(t => t.textContent?.trim()?.substring(0, 30))
            };
        });

        if (!result.hasSidebar && result.templateCount === 0) {
            return { passed: null, skipped: true, message: 'No templates sidebar found' };
        }

        return {
            passed: true,
            message: `Templates: sidebar=${result.hasSidebar}, ${result.templateCount} templates (${result.templateTexts.join(', ')})`
        };
    },

    async templateClickable(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        // The news-templates sidebar renders <button class="ldr-template-btn"
        // onclick="useNewsTemplate('<id>')">. Clicking a prompt-free template
        // (e.g. 'breaking-news') redirects to /news/subscriptions/new with the
        // query pre-filled — no LLM involved, so this is CI-safe.
        const wiring = await page.evaluate(() => {
            const btns = Array.from(document.querySelectorAll('.ldr-template-btn'));
            const breaking = btns.find(b =>
                (b.getAttribute('onclick') || '').includes("'breaking-news'"));
            return {
                templateCount: btns.length,
                hasUseNewsTemplate: typeof window.useNewsTemplate === 'function',
                hasBreakingNews: !!breaking,
            };
        });

        if (wiring.templateCount === 0) {
            // A real failure, not a skip: the templates sidebar is part of the
            // server-rendered news page and must always be present.
            return {
                passed: false,
                message: 'No .ldr-template-btn rendered on the news page'
            };
        }

        // Click the breaking-news template and require navigation to the
        // create-subscription page, whose page-specific container proves the
        // correct page loaded (not login/error).
        await Promise.all([
            page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 15000 }),
            page.evaluate(() => {
                const btns = Array.from(document.querySelectorAll('.ldr-template-btn'));
                const breaking = btns.find(b =>
                    (b.getAttribute('onclick') || '').includes("'breaking-news'"));
                (breaking || btns[0]).click();
            }),
        ]);

        const dest = await page.evaluate(() => ({
            path: window.location.pathname,
            hasForm: !!document.querySelector('.ldr-subscription-form-container #subscription-form'),
            query: document.querySelector('#subscription-query')?.value || ''
        }));

        const onSubForm = dest.path.startsWith('/news/subscriptions/new') && dest.hasForm;

        return {
            passed: onSubForm && wiring.hasBreakingNews && wiring.hasUseNewsTemplate,
            message: `Template click: ${wiring.templateCount} templates -> navigated to "${dest.path}" (form=${dest.hasForm}, query="${dest.query.substring(0, 40)}")`
        };
    },

    async trendingTopics(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const trending = document.querySelector(
                '.trending, ' +
                '.trending-topics, ' +
                '[class*="trending"], ' +
                '.hot-topics'
            );

            const topics = document.querySelectorAll(
                '.topic, ' +
                '.trending-item, ' +
                '[class*="topic-tag"]'
            );

            return {
                hasTrending: !!trending,
                topicCount: topics.length,
                topics: Array.from(topics).slice(0, 5).map(t => t.textContent?.trim())
            };
        });

        if (!result.hasTrending && result.topicCount === 0) {
            return { passed: null, skipped: true, message: 'No trending topics section found' };
        }

        return {
            passed: true,
            message: `Trending: ${result.topicCount} topics (${result.topics.join(', ')})`
        };
    }
};

// ============================================================================
// Subscription Management Tests
// ============================================================================
const SubscriptionTests = {
    async createSubscriptionButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a.btn'));
            const createBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('create') || text.includes('new subscription') ||
                       text.includes('add subscription') || text.includes('subscribe');
            });

            return {
                hasButton: !!createBtn,
                buttonText: createBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No create subscription button found' };
        }

        return {
            passed: true,
            message: `Create subscription button: "${result.buttonText}"`
        };
    },

    async manageSubscriptionsButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a.btn, a'));
            const manageBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                return text.includes('manage') || text.includes('subscriptions') ||
                       href.includes('subscription');
            });

            return {
                hasButton: !!manageBtn,
                buttonText: manageBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No manage subscriptions button found' };
        }

        return {
            passed: true,
            message: `Manage subscriptions button: "${result.buttonText}"`
        };
    },

    async manageSubscriptionsNavigates(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        // On the news page "Manage Subscriptions" is an <a href="/news/subscriptions">
        // link (not a modal trigger). The real, CI-safe behaviour is that it
        // navigates to the standalone subscriptions page. Assert that rather than
        // the old loose "any element whose class contains 'subscription'" check,
        // which matched static page chrome inside dead modal markup.
        const link = await page.evaluate(() => {
            const a = document.querySelector('a[href="/news/subscriptions"]');
            return {
                hasLink: !!a,
                text: a?.textContent?.trim()?.replace(/\s+/g, ' ') || ''
            };
        });

        if (!link.hasLink) {
            return {
                passed: false,
                message: 'No Manage Subscriptions link (a[href="/news/subscriptions"]) on the news page'
            };
        }

        await clickAndWaitForNavigation(page, 'a[href="/news/subscriptions"]', { timeout: 15000 });

        const dest = await page.evaluate(() => ({
            path: window.location.pathname,
            // .ldr-subscriptions-page is the page-specific wrapper rendered by
            // pages/subscriptions.html — proves the subscriptions page loaded,
            // not login/error.
            hasPage: !!document.querySelector('.ldr-subscriptions-page'),
            hasGrid: !!document.querySelector('#subscriptions-grid')
        }));

        const ok = dest.path === '/news/subscriptions' && dest.hasPage;

        return {
            passed: ok,
            message: `Manage Subscriptions "${link.text}" -> "${dest.path}" (page=${dest.hasPage}, grid=${dest.hasGrid})`
        };
    },

    async subscriptionFormFields(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        // Try to open create subscription form (only <button> elements, not <a> links)
        const clicked = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button'));
            const createBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('create') || text.includes('new subscription') ||
                       text.includes('add subscription');
            });
            if (createBtn) {
                createBtn.click();
                return true;
            }
            return false;
        });

        if (!clicked) {
            return { passed: null, skipped: true, message: 'No create subscription button found (only navigation link exists)' };
        }

        await delay(500);

        const result = await page.evaluate(() => {
            const queryInput = document.querySelector(
                'input[name*="query"], ' +
                'textarea[name*="query"], ' +
                '#subscription-query'
            );

            const nameInput = document.querySelector(
                'input[name*="name"], ' +
                '#subscription-name'
            );

            const frequencySelect = document.querySelector(
                'select[name*="frequency"], ' +
                '#update-frequency'
            );

            const folderSelect = document.querySelector(
                'select[name*="folder"], ' +
                '#subscription-folder'
            );

            return {
                hasQuery: !!queryInput,
                hasName: !!nameInput,
                hasFrequency: !!frequencySelect,
                hasFolder: !!folderSelect
            };
        });

        const hasForm = result.hasQuery || result.hasName;

        if (!hasForm) {
            return { passed: null, skipped: true, message: 'No subscription form fields found' };
        }

        return {
            passed: true,
            message: `Form fields: query=${result.hasQuery}, name=${result.hasName}, frequency=${result.hasFrequency}, folder=${result.hasFolder}`
        };
    }
};

// ============================================================================
// Recent Searches Tests
// ============================================================================
const RecentSearchesTests = {
    async recentSearchesSection(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const section = document.querySelector(
                '.recent-searches, ' +
                '.search-history, ' +
                '[class*="recent-search"], ' +
                '.history-list'
            );

            const searches = document.querySelectorAll(
                '.recent-search, ' +
                '.search-item, ' +
                '.history-item'
            );

            return {
                hasSection: !!section,
                searchCount: searches.length,
                searches: Array.from(searches).slice(0, 3).map(s => s.textContent?.trim()?.substring(0, 30))
            };
        });

        if (!result.hasSection && result.searchCount === 0) {
            return { passed: null, skipped: true, message: 'No recent searches section found' };
        }

        return {
            passed: true,
            message: `Recent searches: ${result.searchCount} items (${result.searches.join(', ')})`
        };
    },

    async clearSearchHistoryButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, .btn, a'));
            const clearBtn = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const title = btn.getAttribute('title')?.toLowerCase() || '';
                return text.includes('clear') || text.includes('delete history') ||
                       title.includes('clear');
            });

            return {
                hasButton: !!clearBtn,
                buttonText: clearBtn?.textContent?.trim()
            };
        });

        if (!result.hasButton) {
            return { passed: null, skipped: true, message: 'No clear history button found' };
        }

        return {
            passed: true,
            message: `Clear history button: "${result.buttonText}"`
        };
    }
};

// ============================================================================
// Main Test Runner
// ============================================================================
async function main() {
    log.section('News Feed & Subscriptions Tests');

    const ctx = await setupTest({ authenticate: true });
    const results = new TestResults('News Feed Tests');
    const { page } = ctx;
    const { baseUrl } = ctx.config;

    // Helper: run a sub-test with a timeout so a single hang
    // doesn't block the entire 300s process-level timeout.
    // Use 60s to match navigateTo's CI timeout — 30s was too tight
    // when the server is under load from prior test files.
    const testTimeout = ctx.config.isCI ? 60000 : 30000;
    async function run(category, name, testFn) {
        try {
            const result = await withTimeout(
                testFn(page, baseUrl),
                testTimeout,
                `${category}/${name}`
            );
            if (result.skipped) {
                results.skip(category, name, result.message);
            } else {
                results.add(category, name, result.passed, result.message);
            }
        } catch (error) {
            results.add(category, name, false, `Error: ${error.message}`);
        }
    }

    try {
        // News Feed Page
        log.section('News Feed Page');
        await run('Page', 'Loads', (p, u) => NewsFeedPageTests.newsFeedPageLoads(p, u));
        await run('Page', 'Search', (p, u) => NewsFeedPageTests.searchFunctionality(p, u));
        await run('Page', 'Refresh Button', (p, u) => NewsFeedPageTests.refreshFeedButton(p, u));
        await run('Page', 'Auto-Refresh Toggle', (p, u) => NewsFeedPageTests.autoRefreshToggle(p, u));

        // News Filters
        log.section('News Filters');
        await run('Filters', 'Subscription Filter', (p, u) => NewsFiltersTests.subscriptionFilter(p, u));
        await run('Filters', 'Time Buttons', (p, u) => NewsFiltersTests.timeFilterButtons(p, u));
        await run('Filters', 'Impact Slider', (p, u) => NewsFiltersTests.impactSlider(p, u));

        // News Cards
        log.section('News Cards');
        await run('Cards', 'Display', (p, u) => NewsCardsTests.newsCardsDisplay(p, u));
        await run('Cards', 'Load More', (p, u) => NewsCardsTests.loadMoreButton(p, u));

        // Templates
        log.section('Templates');
        await run('Templates', 'Sidebar Exists', (p, u) => NewsTemplatesTests.templatesSidebarExists(p, u));
        await run('Templates', 'Clickable', (p, u) => NewsTemplatesTests.templateClickable(p, u));
        await run('Templates', 'Trending Topics', (p, u) => NewsTemplatesTests.trendingTopics(p, u));

        // Subscriptions
        log.section('Subscriptions');
        await run('Subs', 'Create Button', (p, u) => SubscriptionTests.createSubscriptionButton(p, u));
        await run('Subs', 'Manage Button', (p, u) => SubscriptionTests.manageSubscriptionsButton(p, u));
        await run('Subs', 'Manage Navigates', (p, u) => SubscriptionTests.manageSubscriptionsNavigates(p, u));
        await run('Subs', 'Form Fields', (p, u) => SubscriptionTests.subscriptionFormFields(p, u));

        // Recent Searches
        log.section('Recent Searches');
        await run('History', 'Recent Searches', (p, u) => RecentSearchesTests.recentSearchesSection(p, u));
        await run('History', 'Clear Button', (p, u) => RecentSearchesTests.clearSearchHistoryButton(p, u));

    } catch (error) {
        log.error(`Fatal error: ${error.message}`);
        console.error(error.stack);
    } finally {
        results.print();
        results.save();
        await teardownTest(ctx);
        process.exit(results.exitCode());
    }
}

// Run if executed directly
if (require.main === module) {
    main().catch(error => {
        console.error('Test runner failed:', error);
        process.exit(1);
    });
}

module.exports = { NewsFeedPageTests, NewsFiltersTests, NewsCardsTests, NewsTemplatesTests, SubscriptionTests, RecentSearchesTests };
