#!/usr/bin/env node
/**
 * Real-time Progress UI Tests
 *
 * Tests for the research progress page and real-time update elements.
 *
 * Run: node test_realtime_progress_ci.js
 */
const { setupTest, teardownTest, TestResults, log, delay, navigateTo, withTimeout } = require('./test_lib');

// ============================================================================
// Progress Page Structure Tests
// ============================================================================
const ProgressPageTests = {
    async progressPageStructure(page, baseUrl) {
        // First check if there's any research in progress or history
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            // Try to find any research ID from history
            const item = document.querySelector('[data-research-id], [data-id], a[href*="/progress/"], a[href*="/results/"]');
            if (!item) return null;

            const href = item.href || '';
            const match = href.match(/\/(progress|results)\/([a-zA-Z0-9-]+)/);
            if (match) return match[2];

            return item.dataset?.researchId || item.dataset?.id;
        });

        if (!researchId) {
            // Navigate to a test progress page anyway to check structure
            await navigateTo(page, `${baseUrl}/progress/test-id`);
        } else {
            await navigateTo(page, `${baseUrl}/progress/${researchId}`);
        }

        const result = await page.evaluate(() => {
            // Page-specific container that ONLY the progress template renders
            // (pages/progress.html: <div class="ldr-page active" id="research-progress">).
            // This fails if a login/error page loaded instead of the progress page.
            const pageContainer = document.querySelector('#research-progress.ldr-page');

            // Real progress signals rendered server-side by the progress template,
            // not generic page chrome:
            //   - the progress bar:   <div class="ldr-progress-bar" role="progressbar">
            //   - the percent display: <div id="progress-percentage">
            //   - the status indicator:<div id="status-text">
            const progressBar = document.querySelector('.ldr-progress-bar[role="progressbar"]');
            const percentDisplay = document.querySelector('#progress-percentage');
            const statusIndicator = document.querySelector('#status-text');

            return {
                hasPageContainer: !!pageContainer,
                hasProgressBar: !!progressBar,
                hasPercentDisplay: !!percentDisplay,
                hasStatusIndicator: !!statusIndicator,
                is404: document.body.textContent?.toLowerCase().includes('not found') ||
                       document.body.textContent?.toLowerCase().includes('404')
            };
        });

        if (result.is404) {
            return { passed: null, skipped: true, message: 'No active research to test progress page' };
        }

        // Require the progress-specific container AND at least one real progress
        // signal (bar / percent / status) — a bare <h1> on any page is no longer enough.
        const hasProgressSignal = result.hasProgressBar || result.hasPercentDisplay || result.hasStatusIndicator;
        const passed = result.hasPageContainer && hasProgressSignal;
        return {
            passed,
            message: passed
                ? `Progress page structure OK (container #research-progress + ` +
                  `bar=${result.hasProgressBar}, percent=${result.hasPercentDisplay}, status=${result.hasStatusIndicator})`
                : `Progress page missing expected elements ` +
                  `(container=${result.hasPageContainer}, bar=${result.hasProgressBar}, ` +
                  `percent=${result.hasPercentDisplay}, status=${result.hasStatusIndicator})`
        };
    },

    async progressBarExists(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], [data-id], a[href*="/progress/"]');
            if (!item) return null;
            const href = item.href || '';
            const match = href.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : (item.dataset?.researchId || item.dataset?.id);
        });

        if (!researchId) {
            await navigateTo(page, `${baseUrl}/progress/test-id`);
        } else {
            await navigateTo(page, `${baseUrl}/progress/${researchId}`);
        }

        const result = await page.evaluate(() => {
            const progressBar = document.querySelector(
                '.progress-bar, ' +
                'progress, ' +
                '[role="progressbar"], ' +
                '.ldr-progress-bar, ' +
                '[class*="progress-bar"]'
            );

            if (progressBar) {
                return {
                    exists: true,
                    hasValue: progressBar.value !== undefined || progressBar.style.width || progressBar.getAttribute('aria-valuenow'),
                    value: progressBar.value || progressBar.style.width || progressBar.getAttribute('aria-valuenow')
                };
            }

            return { exists: false };
        });

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'No progress bar element found' };
        }

        return {
            passed: true,
            message: `Progress bar exists (value: ${result.value || 'not set'})`
        };
    },

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

        // Check home page for any in-progress indicators first
        const homeResult = await page.evaluate(() => {
            const percentPattern = /(\d+)\s*%/;
            const bodyText = document.body.textContent || '';
            const match = bodyText.match(percentPattern);

            const percentElement = document.querySelector(
                '.percentage, ' +
                '.progress-percentage, ' +
                '[class*="percent"]'
            );

            return {
                hasPercentElement: !!percentElement,
                percentText: percentElement?.textContent?.trim(),
                foundInText: !!match,
                percentValue: match ? match[1] : null
            };
        });

        if (homeResult.hasPercentElement || homeResult.foundInText) {
            return {
                passed: true,
                message: `Progress percentage found: ${homeResult.percentText || homeResult.percentValue + '%'}`
            };
        }

        // Try progress page
        await navigateTo(page, `${baseUrl}/history`);
        const researchId = await page.evaluate(() => {
            const item = document.querySelector('a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : null;
        });

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

            const progressResult = await page.evaluate(() => {
                const percentPattern = /(\d+)\s*%/;
                const bodyText = document.body.textContent || '';
                const match = bodyText.match(percentPattern);

                return {
                    found: !!match,
                    value: match ? match[1] : null
                };
            });

            if (progressResult.found) {
                return {
                    passed: true,
                    message: `Progress percentage displayed: ${progressResult.value}%`
                };
            }
        }

        return { passed: null, skipped: true, message: 'No progress percentage display found' };
    },

    async progressStatusMessages(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : (item?.dataset?.researchId || null);
        });

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

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

        const result = await page.evaluate(() => {
            const statusArea = document.querySelector(
                '.status-messages, ' +
                '.progress-status, ' +
                '.status-text, ' +
                '[class*="status-message"], ' +
                '.current-step'
            );

            const hasStatusText = document.body.textContent?.toLowerCase().includes('searching') ||
                                  document.body.textContent?.toLowerCase().includes('analyzing') ||
                                  document.body.textContent?.toLowerCase().includes('generating') ||
                                  document.body.textContent?.toLowerCase().includes('completed');

            return {
                hasStatusArea: !!statusArea,
                statusText: statusArea?.textContent?.trim().substring(0, 100),
                hasStatusText
            };
        });

        if (!result.hasStatusArea && !result.hasStatusText) {
            return { passed: null, skipped: true, message: 'No status messages area found' };
        }

        return {
            passed: true,
            message: result.hasStatusArea
                ? `Status area found: "${result.statusText?.substring(0, 50)}..."`
                : 'Status text found in page'
        };
    },

    async progressLogPanel(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : (item?.dataset?.researchId || null);
        });

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

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

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

            const logEntries = document.querySelectorAll('.log-entry, .log-line, .log-message');

            return {
                hasLogPanel: !!logPanel,
                logEntryCount: logEntries.length,
                sampleLog: logPanel?.textContent?.substring(0, 100)
            };
        });

        if (!result.hasLogPanel && result.logEntryCount === 0) {
            return { passed: null, skipped: true, message: 'No log panel found' };
        }

        return {
            passed: true,
            message: result.hasLogPanel
                ? `Log panel found (${result.logEntryCount} entries)`
                : `${result.logEntryCount} log entries found`
        };
    },

    async progressCancelButton(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        // First check home page for active research
        await navigateTo(page, `${baseUrl}/`);

        const homeResult = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const cancelBtn = buttons.find(b => {
                const text = b.textContent?.toLowerCase() || '';
                return text.includes('cancel') || text.includes('stop') || text.includes('terminate');
            });

            return {
                found: !!cancelBtn,
                text: cancelBtn?.textContent?.trim()
            };
        });

        if (homeResult.found) {
            return {
                passed: true,
                message: `Cancel button found on home: "${homeResult.text}"`
            };
        }

        // Check progress page
        const researchId = await page.evaluate(() => {
            const item = document.querySelector('a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : null;
        });

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

            const progressResult = await page.evaluate(() => {
                const buttons = Array.from(document.querySelectorAll('button, a.btn'));
                const cancelBtn = buttons.find(b => {
                    const text = b.textContent?.toLowerCase() || '';
                    return text.includes('cancel') || text.includes('stop') || text.includes('terminate');
                });

                return {
                    found: !!cancelBtn,
                    text: cancelBtn?.textContent?.trim()
                };
            });

            if (progressResult.found) {
                return {
                    passed: true,
                    message: `Cancel button found: "${progressResult.text}"`
                };
            }
        }

        return { passed: null, skipped: true, message: 'No cancel button found (may only appear during active research)' };
    },

    async progressPhaseIndicator(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : null;
        });

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

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

        const result = await page.evaluate(() => {
            const phaseIndicator = document.querySelector(
                '.phase-indicator, ' +
                '.current-phase, ' +
                '.step-indicator, ' +
                '[class*="phase"], ' +
                '.research-stage'
            );

            // Look for phase/step text patterns
            const phasePattern = /(?:phase|step|stage)\s*[:\d]/i;
            const bodyText = document.body.textContent || '';
            const hasPhaseText = phasePattern.test(bodyText);

            // Look for stepper/timeline component
            const stepper = document.querySelector('.stepper, .timeline, .steps, .wizard-steps');

            return {
                hasPhaseIndicator: !!phaseIndicator,
                phaseText: phaseIndicator?.textContent?.trim(),
                hasPhaseInText: hasPhaseText,
                hasStepper: !!stepper
            };
        });

        if (!result.hasPhaseIndicator && !result.hasPhaseInText && !result.hasStepper) {
            return { passed: null, skipped: true, message: 'No phase indicator found' };
        }

        return {
            passed: true,
            message: result.hasPhaseIndicator
                ? `Phase indicator: "${result.phaseText}"`
                : (result.hasStepper ? 'Stepper component found' : 'Phase text found in page')
        };
    },

    async progressEstimatedTime(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            const item = document.querySelector('[data-research-id], a[href*="/progress/"]');
            const match = item?.href?.match(/\/progress\/([a-zA-Z0-9-]+)/);
            return match ? match[1] : null;
        });

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

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

        const result = await page.evaluate(() => {
            const timeElement = document.querySelector(
                '.estimated-time, ' +
                '.time-remaining, ' +
                '.elapsed-time, ' +
                '[class*="duration"], ' +
                '.eta'
            );

            // Look for time patterns
            const timePattern = /\d+\s*(?:min|sec|hour|[smh])|remaining|elapsed|eta/i;
            const bodyText = document.body.textContent || '';
            const hasTimeText = timePattern.test(bodyText);

            return {
                hasTimeElement: !!timeElement,
                timeText: timeElement?.textContent?.trim(),
                hasTimeInText: hasTimeText
            };
        });

        if (!result.hasTimeElement && !result.hasTimeInText) {
            return { passed: null, skipped: true, message: 'No time display found' };
        }

        return {
            passed: true,
            message: result.hasTimeElement
                ? `Time display: "${result.timeText}"`
                : 'Time information found in page'
        };
    }
};

