#!/usr/bin/env node
/**
 * Research Workflow UI Tests
 *
 * Tests for the research submission, progress tracking, and results viewing workflow.
 *
 * Run: node test_research_workflow_ci.js
 */

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

// ============================================================================
// Research Form Tests
// ============================================================================
const ResearchFormTests = {
    async researchFormStructure(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const queryInput = document.querySelector('#query, textarea[name="query"], input[name="query"]');
            const submitBtn = document.querySelector('button[type="submit"], input[type="submit"], .ldr-research-submit');
            const form = document.querySelector('form');

            return {
                hasQueryInput: !!queryInput,
                queryInputType: queryInput?.tagName?.toLowerCase(),
                hasSubmitBtn: !!submitBtn,
                submitBtnText: submitBtn?.textContent?.trim(),
                hasForm: !!form
            };
        });

        const passed = result.hasQueryInput && result.hasSubmitBtn;
        return {
            passed,
            message: passed
                ? `Research form complete (query: ${result.queryInputType}, submit: "${result.submitBtnText}")`
                : `Missing: query=${result.hasQueryInput}, submit=${result.hasSubmitBtn}`
        };
    },

    async advancedOptionsToggle(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            // Look for advanced options toggle button/link
            const toggleBtn = document.querySelector(
                '[data-bs-toggle="collapse"][data-bs-target*="advanced"], ' +
                '.advanced-options-toggle, ' +
                'button[onclick*="advanced"], ' +
                'a[href*="advanced"], ' +
                '.ldr-advanced-toggle'
            );

            if (!toggleBtn) return { hasToggle: false };

            // Find the collapsible section
            const advancedSection = document.querySelector(
                '#advancedOptions, .advanced-options, .collapse, [id*="advanced"]'
            );

            return {
                hasToggle: true,
                toggleText: toggleBtn.textContent?.trim(),
                hasSection: !!advancedSection,
                sectionVisible: advancedSection ? window.getComputedStyle(advancedSection).display !== 'none' : null
            };
        });

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

        return {
            passed: result.hasSection,
            message: result.hasSection
                ? `Advanced options toggle found ("${result.toggleText}"), section exists`
                : 'Toggle found but section missing'
        };
    },

    async modelProviderDropdown(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const providerSelect = document.querySelector(
                'select[name*="provider"], ' +
                '#llm_provider, ' +
                '.ldr-provider-select, ' +
                '[data-setting="llm.provider"]'
            );

            if (!providerSelect) return { exists: false };

            const options = Array.from(providerSelect.options || providerSelect.querySelectorAll('option'));

            return {
                exists: true,
                optionCount: options.length,
                options: options.slice(0, 5).map(o => o.textContent?.trim() || o.value),
                currentValue: providerSelect.value
            };
        });

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'No provider dropdown on main page (may be in settings)' };
        }

        return {
            passed: result.optionCount > 0,
            message: `Provider dropdown has ${result.optionCount} options: ${result.options.join(', ')}`
        };
    },

    async searchEngineDropdown(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const engineSelect = document.querySelector(
                'select[name*="search"], ' +
                '#search_tool, ' +
                '.ldr-search-engine-select, ' +
                '[data-setting*="search"]'
            );

            if (!engineSelect) return { exists: false };

            const options = Array.from(engineSelect.options || engineSelect.querySelectorAll('option'));

            return {
                exists: true,
                optionCount: options.length,
                options: options.slice(0, 5).map(o => o.textContent?.trim() || o.value)
            };
        });

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'No search engine dropdown on main page' };
        }

        return {
            passed: result.optionCount > 0,
            message: `Search engine dropdown has ${result.optionCount} options: ${result.options.join(', ')}`
        };
    },

    async researchModeSelector(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            // Look for mode selector (radio buttons, toggle, or dropdown)
            const modeRadios = document.querySelectorAll('input[name="mode"], input[name="research_mode"]');
            const modeSelect = document.querySelector('select[name="mode"], select[name="research_mode"]');
            const modeToggle = document.querySelector('.mode-toggle, .research-mode-toggle, [data-mode]');

            if (modeRadios.length > 0) {
                return {
                    exists: true,
                    type: 'radio',
                    options: Array.from(modeRadios).map(r => r.value)
                };
            }

            if (modeSelect) {
                return {
                    exists: true,
                    type: 'select',
                    options: Array.from(modeSelect.options).map(o => o.value)
                };
            }

            if (modeToggle) {
                return {
                    exists: true,
                    type: 'toggle'
                };
            }

            return { exists: false };
        });

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'No research mode selector found' };
        }

        return {
            passed: true,
            message: `Research mode selector found (${result.type})${result.options ? ': ' + result.options.join(', ') : ''}`
        };
    }
};

