#!/usr/bin/env node
/**
 * News & Subscriptions UI Tests
 *
 * Tests for the news feed page and subscription management.
 *
 * Run: node test_news_subscriptions_ci.js
 */

const { setupTest, teardownTest, TestResults, log, navigateTo, withTimeout, findActionButton } = require('./test_lib');
const { seedSubscription, deleteSubscription } = require('./test_lib/fixtures');

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

        const result = await page.evaluate(() => {
            return {
                hasNewsContent: !!document.querySelector('.news-container, .ldr-news, #news-feed, .news-feed'),
                hasHeader: !!document.querySelector('h1, .news-header, .page-title'),
                headerText: document.querySelector('h1, .news-header, .page-title')?.textContent?.trim(),
                hasCards: document.querySelectorAll('.news-card, .news-item, .article-card, [data-news-id]').length,
                hasEmptyState: !!document.querySelector('.ldr-empty-state, .no-news, .alert-info')
            };
        });

        const passed = result.hasNewsContent || result.hasHeader || result.hasCards > 0 || result.hasEmptyState;
        return {
            passed,
            message: passed
                ? `News page loaded (header: "${result.headerText}", cards: ${result.hasCards})`
                : 'News page failed to load'
        };
    },

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

        const result = await page.evaluate(() => {
            const cards = document.querySelectorAll('.news-card, .news-item, .article-card, [data-news-id]');
            if (cards.length === 0) return { hasCards: false };

            const firstCard = cards[0];
            return {
                hasCards: true,
                cardCount: cards.length,
                hasTitle: !!firstCard.querySelector('.card-title, h3, h4, .title, .news-title'),
                hasSummary: !!firstCard.querySelector('.card-text, .summary, .description, p'),
                hasSource: !!firstCard.querySelector('.source, .publication, .meta'),
                hasDate: !!firstCard.querySelector('.date, time, .timestamp'),
                hasActions: !!firstCard.querySelector('.card-actions, .actions, button, .btn')
            };
        });

        if (!result.hasCards) {
            return { passed: null, skipped: true, message: 'No news cards to test structure' };
        }

        const hasRequiredParts = result.hasTitle;
        return {
            passed: hasRequiredParts,
            message: hasRequiredParts
                ? `News cards: ${result.cardCount} found (title=${result.hasTitle}, summary=${result.hasSummary}, actions=${result.hasActions})`
                : 'News cards missing required title element'
        };
    },

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

        const result = await page.evaluate(() => {
            const cards = document.querySelectorAll('.news-card, .news-item, .article-card');
            if (cards.length === 0) return { hasCards: false };

            const firstCard = cards[0];
            const upvoteBtn = firstCard.querySelector(
                '.upvote, ' +
                '.vote-up, ' +
                '[data-vote="up"], ' +
                'button[onclick*="upvote"], ' +
                '.fa-thumbs-up, ' +
                '.bi-hand-thumbs-up'
            );
            const downvoteBtn = firstCard.querySelector(
                '.downvote, ' +
                '.vote-down, ' +
                '[data-vote="down"], ' +
                'button[onclick*="downvote"], ' +
                '.fa-thumbs-down, ' +
                '.bi-hand-thumbs-down'
            );

            return {
                hasCards: true,
                hasUpvote: !!upvoteBtn,
                hasDownvote: !!downvoteBtn
            };
        });

        if (!result.hasCards) {
            return { passed: null, skipped: true, message: 'No news cards to test vote buttons' };
        }

        const hasVoting = result.hasUpvote || result.hasDownvote;
        if (!hasVoting) {
            return { passed: null, skipped: true, message: 'No voting buttons found on news cards' };
        }

        return {
            passed: true,
            message: `Vote buttons found (upvote=${result.hasUpvote}, downvote=${result.hasDownvote})`
        };
    },

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

        const result = await page.evaluate(() => {
            const cards = document.querySelectorAll('.news-card, .news-item, .article-card');
            if (cards.length === 0) return { hasCards: false };

            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const researchBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('research') ||
                b.textContent?.toLowerCase().includes('deeper') ||
                b.textContent?.toLowerCase().includes('investigate')
            );

            return {
                hasCards: true,
                hasResearchButton: !!researchBtn,
                buttonText: researchBtn?.textContent?.trim()
            };
        });

        if (!result.hasCards) {
            return { passed: null, skipped: true, message: 'No news cards to test deeper research button' };
        }

        if (!result.hasResearchButton) {
            return { passed: null, skipped: true, message: 'No deeper research button found' };
        }

        return {
            passed: true,
            message: `Deeper research button found ("${result.buttonText}")`
        };
    },

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

        const result = await page.evaluate(() => {
            const categoryFilter = document.querySelector(
                'select[name*="category"], ' +
                '.category-filter, ' +
                '.category-tabs, ' +
                '[data-category]'
            );

            const categoryBadges = document.querySelectorAll('.category-badge, .category-tag, .badge[data-category]');

            return {
                hasFilter: !!categoryFilter,
                hasCategoryBadges: categoryBadges.length > 0,
                badgeCount: categoryBadges.length
            };
        });

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

        return {
            passed: true,
            message: result.hasFilter
                ? 'Category filter dropdown found'
                : `${result.badgeCount} category badges found`
        };
    }
};

