#!/usr/bin/env node
/**
 * Research Results & Export UI Tests
 *
 * Tests for the results page including star ratings, export buttons,
 * and download functionality.
 *
 * Run: node test_results_exports_ci.js
 */

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

// ============================================================================
// Helper to find a completed research
// ============================================================================
async function findCompletedResearch(page, baseUrl) {
    return await page.evaluate(async (url) => {
        try {
            const response = await fetch(`${url}/history/api`);
            if (!response.ok) return null;
            const data = await response.json();
            const completed = data.items?.find(item => item.status === 'completed');
            return completed?.id || null;
        } catch (e) {
            return null;
        }
    }, baseUrl);
}

// ============================================================================
// Results Page Structure Tests
// ============================================================================
const ResultsPageTests = {
    async resultsPageLoads(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const hasContent = document.body.textContent.length > 100;
            const hasResultsContainer = !!document.querySelector('.results, .research-results, #results, main');
            const title = document.title;

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

        return {
            passed: result.hasContent && result.hasResultsContainer,
            message: `Results page loads (content=${result.hasContent}, container=${result.hasResultsContainer})`
        };
    },

    async resultsMetadataDisplay(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            // Look for metadata elements
            const queryDisplay = document.querySelector('.query, .research-query, [class*="query"]');
            const dateDisplay = document.querySelector('.date, .created, [class*="date"], time');
            const modeBadge = document.querySelector('.badge, .mode, [class*="mode"]');
            const durationDisplay = document.querySelector('.duration, [class*="duration"], [class*="time"]');

            return {
                hasQuery: !!queryDisplay,
                queryText: queryDisplay?.textContent?.trim()?.substring(0, 50),
                hasDate: !!dateDisplay,
                hasMode: !!modeBadge,
                hasDuration: !!durationDisplay,
                modeText: modeBadge?.textContent?.trim()
            };
        });

        const hasMetadata = result.hasQuery || result.hasDate || result.hasMode;

        return {
            passed: hasMetadata,
            message: `Metadata: query="${result.queryText}", date=${result.hasDate}, mode="${result.modeText}"`
        };
    },

    async resultsContentRendered(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            // Look for rendered markdown content
            const contentArea = document.querySelector('.markdown, .content, .results-content, .report, article');
            const hasHeadings = document.querySelectorAll('h1, h2, h3').length > 0;
            const hasParagraphs = document.querySelectorAll('p').length > 0;
            const hasLists = document.querySelectorAll('ul, ol').length > 0;
            const hasLinks = document.querySelectorAll('a[href^="http"]').length > 0;

            return {
                hasContentArea: !!contentArea,
                contentLength: contentArea?.textContent?.length || 0,
                hasHeadings,
                hasParagraphs,
                hasLists,
                hasLinks
            };
        });

        // Consider content rendered if we have a content area with text OR if structural elements exist
        const hasContent = (result.hasContentArea && result.contentLength > 100) ||
                          (result.hasHeadings && result.hasParagraphs);

        // Skip instead of fail if no completed research was available
        if (!hasContent && result.contentLength === 0) {
            return { passed: null, skipped: true, message: 'No content rendered (may not have completed research data)' };
        }

        return {
            passed: hasContent,
            message: `Content rendered: ${result.contentLength} chars, headings=${result.hasHeadings}, paragraphs=${result.hasParagraphs}, lists=${result.hasLists}`
        };
    }
};

