#!/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, delay, navigateTo, withTimeout } = require('./test_lib');

// ============================================================================
// 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 result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn'));
            const createBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('create') ||
                b.textContent?.toLowerCase().includes('new') ||
                b.textContent?.toLowerCase().includes('add')
            );

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

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

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

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

            const firstCard = cards[0];
            return {
                hasCards: true,
                cardCount: cards.length,
                hasName: !!firstCard.querySelector('.subscription-name, .card-title, h3, h4, .name'),
                hasQuery: !!firstCard.querySelector('.query, .search-query, .subscription-query'),
                hasStatus: !!firstCard.querySelector('.status, .badge, .subscription-status'),
                hasFrequency: !!firstCard.querySelector('.frequency, .interval, .refresh'),
                hasLastUpdated: !!firstCard.querySelector('.last-updated, .updated, time'),
                hasEditButton: !!firstCard.querySelector('.edit-btn, button[onclick*="edit"], .btn-edit'),
                hasDeleteButton: !!firstCard.querySelector('.delete-btn, button[onclick*="delete"], .btn-danger')
            };
        });

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

        const hasRequiredParts = result.hasName || result.hasQuery;
        return {
            passed: hasRequiredParts,
            message: hasRequiredParts
                ? `Subscription cards: ${result.cardCount} found (name=${result.hasName}, query=${result.hasQuery}, status=${result.hasStatus}, edit=${result.hasEditButton}, delete=${result.hasDeleteButton})`
                : 'Subscription cards missing required elements'
        };
    },

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

        // Try to click create button to open modal
        // Use XPath for text-based button selection (Puppeteer doesn't support :has-text)
        let createBtn = await page.$('.btn-primary, .create-btn, [data-action="create"]');
        if (!createBtn) {
            // Try XPath for text-based selection
            const buttons = await page.$x('//button[contains(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "create") or contains(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "new") or contains(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "add")]');
            createBtn = buttons.length > 0 ? buttons[0] : null;
        }

        if (!createBtn) {
            // Check if form is inline instead of modal
            const inlineForm = await page.$('form.subscription-form, #subscription-form');
            if (inlineForm) {
                return { passed: true, message: 'Subscription form is inline (not modal)' };
            }
            return { passed: null, skipped: true, message: 'No create button to open subscription form' };
        }

        await createBtn.click();
        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector('.modal.show, .modal[style*="display: block"], [role="dialog"], .subscription-modal');
            const form = document.querySelector('form.subscription-form, #subscription-form, .modal form');

            return {
                hasModal: !!modal,
                hasForm: !!form,
                hasQueryInput: !!document.querySelector('input[name*="query"], textarea[name*="query"]'),
                hasFrequencyInput: !!document.querySelector('input[name*="frequency"], select[name*="frequency"], input[name*="interval"]')
            };
        });

        // Close modal if opened
        const closeBtn = await page.$('.modal .btn-close, .modal .close, [data-bs-dismiss="modal"]');
        if (closeBtn) await closeBtn.click();

        const passed = result.hasModal || result.hasForm;
        return {
            passed,
            message: passed
                ? `Subscription form modal opens (form=${result.hasForm}, query=${result.hasQueryInput}, frequency=${result.hasFrequencyInput})`
                : 'Subscription form modal did not open'
        };
    },

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

        // Try to open form
        // Use CSS selectors first, then try XPath for text-based selection
        let createBtn = await page.$('.btn-primary, .create-btn, [data-action="create"]');
        if (!createBtn) {
            const buttons = await page.$x('//button[contains(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "create") or contains(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "new")]');
            createBtn = buttons.length > 0 ? buttons[0] : null;
        }
        if (createBtn) {
            await createBtn.click();
            await delay(500);
        }

        const result = await page.evaluate(() => {
            return {
                hasQueryField: !!document.querySelector('input[name*="query"], textarea[name*="query"], #query'),
                hasTypeField: !!document.querySelector('select[name*="type"], input[name*="type"], #subscription-type'),
                hasFrequencyField: !!document.querySelector('input[name*="frequency"], select[name*="frequency"], #frequency'),
                hasProviderField: !!document.querySelector('select[name*="provider"], #llm-provider'),
                hasModelField: !!document.querySelector('select[name*="model"], #model'),
                hasSearchEngineField: !!document.querySelector('select[name*="search"], #search-engine'),
                hasActiveToggle: !!document.querySelector('input[type="checkbox"][name*="active"], .toggle-active')
            };
        });

        // Close modal
        const closeBtn = await page.$('.modal .btn-close, .modal .close, [data-bs-dismiss="modal"]');
        if (closeBtn) await closeBtn.click();

        const hasBasicFields = result.hasQueryField;
        if (!hasBasicFields) {
            return { passed: null, skipped: true, message: 'Could not find subscription form fields' };
        }

        return {
            passed: true,
            message: `Subscription form fields: query=${result.hasQueryField}, type=${result.hasTypeField}, frequency=${result.hasFrequencyField}, provider=${result.hasProviderField}`
        };
    }
};

// ============================================================================
// 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 Modal', (p, u) => SubscriptionsPageTests.subscriptionFormModal(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 };