// ============================================================================
// Subscriptions Page Tests
// ============================================================================
const SubscriptionsPageTests = {
    async subscriptionsPageLoads(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news/subscriptions`);

        const result = await page.evaluate(() => {
            return {
                hasContent: !!document.querySelector('.subscriptions-container, .ldr-subscriptions, #subscriptions'),
                hasHeader: !!document.querySelector('h1, .subscriptions-header, .page-title'),
                headerText: document.querySelector('h1, .subscriptions-header, .page-title')?.textContent?.trim(),
                subscriptionCount: document.querySelectorAll('.subscription-card, .subscription-item, [data-subscription-id]').length,
                hasEmptyState: !!document.querySelector('.ldr-empty-state, .no-subscriptions')
            };
        });

        const passed = result.hasContent || result.hasHeader || result.subscriptionCount > 0 || result.hasEmptyState;
        return {
            passed,
            message: passed
                ? `Subscriptions page loaded (header: "${result.headerText}", subscriptions: ${result.subscriptionCount})`
                : 'Subscriptions page failed to load'
        };
    },

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

        const result = await page.evaluate(() => {
            const statsSection = document.querySelector('.stats-overview, .subscription-stats, .stats');
            const statBadges = document.querySelectorAll('.stat-badge, .badge, .ldr-stat-item');

            // Look for common stat indicators
            const pageText = document.body.textContent?.toLowerCase() || '';
            const hasTotal = pageText.includes('total');
            const hasActive = pageText.includes('active');
            const hasPaused = pageText.includes('paused') || pageText.includes('inactive');

            return {
                hasStatsSection: !!statsSection,
                statCount: statBadges.length,
                hasTotal,
                hasActive,
                hasPaused
            };
        });

        const hasAnyStats = result.hasStatsSection || (result.hasTotal && result.hasActive);
        if (!hasAnyStats) {
            return { passed: null, skipped: true, message: 'No subscription stats display found' };
        }

        return {
            passed: true,
            message: `Subscription stats found (total=${result.hasTotal}, active=${result.hasActive}, paused=${result.hasPaused})`
        };
    },

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

        const { found, text } = await findActionButton(page);

        return {
            passed: found,
            message: found
                ? `Create subscription button found ("${text}")`
                : 'No create subscription button found'
        };
    },

    async subscriptionCardStructure(page, baseUrl) {
        // Subscription cards render client-side (subscriptions.js::
        // createSubscriptionCard) as .ldr-subscription-card[data-subscription-id]
        // with .ldr-card-header h4 (title) + .ldr-query-text + .ldr-status-badge,
        // an edit link (a[href*="/edit"]) and a .btn-danger delete button. The old
        // test scanned for `.subscription-card` (no such class) and SKIPped on an
        // empty DB. Seed a subscription and assert the real card markup.
        await navigateTo(page, `${baseUrl}/news/subscriptions`);
        const seeded = await seedSubscription(page);
        if (!seeded) {
            return { passed: false, message: 'Could not seed subscription for card-structure test' };
        }
        try {
            // navigateTo no-ops (already on this path); reload to render the card.
            await page.reload({ waitUntil: 'domcontentloaded' });
            await page.waitForSelector(`.ldr-subscription-card[data-subscription-id="${seeded.id}"]`, { timeout: 10000 });

            const result = await page.evaluate((id) => {
                const card = document.querySelector(`.ldr-subscription-card[data-subscription-id="${id}"]`);
                if (!card) return { found: false };
                return {
                    found: true,
                    hasTitle: !!card.querySelector('.ldr-card-header h4'),
                    hasQuery: !!card.querySelector('.ldr-query-text'),
                    hasStatus: !!card.querySelector('.ldr-status-badge'),
                    hasEdit: !!card.querySelector('a[href*="/edit"]'),
                    hasDelete: !!card.querySelector('.btn-danger'),
                };
            }, seeded.id);

            const passed = result.found && result.hasTitle && result.hasQuery
                && result.hasStatus && result.hasEdit && result.hasDelete;
            return {
                passed,
                message: passed
                    ? 'Subscription card renders real structure (title + query + status badge + edit + delete)'
                    : `Subscription card structure incomplete (${JSON.stringify(result)})`
            };
        } finally {
            await deleteSubscription(page, seeded.id);
        }
    },

    async subscriptionFormPage(page, baseUrl) {
        // The Create Subscription flow is page-navigation, not a modal:
        // #create-subscription-btn on /news/subscriptions has a JS handler
        // that runs `window.location.href = '/news/subscriptions/new'`.
        // Earlier this method ("subscriptionFormModal") asserted a modal
        // and accidentally passed when the navigation landed on the new
        // page (which happens to contain a form). Rename + rewrite to
        // test the real flow: navigate directly to /new and assert the
        // create form is present and reachable.

        await navigateTo(page, `${baseUrl}/news/subscriptions/new`);

        const result = await page.evaluate(() => {
            const form = document.querySelector('form:not(#logout-form)');
            const query = document.querySelector('#subscription-query');
            return {
                hasForm: !!form,
                hasQueryField: !!query,
                queryRequired: !!query?.required,
            };
        });

        const passed = result.hasForm && result.hasQueryField;
        return {
            passed,
            message: passed
                ? `Subscription create form reachable (queryRequired=${result.queryRequired})`
                : `Subscription create form missing (form=${result.hasForm}, query=${result.hasQueryField})`
        };
    },

    async subscriptionFormFields(page, baseUrl) {
        // The form lives at /news/subscriptions/new and uses
        // `subscription-*` ids (#subscription-query, #subscription-model,
        // #subscription-search-engine, ...). The previous version
        // looked for `name*="query"` / `#query` / etc., which match
        // nothing on the actual page, so this test always reported a
        // misleading SKIP ("Could not find subscription form fields").

        await navigateTo(page, `${baseUrl}/news/subscriptions/new`);

        const result = await page.evaluate(() => {
            const form = document.querySelector('form:not(#logout-form)');
            if (!form) return { hasForm: false };
            return {
                hasForm: true,
                hasQueryField: !!form.querySelector('#subscription-query'),
                hasModelField: !!form.querySelector('#subscription-model'),
                hasSearchEngineField: !!form.querySelector('#subscription-search-engine'),
                hasStrategyField: !!form.querySelector('#subscription-strategy'),
                hasActiveToggle: !!form.querySelector('#subscription-active'),
            };
        });

        if (!result.hasForm) {
            return { passed: false, message: 'Subscription form not present at /news/subscriptions/new' };
        }

        // The query textarea is the core required field. Other fields
        // (model, search engine, strategy, active toggle) round out the
        // expected schema; we treat all four as required signals so the
        // test fails loudly if the form skeleton ever drops a section.
        const passed = result.hasQueryField &&
            result.hasModelField &&
            result.hasSearchEngineField &&
            result.hasStrategyField &&
            result.hasActiveToggle;

        return {
            passed,
            message: passed
                ? 'Subscription form has query, model, search-engine, strategy, active toggle'
                : `Subscription form missing fields: query=${result.hasQueryField}, model=${result.hasModelField}, search=${result.hasSearchEngineField}, strategy=${result.hasStrategyField}, active=${result.hasActiveToggle}`
        };
    }
};