// ============================================================================
// Star Rating Tests
// ============================================================================
const StarRatingTests = {
    async starRatingWidgetExists(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const starWidget = document.querySelector(
                '.star-rating, ' +
                '.rating, ' +
                '[class*="star"], ' +
                '[class*="rating"], ' +
                '.stars'
            );

            const stars = document.querySelectorAll(
                '.star, ' +
                '[class*="star"]:not([class*="start"]), ' +
                'input[type="radio"][name*="rating"], ' +
                '.rating-star'
            );

            return {
                hasWidget: !!starWidget,
                starCount: stars.length,
                widgetClass: starWidget?.className
            };
        });

        if (!result.hasWidget && result.starCount === 0) {
            return { passed: null, skipped: true, message: 'No star rating widget found on results page' };
        }

        return {
            passed: true,
            message: `Star rating widget: ${result.starCount} stars found, class="${result.widgetClass}"`
        };
    },

    async starRatingClickable(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const stars = document.querySelectorAll(
                '.star, ' +
                '[class*="star"]:not([class*="start"]), ' +
                'input[type="radio"][name*="rating"], ' +
                '.rating-star, ' +
                '.star-rating button, ' +
                '.star-rating span[data-value]'
            );

            if (stars.length === 0) return { hasStars: false };

            // Try to click the third star (rating of 3)
            const targetStar = stars[Math.min(2, stars.length - 1)];
            const beforeClick = document.querySelector('.star-rating, .rating')?.className || '';

            targetStar.click();

            const afterClick = document.querySelector('.star-rating, .rating')?.className || '';

            return {
                hasStars: true,
                starCount: stars.length,
                clicked: true,
                classChanged: beforeClick !== afterClick
            };
        });

        if (!result.hasStars) {
            return { passed: null, skipped: true, message: 'No clickable stars found' };
        }

        return {
            passed: result.clicked,
            message: `Stars are clickable (${result.starCount} stars, class changed: ${result.classChanged})`
        };
    },

    async starRatingHoverFeedback(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const starSelector = '.ldr-star';
        const star = await page.$(starSelector);

        if (!star) {
            return { passed: null, skipped: true, message: 'No star elements found for hover test' };
        }

        // Get initial state
        const beforeHover = await page.evaluate((sel) => {
            const star = document.querySelector(sel);
            return {
                className: star?.className,
                style: star ? window.getComputedStyle(star).color : null
            };
        }, starSelector);

        // Hover over star
        await star.hover();
        await delay(200);

        // Get hover state
        const afterHover = await page.evaluate((sel) => {
            const star = document.querySelector(sel);
            return {
                className: star?.className,
                style: star ? window.getComputedStyle(star).color : null
            };
        }, starSelector);

        const hasHoverEffect = afterHover.className?.includes('ldr-hover') ||
                              beforeHover.className !== afterHover.className ||
                              beforeHover.style !== afterHover.style;

        return {
            passed: hasHoverEffect,
            message: hasHoverEffect
                ? 'Star rating has hover feedback'
                : 'No visible hover feedback detected'
        };
    }
};

