/**
 * Comprehensive UI Functionality Tests
 *
 * Tests the main user workflows:
 * 1. Research workflow (start, monitor progress, view results)
 * 2. Settings changes and persistence
 * 3. Library functionality (view, search, collections)
 * 4. News page (search, subscriptions, filters)
 * 5. Navigation and core UI elements
 */

const puppeteer = require('puppeteer');
const { expect } = require('chai');

// Import shared helpers
const {
    BASE_URL,
    HEADLESS,
    SLOW_MO,
    SCREENSHOT_DIR,
    delay,
    takeScreenshot,
    ensureLoggedIn,
    getLaunchOptions
} = require('./helpers');

// Test configuration - static username for UI tests
const TEST_USERNAME = 'ui_test_user';
const TEST_PASSWORD = 'Test_password_123';

// Helper to log page content (unique to this test file)
async function logPageInfo(page, label = '') {
    const url = page.url();
    const title = await page.title();
    console.log(`\n--- ${label} ---`);
    console.log(`URL: ${url}`);
    console.log(`Title: ${title}`);
}

describe('UI Functionality Tests', function() {
    this.timeout(300000); // 5 minute timeout for full suite

    let browser;
    let page;

    before(async () => {
        console.log(`\nStarting browser (headless: ${HEADLESS}, slowMo: ${SLOW_MO})`);
        console.log(`Screenshots will be saved to: ${SCREENSHOT_DIR}`);

        browser = await puppeteer.launch(getLaunchOptions());
        page = await browser.newPage();
        await page.setViewport({ width: 1400, height: 900 });
        page.setDefaultNavigationTimeout(30000);

        // Log browser console messages
        page.on('console', msg => {
            if (msg.type() === 'error') {
                console.log('Browser ERROR:', msg.text());
            }
        });
    });

    after(async () => {
        if (browser) {
            await browser.close();
        }
    });

    describe('Authentication', () => {
        it('should be logged in (login or register as needed)', async () => {
            await ensureLoggedIn(page, TEST_USERNAME, TEST_PASSWORD);
            await takeScreenshot(page, 'after-auth');

            const url = page.url();
            console.log(`  -> Final URL: ${url}`);
            expect(url).to.not.include('/login');
            expect(url).to.not.include('/register');
        });
    });

    describe('Research Workflow', () => {
        it('should load the research page with form elements', async () => {
            await logPageInfo(page, 'Research Page');
            await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });
            await takeScreenshot(page, 'research-page');

            // Verify key elements exist
            const queryInput = await page.$('#query, textarea[name="query"]');
            expect(queryInput).to.not.be.null;
            console.log('  ✓ Query input found');

            const submitBtn = await page.$('#start-research-btn, button[type="submit"]');
            expect(submitBtn).to.not.be.null;
            console.log('  ✓ Submit button found');

            // Check for research mode options
            const quickMode = await page.$('#mode-quick, [data-mode="quick"]');
            const detailedMode = await page.$('#mode-detailed, [data-mode="detailed"]');
            expect(quickMode).to.not.be.null;
            expect(detailedMode).to.not.be.null;
            console.log('  ✓ Research mode options found');
        });

        it('should expand advanced options and show model/search settings', async () => {
            // Ensure advanced options are expanded (may already be open by default)
            const toggle = await page.$('.ldr-advanced-options-toggle');
            if (toggle) {
                const panel = await page.$('.ldr-advanced-options-panel');
                const isVisible = panel ? await page.evaluate(el => {
                    const style = window.getComputedStyle(el);
                    return style.visibility !== 'hidden' && style.opacity !== '0';
                }, panel) : false;
                if (!isVisible) {
                    await toggle.click();
                    await delay(500);
                }
                await takeScreenshot(page, 'advanced-options-expanded');

                // Check for advanced options content
                const modelProvider = await page.$('#model_provider, select[name="model_provider"]');
                const searchEngine = await page.$('#search_engine, [id*="search-engine"]');

                console.log(`  Model provider visible: ${modelProvider !== null}`);
                console.log(`  Search engine visible: ${searchEngine !== null}`);
            }
        });

        it('should type a research query without errors', async () => {
            const testQuery = 'What is the capital of France?';

            const queryInput = await page.$('#query, textarea[name="query"]');
            await queryInput.click({ clickCount: 3 }); // Select all
            await page.keyboard.press('Backspace'); // Clear
            await page.type('#query', testQuery);

            await takeScreenshot(page, 'research-query-entered');

            const value = await page.$eval('#query', el => el.value);
            expect(value).to.equal(testQuery);
            console.log(`  ✓ Query entered: "${testQuery}"`);
        });

        it('should pass form validation when hidden context_window has a non-step value (#3909 regression)', async () => {
            // Regression for #3909 / PR #4051: the context_window input lives
            // inside a display:none container that is only shown for local
            // providers. Previously the input had step="512", so any stored
            // value not aligned to that grid (e.g. 25000) failed HTML5
            // validation. Because the field is hidden, the browser cannot
            // focus it to surface the error, so the Start Research click
            // silently no-ops with no log line. We assert via checkValidity()
            // (no submit) so the test does not consume LLM credits.
            await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });
            await delay(500);

            const check = await page.evaluate(() => {
                const input = document.getElementById('context_window');
                const container = document.getElementById('context_window_container');
                const query = document.getElementById('query');
                const form = document.getElementById('research-form');
                if (!input || !container || !query || !form) {
                    return { ok: false, reason: 'expected form elements not found' };
                }
                // Populate the textarea so query (required-by-JS) is not the
                // reason validation might fail.
                query.value = 'regression check for #3909';
                // The exact stored value from the reporter's issue. 25000 is
                // not a multiple of 512 from min=512.
                input.value = '25000';
                return {
                    ok: true,
                    hiddenForCloudProvider: window.getComputedStyle(container).display === 'none',
                    formValid: form.checkValidity(),
                    inputValid: input.validity.valid,
                    stepMismatch: input.validity.stepMismatch,
                    rangeUnderflow: input.validity.rangeUnderflow,
                    rangeOverflow: input.validity.rangeOverflow,
                };
            });

            expect(check.ok, check.reason).to.be.true;
            expect(
                check.hiddenForCloudProvider,
                '#context_window_container should be hidden by default (cloud provider) — the regression scenario'
            ).to.be.true;
            expect(
                check.formValid,
                'research-form.checkValidity() must return true with context_window=25000 in a hidden container. ' +
                `If this fails, the step="512" constraint has been re-added to #context_window — see PR #4051. ` +
                `Validity flags: ${JSON.stringify({
                    inputValid: check.inputValid,
                    stepMismatch: check.stepMismatch,
                    rangeUnderflow: check.rangeUnderflow,
                    rangeOverflow: check.rangeOverflow,
                })}`
            ).to.be.true;
            console.log('  ✓ Hidden context_window with non-step value does not block submit');
        });

        it('should handle research form submission without crashing', async () => {
            // Note: We don't actually run a full research (would take too long)
            // Just verify the form can be submitted and the UI responds

            // Get current URL before submit
            const urlBefore = page.url();

            // Check if form is valid before submitting
            const query = await page.$eval('#query', el => el.value);
            console.log(`  Query before submit: "${query}"`);

            if (query && query.length > 0) {
                // Submit the form - may trigger navigation to progress page
                const navigationPromise = page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }).catch(() => null);
                await page.click('#start-research-btn');
                await navigationPromise;

                await takeScreenshot(page, 'after-research-submit');

                // Check URL changed to progress page or research started
                const urlAfter = page.url();
                console.log(`  URL after submit: ${urlAfter}`);

                // Verify page didn't crash (should have some content)
                const bodyText = await page.$eval('body', el => el.textContent.substring(0, 500));
                expect(bodyText.length).to.be.greaterThan(0);
                console.log('  ✓ Page responded to form submission');
            }
        });
    });

    describe('Settings Page', () => {
        it('should load settings page with all sections', async () => {
            await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' });
            await logPageInfo(page, 'Settings Page');
            await takeScreenshot(page, 'settings-page');

            // Check for settings container
            const settingsContainer = await page.$('.ldr-settings-container, #settings');
            expect(settingsContainer).to.not.be.null;
            console.log('  ✓ Settings container found');

            // Check for tabs
            const tabs = await page.$$('.ldr-settings-tab');
            console.log(`  Found ${tabs.length} setting tabs`);
            expect(tabs.length).to.be.greaterThan(0);
        });

        it('should have working settings tabs navigation', async () => {
            // Get all tabs
            const tabs = await page.$$('.ldr-settings-tab');

            for (let i = 0; i < Math.min(tabs.length, 4); i++) {
                const tabText = await tabs[i].evaluate(el => el.textContent.trim());
                await tabs[i].click();
                await delay(500);
                console.log(`  Clicked tab: "${tabText}"`);
            }

            await takeScreenshot(page, 'settings-tabs-navigated');
        });

        it('should display LLM provider options', async () => {
            // Wait for settings to load
            await delay(1000);

            // Look for LLM-related settings
            const llmTab = await page.$('[data-tab="llm"]');
            if (llmTab) {
                await llmTab.click();
                await delay(500);
            }

            await takeScreenshot(page, 'settings-llm-section');

            // Check for provider dropdown or options
            const bodyText = await page.$eval('body', el => el.textContent.toLowerCase());
            const hasProviderOptions = bodyText.includes('ollama') ||
                                       bodyText.includes('openai') ||
                                       bodyText.includes('provider');

            console.log(`  Has provider options: ${hasProviderOptions}`);
        });

        it('should have search input for filtering settings', async () => {
            const searchInput = await page.$('#settings-search');
            expect(searchInput).to.not.be.null;

            await searchInput.type('ollama');
            await delay(500);
            await takeScreenshot(page, 'settings-search-filter');

            console.log('  ✓ Settings search works');
        });
    });

    describe('News Page', () => {
        it('should load news page with all components', async () => {
            await page.goto(`${BASE_URL}/news/`, { waitUntil: 'domcontentloaded' });
            await logPageInfo(page, 'News Page');
            await takeScreenshot(page, 'news-page');

            // Check for news container
            const newsContainer = await page.$('.ldr-news-page-wrapper, .ldr-news-container');
            expect(newsContainer).to.not.be.null;
            console.log('  ✓ News container found');
        });

        it('should have search functionality', async () => {
            const searchInput = await page.$('#news-search');
            expect(searchInput).to.not.be.null;

            await searchInput.type('technology');
            await takeScreenshot(page, 'news-search-entered');

            const searchBtn = await page.$('#search-btn');
            if (searchBtn) {
                await searchBtn.click();
                await delay(1000);
                await takeScreenshot(page, 'news-search-results');
            }

            console.log('  ✓ News search input works');
        });

        it('should have filter controls', async () => {
            // Check for time filter buttons
            const filterBtns = await page.$$('.ldr-filter-btn');
            console.log(`  Found ${filterBtns.length} filter buttons`);
            expect(filterBtns.length).to.be.greaterThan(0);

            // Click on different time filters
            for (const btn of filterBtns.slice(0, 3)) {
                const text = await btn.evaluate(el => el.textContent.trim());
                await btn.click();
                console.log(`  Clicked filter: "${text}"`);
                await delay(300);
            }

            await takeScreenshot(page, 'news-filters');
        });

        it('should have subscription links', async () => {
            const createSubLink = await page.$('a[href="/news/subscriptions/new"]');
            const manageSubLink = await page.$('a[href="/news/subscriptions"]');

            console.log(`  Create subscription link: ${createSubLink !== null}`);
            console.log(`  Manage subscriptions link: ${manageSubLink !== null}`);
        });

        it('should load subscriptions page', async () => {
            await page.goto(`${BASE_URL}/news/subscriptions`, { waitUntil: 'domcontentloaded' });
            await takeScreenshot(page, 'subscriptions-page');

            const url = page.url();
            expect(url).to.include('/subscriptions');
            console.log('  ✓ Subscriptions page loaded');
        });
    });

    describe('Library Page', () => {
        it('should load library page with filters', async () => {
            await page.goto(`${BASE_URL}/library/`, { waitUntil: 'domcontentloaded' });
            await logPageInfo(page, 'Library Page');
            await takeScreenshot(page, 'library-page');

            // Check for library container
            const libraryContainer = await page.$('.ldr-library-container');
            expect(libraryContainer).to.not.be.null;
            console.log('  ✓ Library container found');
        });

        it('should have collection filter', async () => {
            const collectionFilter = await page.$('#filter-collection');
            expect(collectionFilter).to.not.be.null;
            console.log('  ✓ Collection filter found');
        });

        it('should have domain filter', async () => {
            const domainFilter = await page.$('#filter-domain');
            expect(domainFilter).to.not.be.null;
            console.log('  ✓ Domain filter found');
        });

        it('should have search functionality', async () => {
            const searchInput = await page.$('#search-documents');
            expect(searchInput).to.not.be.null;

            await searchInput.type('test search');
            await takeScreenshot(page, 'library-search');
            console.log('  ✓ Library search input works');
        });

        it('should have action buttons', async () => {
            const syncBtn = await page.$('button[onclick="showSyncModal()"]');
            console.log(`  Sync button: ${syncBtn !== null}`);

            const getAllPdfsBtn = await page.$('button[onclick="downloadAllNew()"]');
            console.log(`  Get All PDFs button: ${getAllPdfsBtn !== null}`);

            await takeScreenshot(page, 'library-actions');
        });

        it('should navigate to collections page', async () => {
            await page.goto(`${BASE_URL}/library/collections`, { waitUntil: 'domcontentloaded' });
            await takeScreenshot(page, 'collections-page');

            const url = page.url();
            expect(url).to.include('/collections');
            console.log('  ✓ Collections page loaded');
        });
    });

    describe('History Page', () => {
        it('should load history page', async () => {
            await page.goto(`${BASE_URL}/history`, { waitUntil: 'domcontentloaded' });
            await logPageInfo(page, 'History Page');
            await takeScreenshot(page, 'history-page');

            const url = page.url();
            // History might redirect to home with history modal or have its own page
            console.log(`  History page URL: ${url}`);
        });
    });

    describe('Embedding Settings Page', () => {
        it('should load embedding settings page', async () => {
            await page.goto(`${BASE_URL}/library/embedding-settings`, { waitUntil: 'domcontentloaded' });
            await logPageInfo(page, 'Embedding Settings Page');
            await takeScreenshot(page, 'embedding-settings');

            const url = page.url();
            expect(url).to.include('/embedding-settings');
            console.log('  ✓ Embedding settings page loaded');
        });
    });

    describe('Navigation', () => {
        it('should have working sidebar navigation', async () => {
            await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });

            // Check sidebar exists
            const sidebar = await page.$('.ldr-sidebar, aside, nav');
            expect(sidebar).to.not.be.null;
            console.log('  ✓ Sidebar found');

            await takeScreenshot(page, 'sidebar-navigation');
        });

        it('should navigate to all main sections without errors', async () => {
            const routes = [
                { path: '/', name: 'Home', minContent: 100 },
                { path: '/settings', name: 'Settings', minContent: 100 },
                { path: '/news/', name: 'News', minContent: 100 },
                { path: '/library/', name: 'Library', minContent: 100 },
                { path: '/library/embedding-settings', name: 'Embeddings', minContent: 10 }  // May have minimal content
            ];

            for (const route of routes) {
                await page.goto(`${BASE_URL}${route.path}`, { waitUntil: 'domcontentloaded' });
                const title = await page.title();
                console.log(`  ${route.name}: ${title}`);

                // Verify page loaded (has some content - not a complete crash)
                const bodyText = await page.$eval('body', el => el.textContent.length);
                expect(bodyText).to.be.greaterThan(route.minContent);
            }

            console.log('  ✓ All main routes load successfully');
        });
    });

    describe('Error Handling', () => {
        it('should handle 404 pages gracefully', async () => {
            await page.goto(`${BASE_URL}/nonexistent-page-xyz`, { waitUntil: 'domcontentloaded' });
            await takeScreenshot(page, 'error-404');

            // Should not crash, should have some content
            const bodyText = await page.$eval('body', el => el.textContent);
            expect(bodyText.length).to.be.greaterThan(0);
            console.log('  ✓ 404 page handled gracefully');
        });
    });

    describe('Logout', () => {
        it('should be able to logout', async () => {
            // Find and click logout link
            await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' });

            const logoutLink = await page.$('a[href*="logout"]');
            if (logoutLink) {
                await logoutLink.click();
                await delay(1000);
                await takeScreenshot(page, 'after-logout');

                const url = page.url();
                console.log(`  After logout URL: ${url}`);
            }
        });
    });
});