// ============================================================================
// News API Tests
// ============================================================================
const NewsApiTests = {
    async newsFeedApiResponds(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/news`);

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/api/news/feed`);
                if (!response.ok) return { ok: false, status: response.status };

                const data = await response.json();
                return {
                    ok: true,
                    status: response.status,
                    itemCount: Array.isArray(data) ? data.length : (data.items?.length || 0)
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

        if (!result.ok && result.status === 404) {
            return { passed: null, skipped: true, message: 'News feed API endpoint not found' };
        }

        return {
            passed: result.ok,
            message: result.ok
                ? `News feed API responds (${result.itemCount} items)`
                : `News feed API failed: ${result.error || 'status ' + result.status}`
        };
    },

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

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/api/news/subscriptions`);
                if (!response.ok) return { ok: false, status: response.status };

                const data = await response.json();
                return {
                    ok: true,
                    status: response.status,
                    subscriptionCount: Array.isArray(data) ? data.length : (data.subscriptions?.length || 0)
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

        if (!result.ok && result.status === 404) {
            return { passed: null, skipped: true, message: 'Subscriptions API endpoint not found' };
        }

        return {
            passed: result.ok,
            message: result.ok
                ? `Subscriptions API responds (${result.subscriptionCount} subscriptions)`
                : `Subscriptions API failed: ${result.error || 'status ' + result.status}`
        };
    }
};

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

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

    const subTestTimeout = ctx.config.isCI ? 60000 : 30000;
    async function run(category, name, testFn) {
        try {
            const result = await withTimeout(
                testFn(page, baseUrl),
                subTestTimeout,
                `${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 Tests
        log.section('News Feed');
        await run('News', 'News Page Loads', (p, u) => NewsFeedTests.newsPageLoads(p, u));
        await run('News', 'News Card Structure', (p, u) => NewsFeedTests.newsCardStructure(p, u));
        await run('News', 'News Card Vote Buttons', (p, u) => NewsFeedTests.newsCardVoteButtons(p, u));
        await run('News', 'Deeper Research Button', (p, u) => NewsFeedTests.deeperResearchButton(p, u));
        await run('News', 'Category Filter', (p, u) => NewsFeedTests.newsCategoryFilter(p, u));

        // Subscriptions Page Tests
        log.section('Subscriptions Page');
        await run('Subscriptions', 'Subscriptions Page Loads', (p, u) => SubscriptionsPageTests.subscriptionsPageLoads(p, u));
        await run('Subscriptions', 'Subscription Stats Display', (p, u) => SubscriptionsPageTests.subscriptionStatsDisplay(p, u));
        await run('Subscriptions', 'Create Subscription Button', (p, u) => SubscriptionsPageTests.createSubscriptionButton(p, u));
        await run('Subscriptions', 'Subscription Card Structure', (p, u) => SubscriptionsPageTests.subscriptionCardStructure(p, u));
        await run('Subscriptions', 'Subscription Form Page', (p, u) => SubscriptionsPageTests.subscriptionFormPage(p, u));
        await run('Subscriptions', 'Subscription Form Fields', (p, u) => SubscriptionsPageTests.subscriptionFormFields(p, u));

        // API Tests
        log.section('News APIs');
        await run('API', 'News Feed API Responds', (p, u) => NewsApiTests.newsFeedApiResponds(p, u));
        await run('API', 'Subscriptions API Responds', (p, u) => NewsApiTests.subscriptionsApiResponds(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 = { NewsFeedTests, SubscriptionsPageTests, NewsApiTests };