// ============================================================================
// Progress Page Tests
// ============================================================================
const ProgressTests = {
    async progressPageStructure(page, baseUrl) {
        // We need to check if there's any research in progress or check the page structure
        // First, check history for any research
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const historyResult = await page.evaluate(() => {
            const items = document.querySelectorAll('.history-item, .research-item, [data-research-id], tr[data-id]');
            if (items.length === 0) return { hasResearch: false };

            // Get first research ID
            const firstItem = items[0];
            const researchId = firstItem.dataset?.researchId || firstItem.dataset?.id ||
                              firstItem.querySelector('[data-research-id]')?.dataset?.researchId ||
                              firstItem.querySelector('a[href*="/results/"], a[href*="/progress/"]')?.href?.match(/\/(results|progress)\/(\d+)/)?.[2];

            return { hasResearch: true, researchId };
        });

        if (!historyResult.hasResearch) {
            return { passed: null, skipped: true, message: 'No research history to test progress page' };
        }

        // Try to access results page (completed research)
        await page.goto(`${baseUrl}/results/${historyResult.researchId}`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            // Check for common progress/results page elements
            const progressBar = document.querySelector('.progress-bar, .progress, [role="progressbar"]');
            const statusBadge = document.querySelector('.status-badge, .badge, [class*="status"]');
            const reportContent = document.querySelector('.report-content, .research-report, .markdown-content, #report');

            return {
                hasProgressBar: !!progressBar,
                hasStatusBadge: !!statusBadge,
                hasReportContent: !!reportContent,
                pageTitle: document.title
            };
        });

        const hasAnyElement = result.hasProgressBar || result.hasStatusBadge || result.hasReportContent;
        return {
            passed: hasAnyElement,
            message: hasAnyElement
                ? `Results page loaded (progress=${result.hasProgressBar}, status=${result.hasStatusBadge}, report=${result.hasReportContent})`
                : 'Results page missing expected elements'
        };
    }
};

// ============================================================================
// Results Page Tests
// ============================================================================
const ResultsTests = {
    async resultsPageStructure(page, baseUrl) {
        // Get a research ID from history
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const researchId = await page.evaluate(() => {
            const link = document.querySelector('a[href*="/results/"]');
            if (link) {
                const match = link.href.match(/\/results\/(\d+)/);
                return match ? match[1] : null;
            }
            return null;
        });

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research to test results page' };
        }

        await page.goto(`${baseUrl}/results/${researchId}`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            return {
                hasTitle: !!document.querySelector('h1, .research-title, .query-title'),
                hasReport: !!document.querySelector('.report-content, .research-report, .markdown-content, #report, .ldr-report'),
                hasMetadata: !!document.querySelector('.research-metadata, .metadata, .info-section'),
                hasActions: !!document.querySelector('.action-buttons, .export-buttons, .btn-group')
            };
        });

        const passed = result.hasTitle || result.hasReport;
        return {
            passed,
            message: passed
                ? `Results page structure OK (title=${result.hasTitle}, report=${result.hasReport}, metadata=${result.hasMetadata}, actions=${result.hasActions})`
                : 'Results page missing title and report content'
        };
    },

    async exportButtonsExist(page, baseUrl) {
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const researchId = await page.evaluate(() => {
            const link = document.querySelector('a[href*="/results/"]');
            if (link) {
                const match = link.href.match(/\/results\/(\d+)/);
                return match ? match[1] : null;
            }
            return null;
        });

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research to test export buttons' };
        }

        await page.goto(`${baseUrl}/results/${researchId}`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const buttons = document.querySelectorAll('button, a.btn, .dropdown-item');
            const buttonTexts = Array.from(buttons).map(b => b.textContent?.toLowerCase() || '');

            return {
                hasPdf: buttonTexts.some(t => t.includes('pdf')),
                hasMarkdown: buttonTexts.some(t => t.includes('markdown') || t.includes('.md')),
                hasLatex: buttonTexts.some(t => t.includes('latex') || t.includes('tex')),
                hasExport: buttonTexts.some(t => t.includes('export') || t.includes('download')),
                foundButtons: buttonTexts.filter(t => t.includes('export') || t.includes('download') || t.includes('pdf')).slice(0, 5)
            };
        });

        const hasAnyExport = result.hasPdf || result.hasMarkdown || result.hasLatex || result.hasExport;
        return {
            passed: hasAnyExport,
            message: hasAnyExport
                ? `Export options found: PDF=${result.hasPdf}, Markdown=${result.hasMarkdown}, LaTeX=${result.hasLatex}`
                : 'No export buttons found'
        };
    },

    async starRatingExists(page, baseUrl) {
        await page.goto(`${baseUrl}/history`, { waitUntil: 'networkidle2' });

        const researchId = await page.evaluate(() => {
            const link = document.querySelector('a[href*="/results/"]');
            if (link) {
                const match = link.href.match(/\/results\/(\d+)/);
                return match ? match[1] : null;
            }
            return null;
        });

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research to test star rating' };
        }

        await page.goto(`${baseUrl}/results/${researchId}`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(() => {
            const stars = document.querySelectorAll('.star-rating, .rating, [class*="star"], .fa-star, .bi-star');
            const ratingContainer = document.querySelector('.rating-container, .star-container, [data-rating]');

            return {
                hasStars: stars.length > 0,
                starCount: stars.length,
                hasRatingContainer: !!ratingContainer
            };
        });

        if (!result.hasStars && !result.hasRatingContainer) {
            return { passed: null, skipped: true, message: 'No star rating system on results page' };
        }

        return {
            passed: true,
            message: `Star rating found (${result.starCount} star elements)`
        };
    }
};