// ============================================================================
// Main Test Runner
// ============================================================================
async function main() {
    log.section('Real-time Progress Tests');

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

        // The progress route renders pages/progress.html unconditionally for ANY
        // research_id (no 404), so the page STRUCTURE is server-side and can be
        // validated without real research — run it unconditionally so a no-LLM CI
        // shard actually exercises it (fail, not silently skip).
        await run('Structure', 'Progress Page Structure', (p, u) => ProgressPageTests.progressPageStructure(p, u));

        // The remaining subtests depend on LIVE research data (bar value, status
        // messages, log entries, percentage, ETA) that only exists during an active
        // run. On a fresh CI DB with no research, skip those — they aren't dead, they
        // genuinely require data we can't produce without an LLM.
        await navigateTo(page, `${baseUrl}/history`);
        const hasResearch = await page.evaluate(() => {
            return !!document.querySelector('[data-research-id], [data-id], a[href*="/progress/"], a[href*="/results/"]');
        });

        if (!hasResearch) {
            const skipMsg = 'No research history found (fresh CI environment)';
            results.skip('Structure', 'Progress Bar Exists', skipMsg);
            results.skip('Structure', 'Progress Percentage Display', skipMsg);
            results.skip('Structure', 'Progress Status Messages', skipMsg);
            results.skip('Structure', 'Progress Log Panel', skipMsg);
            results.skip('Structure', 'Progress Cancel Button', skipMsg);
            results.skip('Structure', 'Progress Phase Indicator', skipMsg);
            results.skip('Structure', 'Progress Estimated Time', skipMsg);
        } else {
            await run('Structure', 'Progress Bar Exists', (p, u) => ProgressPageTests.progressBarExists(p, u));
            await run('Structure', 'Progress Percentage Display', (p, u) => ProgressPageTests.progressPercentageDisplay(p, u));
            await run('Structure', 'Progress Status Messages', (p, u) => ProgressPageTests.progressStatusMessages(p, u));
            await run('Structure', 'Progress Log Panel', (p, u) => ProgressPageTests.progressLogPanel(p, u));
            await run('Structure', 'Progress Cancel Button', (p, u) => ProgressPageTests.progressCancelButton(p, u));
            await run('Structure', 'Progress Phase Indicator', (p, u) => ProgressPageTests.progressPhaseIndicator(p, u));
            await run('Structure', 'Progress Estimated Time', (p, u) => ProgressPageTests.progressEstimatedTime(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 = { ProgressPageTests };