// ============================================================================
// Export Button Tests
// ============================================================================
const ExportButtonTests = {
    async exportButtonsExist(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

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

            for (const btn of buttons) {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                const onclick = btn.getAttribute('onclick')?.toLowerCase() || '';

                if (text.includes('pdf') || href.includes('pdf') || onclick.includes('pdf')) {
                    exportButtons.pdf = true;
                }
                if (text.includes('markdown') || text.includes('.md') || href.includes('markdown')) {
                    exportButtons.markdown = true;
                }
                if (text.includes('latex') || text.includes('.tex') || href.includes('latex')) {
                    exportButtons.latex = true;
                }
                if (text.includes('quarto') || text.includes('.qmd') || href.includes('quarto')) {
                    exportButtons.quarto = true;
                }
                if (text.includes('ris') || text.includes('zotero') || href.includes('ris')) {
                    exportButtons.ris = true;
                }
                if (text.includes('export') || text.includes('download')) {
                    exportButtons.generic = true;
                }
            }

            return {
                found: Object.keys(exportButtons),
                pdf: exportButtons.pdf || false,
                markdown: exportButtons.markdown || false,
                latex: exportButtons.latex || false,
                quarto: exportButtons.quarto || false,
                ris: exportButtons.ris || false,
                generic: exportButtons.generic || false
            };
        });

        const hasExportButtons = result.found.length > 0;

        return {
            passed: hasExportButtons,
            message: `Export buttons: ${result.found.join(', ') || 'none found'}`
        };
    },

    async pdfExportButton(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn, a[download]'));
            const pdfButton = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                return text.includes('pdf') || href.includes('pdf') || href.includes('/export/');
            });

            return {
                hasPdfButton: !!pdfButton,
                buttonText: pdfButton?.textContent?.trim(),
                href: pdfButton?.href
            };
        });

        if (!result.hasPdfButton) {
            return { passed: null, skipped: true, message: 'No PDF export button found' };
        }

        return {
            passed: true,
            message: `PDF export button: "${result.buttonText}" (href: ${result.href?.substring(0, 50)}...)`
        };
    },

    async markdownExportButton(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn, a[download]'));
            const mdButton = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                return text.includes('markdown') || text.includes('.md') || href.includes('markdown');
            });

            return {
                hasMdButton: !!mdButton,
                buttonText: mdButton?.textContent?.trim(),
                href: mdButton?.href
            };
        });

        if (!result.hasMdButton) {
            return { passed: null, skipped: true, message: 'No Markdown export button found' };
        }

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

    async exportDropdownMenu(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

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

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            // Look for export dropdown
            const dropdown = document.querySelector(
                '.dropdown, ' +
                '[class*="export-dropdown"], ' +
                '.export-menu, ' +
                'details.export, ' +
                '[data-dropdown="export"]'
            );

            const dropdownToggle = document.querySelector(
                '.dropdown-toggle, ' +
                '[data-toggle="dropdown"], ' +
                'summary, ' +
                '.export-btn'
            );

            return {
                hasDropdown: !!dropdown,
                hasToggle: !!dropdownToggle,
                toggleText: dropdownToggle?.textContent?.trim()
            };
        });

        if (!result.hasDropdown && !result.hasToggle) {
            return { passed: null, skipped: true, message: 'No export dropdown menu found' };
        }

        return {
            passed: true,
            message: `Export dropdown: toggle="${result.toggleText}"`
        };
    }
};

// ============================================================================
// Action Button Tests
// ============================================================================
const ActionButtonTests = {
    async backToHistoryButton(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research for back button test' };
        }

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn, a'));
            const backButton = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                return text.includes('back') || text.includes('history') ||
                       href.includes('history') || btn.classList.contains('back');
            });

            return {
                hasBackButton: !!backButton,
                buttonText: backButton?.textContent?.trim(),
                href: backButton?.href
            };
        });

        return {
            passed: result.hasBackButton,
            message: result.hasBackButton
                ? `Back button: "${result.buttonText}"`
                : 'No back to history button found'
        };
    },

    async viewMetricsButton(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research for metrics button test' };
        }

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn, a'));
            const metricsButton = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                const href = btn.href?.toLowerCase() || '';
                return text.includes('metric') || text.includes('stats') ||
                       text.includes('analytics') || href.includes('metrics');
            });

            return {
                hasMetricsButton: !!metricsButton,
                buttonText: metricsButton?.textContent?.trim()
            };
        });

        if (!result.hasMetricsButton) {
            return { passed: null, skipped: true, message: 'No view metrics button found' };
        }

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

    async helpImproveButton(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research for help button test' };
        }

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn, a'));
            const helpButton = buttons.find(btn => {
                const text = btn.textContent?.toLowerCase() || '';
                return text.includes('help') || text.includes('improve') ||
                       text.includes('feedback') || text.includes('report');
            });

            return {
                hasHelpButton: !!helpButton,
                buttonText: helpButton?.textContent?.trim()
            };
        });

        if (!result.hasHelpButton) {
            return { passed: null, skipped: true, message: 'No help/feedback button found' };
        }

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