// ============================================================================
// API Tests (checking API endpoints respond)
// ============================================================================
const ApiTests = {
    async historyApiResponds(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/api/history`);
                return {
                    ok: response.ok,
                    status: response.status,
                    isJson: response.headers.get('content-type')?.includes('application/json')
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

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

    async settingsApiResponds(page, baseUrl) {
        await page.goto(`${baseUrl}/`, { waitUntil: 'networkidle2' });

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/settings/api`);
                return {
                    ok: response.ok,
                    status: response.status
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

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

// ============================================================================
// Main Test Runner
// ============================================================================
async function main() {
    log.section('Research Workflow Tests');

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

    try {
        // Research Form Tests
        log.section('Research Form');
        await results.run('Form', 'Research Form Structure', () => ResearchFormTests.researchFormStructure(page, baseUrl), { page });

        const advancedResult = await ResearchFormTests.advancedOptionsToggle(page, baseUrl);
        if (advancedResult.skipped) {
            results.skip('Form', 'Advanced Options Toggle', advancedResult.message);
        } else {
            results.add('Form', 'Advanced Options Toggle', advancedResult.passed, advancedResult.message);
        }

        const providerResult = await ResearchFormTests.modelProviderDropdown(page, baseUrl);
        if (providerResult.skipped) {
            results.skip('Form', 'Model Provider Dropdown', providerResult.message);
        } else {
            results.add('Form', 'Model Provider Dropdown', providerResult.passed, providerResult.message);
        }

        const engineResult = await ResearchFormTests.searchEngineDropdown(page, baseUrl);
        if (engineResult.skipped) {
            results.skip('Form', 'Search Engine Dropdown', engineResult.message);
        } else {
            results.add('Form', 'Search Engine Dropdown', engineResult.passed, engineResult.message);
        }

        const modeResult = await ResearchFormTests.researchModeSelector(page, baseUrl);
        if (modeResult.skipped) {
            results.skip('Form', 'Research Mode Selector', modeResult.message);
        } else {
            results.add('Form', 'Research Mode Selector', modeResult.passed, modeResult.message);
        }

        // Progress/Results Tests
        log.section('Progress & Results');
        const progressResult = await ProgressTests.progressPageStructure(page, baseUrl);
        if (progressResult.skipped) {
            results.skip('Results', 'Progress/Results Page Structure', progressResult.message);
        } else {
            results.add('Results', 'Progress/Results Page Structure', progressResult.passed, progressResult.message);
        }

        const resultsResult = await ResultsTests.resultsPageStructure(page, baseUrl);
        if (resultsResult.skipped) {
            results.skip('Results', 'Results Page Structure', resultsResult.message);
        } else {
            results.add('Results', 'Results Page Structure', resultsResult.passed, resultsResult.message);
        }

        const exportResult = await ResultsTests.exportButtonsExist(page, baseUrl);
        if (exportResult.skipped) {
            results.skip('Results', 'Export Buttons Exist', exportResult.message);
        } else {
            results.add('Results', 'Export Buttons Exist', exportResult.passed, exportResult.message);
        }

        const starResult = await ResultsTests.starRatingExists(page, baseUrl);
        if (starResult.skipped) {
            results.skip('Results', 'Star Rating Exists', starResult.message);
        } else {
            results.add('Results', 'Star Rating Exists', starResult.passed, starResult.message);
        }

        // API Tests
        log.section('API Endpoints');
        await results.run('API', 'History API Responds', () => ApiTests.historyApiResponds(page, baseUrl), { page });
        await results.run('API', 'Settings API Responds', () => ApiTests.settingsApiResponds(page, baseUrl), { page });

    } catch (error) {
        // Check if this is a navigation/browser error (not a real test failure)
        const isNavigationError = error.message &&
            (error.message.includes('context was destroyed') ||
             error.message.includes('Target closed') ||
             error.message.includes('Protocol error') ||
             error.message.includes('Session closed') ||
             error.message.includes('page has been closed'));

        if (isNavigationError) {
            log.warn(`Browser navigation error (test infrastructure issue): ${error.message}`);
            // Don't count as failure - this is a flaky test issue
        } else {
            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 = { ResearchFormTests, ProgressTests, ResultsTests, ApiTests };