// ============================================================================
// Log Panel Tests
// ============================================================================
const LogPanelTests = {
    async logPanelExists(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research for log panel test' };
        }

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        const result = await page.evaluate(() => {
            const logPanel = document.querySelector(
                '.logs, ' +
                '.log-panel, ' +
                '#logs, ' +
                '[class*="log"], ' +
                '.research-logs, ' +
                'details.logs, ' +
                '.accordion-logs'
            );

            const logToggle = document.querySelector(
                '[data-toggle="logs"], ' +
                '.log-toggle, ' +
                'summary:has(~ .logs), ' +
                '[aria-controls*="log"]'
            );

            return {
                hasLogPanel: !!logPanel,
                hasToggle: !!logToggle,
                toggleText: logToggle?.textContent?.trim()
            };
        });

        if (!result.hasLogPanel && !result.hasToggle) {
            return { passed: null, skipped: true, message: 'No log panel found on results page' };
        }

        return {
            passed: true,
            message: `Log panel: panel=${result.hasLogPanel}, toggle="${result.toggleText}"`
        };
    },

    async logPanelExpandable(page, baseUrl) {
        const researchId = await findCompletedResearch(page, baseUrl);

        if (!researchId) {
            return { passed: null, skipped: true, message: 'No completed research for expandable log test' };
        }

        await navigateTo(page, `${baseUrl}/results/${researchId}`);

        // Click log toggle
        const clicked = await page.evaluate(() => {
            const toggle = document.querySelector(
                '[data-toggle="logs"], ' +
                '.log-toggle, ' +
                'summary, ' +
                '[aria-controls*="log"], ' +
                '.collapsible-header'
            );

            if (toggle) {
                toggle.click();
                return true;
            }
            return false;
        });

        if (!clicked) {
            return { passed: null, skipped: true, message: 'No log toggle to click' };
        }

        await delay(300);

        const result = await page.evaluate(() => {
            const logContent = document.querySelector(
                '.logs, .log-content, .log-entries, pre.logs, code.logs'
            );

            return {
                hasContent: !!logContent,
                contentLength: logContent?.textContent?.length || 0,
                visible: logContent ? window.getComputedStyle(logContent).display !== 'none' : false
            };
        });

        return {
            passed: result.visible || result.contentLength > 0,
            message: `Log panel expandable: visible=${result.visible}, content=${result.contentLength} chars`
        };
    }
};

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

    const ctx = await setupTest({ authenticate: true });
    const results = new TestResults('Results & Export 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 {
        // Results Page Structure
        log.section('Results Page Structure');

        await run('Page', 'Results Page Loads', (p, u) => ResultsPageTests.resultsPageLoads(p, u));
        await run('Page', 'Metadata Display', (p, u) => ResultsPageTests.resultsMetadataDisplay(p, u));
        await run('Page', 'Content Rendered', (p, u) => ResultsPageTests.resultsContentRendered(p, u));

        // Star Rating Tests
        log.section('Star Rating');

        await run('Stars', 'Widget Exists', (p, u) => StarRatingTests.starRatingWidgetExists(p, u));
        await run('Stars', 'Clickable', (p, u) => StarRatingTests.starRatingClickable(p, u));
        await run('Stars', 'Hover Feedback', (p, u) => StarRatingTests.starRatingHoverFeedback(p, u));

        // Export Button Tests
        log.section('Export Buttons');

        await run('Export', 'Buttons Exist', (p, u) => ExportButtonTests.exportButtonsExist(p, u));
        await run('Export', 'PDF Button', (p, u) => ExportButtonTests.pdfExportButton(p, u));
        await run('Export', 'Markdown Button', (p, u) => ExportButtonTests.markdownExportButton(p, u));
        await run('Export', 'Dropdown Menu', (p, u) => ExportButtonTests.exportDropdownMenu(p, u));

        // Action Buttons
        log.section('Action Buttons');

        await run('Actions', 'Back to History', (p, u) => ActionButtonTests.backToHistoryButton(p, u));
        await run('Actions', 'View Metrics', (p, u) => ActionButtonTests.viewMetricsButton(p, u));
        await run('Actions', 'Help/Feedback', (p, u) => ActionButtonTests.helpImproveButton(p, u));

        // Log Panel
        log.section('Log Panel');

        await run('Logs', 'Panel Exists', (p, u) => LogPanelTests.logPanelExists(p, u));
        await run('Logs', 'Expandable', (p, u) => LogPanelTests.logPanelExpandable(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 = { ResultsPageTests, StarRatingTests, ExportButtonTests, ActionButtonTests, LogPanelTests };
