#!/usr/bin/env node
/**
 * Follow-up Research UI Tests
 *
 * Tests for the follow-up research functionality from existing research results.
 *
 * Run: node test_followup_research_ci.js
 */

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

/**
 * Navigate with a single retry on timeout.
 *
 * In CI the server can be slow after previous tests finished (heavy DB
 * operations, template rendering). A one-shot retry avoids a cascade of
 * "detached frame" failures that would otherwise mark every remaining
 * sub-test as broken.
 */
async function navigateToWithRetry(page, url) {
    try {
        await navigateTo(page, url);
    } catch (firstError) {
        // Retry once after a short pause
        await delay(2000);
        await navigateTo(page, url);
    }
}

// ============================================================================
// Follow-up Research Tests
// ============================================================================
const FollowupResearchTests = {
    async followupButtonOnResults(page, baseUrl) {
        // First find a completed research
        await navigateToWithRetry(page, `${baseUrl}/history`);

        const researchId = await page.evaluate(() => {
            // Look for completed research with results link
            const resultsLink = document.querySelector('a[href*="/results/"]');
            if (resultsLink) {
                const match = resultsLink.href.match(/\/results\/([a-zA-Z0-9-]+)/);
                return match ? match[1] : null;
            }

            // Try data attributes
            const item = document.querySelector('[data-research-id], [data-id]');
            return item?.dataset?.researchId || item?.dataset?.id;
        });

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

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

        const result = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn, .btn'));
            const followupBtn = buttons.find(b => {
                const text = b.textContent?.toLowerCase() || '';
                return text.includes('follow') || text.includes('continue') ||
                       text.includes('deeper') || text.includes('expand');
            });

            return {
                hasFollowupButton: !!followupBtn,
                buttonText: followupBtn?.textContent?.trim()
            };
        });

        if (!result.hasFollowupButton) {
            return { passed: null, skipped: true, message: 'No follow-up button found on results page' };
        }

        return {
            passed: true,
            message: `Follow-up button found: "${result.buttonText}"`
        };
    },

    async followupModalOpens(page, baseUrl) {
        // Navigate to a results page
        await navigateToWithRetry(page, `${baseUrl}/history`);

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

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

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

        // Click follow-up button
        const clicked = await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const followupBtn = buttons.find(b => {
                const text = b.textContent?.toLowerCase() || '';
                return text.includes('follow') || text.includes('continue') || text.includes('deeper');
            });

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

        if (!clicked) {
            return { passed: null, skipped: true, message: 'No follow-up button to click' };
        }

        await delay(500);

        const result = await page.evaluate(() => {
            const modal = document.querySelector('.modal, .dialog, [role="dialog"], .followup-form');
            const form = document.querySelector('form.followup-form, form[action*="followup"], .followup-modal form');

            return {
                hasModal: !!modal && (modal.style.display !== 'none'),
                hasForm: !!form,
                hasQueryInput: !!document.querySelector('input[name*="query"], textarea[name*="query"], #followup-query')
            };
        });

        const passed = result.hasModal || result.hasForm || result.hasQueryInput;

        return {
            passed,
            message: passed
                ? `Follow-up modal/form opens (modal=${result.hasModal}, form=${result.hasForm})`
                : 'Follow-up modal did not open'
        };
    },

    async followupQueryPrefilled(page, baseUrl) {
        await navigateToWithRetry(page, `${baseUrl}/history`);

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

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

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

        // Click follow-up button
        await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const followupBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('follow') ||
                b.textContent?.toLowerCase().includes('deeper')
            );
            if (followupBtn) followupBtn.click();
        });

        await delay(500);

        const result = await page.evaluate(() => {
            const queryInput = document.querySelector(
                'input[name*="query"], ' +
                'textarea[name*="query"], ' +
                '#followup-query, ' +
                '.followup-input'
            );

            if (!queryInput) return { hasInput: false };

            const value = queryInput.value || queryInput.textContent;
            const placeholder = queryInput.placeholder;

            return {
                hasInput: true,
                hasValue: value && value.length > 0,
                valueLength: value?.length || 0,
                hasPlaceholder: placeholder && placeholder.length > 0,
                previewText: value?.substring(0, 50) || placeholder?.substring(0, 50)
            };
        });

        if (!result.hasInput) {
            return { passed: null, skipped: true, message: 'No query input found in follow-up form' };
        }

        return {
            passed: result.hasValue || result.hasPlaceholder,
            message: result.hasValue
                ? `Query prefilled (${result.valueLength} chars): "${result.previewText}..."`
                : (result.hasPlaceholder ? `Placeholder set: "${result.previewText}..."` : 'Query not prefilled')
        };
    },

    async followupSubmitButton(page, baseUrl) {
        await navigateToWithRetry(page, `${baseUrl}/history`);

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

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

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

        // Open follow-up form
        await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const followupBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('follow') ||
                b.textContent?.toLowerCase().includes('deeper')
            );
            if (followupBtn) followupBtn.click();
        });

        await delay(500);

        const result = await page.evaluate(() => {
            const submitBtn = document.querySelector(
                'button[type="submit"], ' +
                '.followup-submit, ' +
                '.btn-primary, ' +
                'button.submit'
            );

            // Check within modal/form context
            const modal = document.querySelector('.modal, .dialog, [role="dialog"]');
            const modalSubmit = modal?.querySelector('button[type="submit"], .btn-primary');

            const btn = modalSubmit || submitBtn;

            return {
                hasSubmitBtn: !!btn,
                buttonText: btn?.textContent?.trim(),
                isDisabled: btn?.disabled
            };
        });

        if (!result.hasSubmitBtn) {
            return { passed: null, skipped: true, message: 'No submit button in follow-up form' };
        }

        return {
            passed: true,
            message: `Submit button found: "${result.buttonText}" (disabled: ${result.isDisabled})`
        };
    },

    async followupModeSelection(page, baseUrl) {
        await navigateToWithRetry(page, `${baseUrl}/history`);

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

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

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

        // Open follow-up form
        await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const followupBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('follow') ||
                b.textContent?.toLowerCase().includes('deeper')
            );
            if (followupBtn) followupBtn.click();
        });

        await delay(500);

        const result = await page.evaluate(() => {
            const modeSelect = document.querySelector(
                'select[name*="mode"], ' +
                '#research-mode, ' +
                '.mode-select'
            );

            if (modeSelect) {
                const options = Array.from(modeSelect.options).map(o => o.text);
                return {
                    exists: true,
                    type: 'select',
                    options: options.slice(0, 5)
                };
            }

            // Check for radio buttons or toggle
            const modeRadios = document.querySelectorAll('input[type="radio"][name*="mode"]');
            const modeToggle = document.querySelector('.mode-toggle, [class*="mode-selector"]');

            if (modeRadios.length > 0) {
                return {
                    exists: true,
                    type: 'radio',
                    optionCount: modeRadios.length
                };
            }

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

            return { exists: false };
        });

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'No mode selection in follow-up form' };
        }

        return {
            passed: true,
            message: result.type === 'select'
                ? `Mode selection: ${result.options.join(', ')}`
                : `Mode selection (${result.type})`
        };
    },

    async followupLinksToOriginal(page, baseUrl) {
        await navigateToWithRetry(page, `${baseUrl}/history`);

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

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

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

        // Open follow-up form
        await page.evaluate(() => {
            const buttons = Array.from(document.querySelectorAll('button, a.btn'));
            const followupBtn = buttons.find(b =>
                b.textContent?.toLowerCase().includes('follow') ||
                b.textContent?.toLowerCase().includes('deeper')
            );
            if (followupBtn) followupBtn.click();
        });

        await delay(500);

        const result = await page.evaluate((originalId) => {
            // Look for reference to original research
            const modal = document.querySelector('.modal, .dialog, [role="dialog"], .followup-form');
            const container = modal || document;

            const originalLink = container.querySelector(`a[href*="${originalId}"], a[href*="original"]`);
            const originalRef = container.querySelector('[class*="original"], [class*="parent"], .source-research');

            // Check text content for reference
            const text = container.textContent?.toLowerCase() || '';
            const hasReference = text.includes('original') || text.includes('parent') ||
                                text.includes('based on') || text.includes('continue from');

            return {
                hasLink: !!originalLink,
                hasRefElement: !!originalRef,
                hasReferenceText: hasReference,
                linkHref: originalLink?.href
            };
        }, researchId);

        const hasReference = result.hasLink || result.hasRefElement || result.hasReferenceText;

        if (!hasReference) {
            return { passed: null, skipped: true, message: 'No reference to original research found' };
        }

        return {
            passed: true,
            message: result.hasLink
                ? `Link to original research found`
                : (result.hasRefElement ? 'Reference to original research shown' : 'Original research reference in text')
        };
    }
};

// ============================================================================
// Follow-up API Tests
// ============================================================================
const FollowupApiTests = {
    async followupApiEndpointExists(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/`);

        const result = await page.evaluate(async (url) => {
            try {
                // Test OPTIONS or GET to see if endpoint exists
                const response = await fetch(`${url}/followup/prepare`, {
                    method: 'OPTIONS'
                });

                // Even 405 (Method Not Allowed) means endpoint exists
                return {
                    exists: response.status !== 404,
                    status: response.status
                };
            } catch {
                // Try GET
                try {
                    const getResponse = await fetch(`${url}/followup/prepare`);
                    return {
                        exists: getResponse.status !== 404,
                        status: getResponse.status
                    };
                } catch (e2) {
                    return { exists: false, error: e2.message };
                }
            }
        }, baseUrl);

        if (!result.exists) {
            return { passed: null, skipped: true, message: 'Follow-up API endpoint not found' };
        }

        return {
            passed: true,
            message: `Follow-up API endpoint exists (status: ${result.status})`
        };
    }
};

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

    const ctx = await setupTest({ authenticate: true });
    const results = new TestResults('Follow-up Research 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}`);
            // If a test timed out, the page may be in a broken state (e.g.
            // a pending navigation that partially completed).  Navigate to
            // about:blank so subsequent tests don't hit "detached frame"
            // errors and can start fresh.
            try {
                await page.goto('about:blank', { timeout: 5000 });
            } catch {
                // Best-effort recovery — don't mask the original failure.
            }
        }
    }

    try {
        // Follow-up Research Tests
        log.section('Follow-up Research');

        await run('Followup', 'Follow-up Button On Results', (p, u) => FollowupResearchTests.followupButtonOnResults(p, u));
        await run('Followup', 'Follow-up Modal Opens', (p, u) => FollowupResearchTests.followupModalOpens(p, u));
        await run('Followup', 'Follow-up Query Prefilled', (p, u) => FollowupResearchTests.followupQueryPrefilled(p, u));
        await run('Followup', 'Follow-up Submit Button', (p, u) => FollowupResearchTests.followupSubmitButton(p, u));
        await run('Followup', 'Follow-up Mode Selection', (p, u) => FollowupResearchTests.followupModeSelection(p, u));
        await run('Followup', 'Follow-up Links To Original', (p, u) => FollowupResearchTests.followupLinksToOriginal(p, u));

        // API Tests
        log.section('Follow-up API');
        await run('API', 'Follow-up API Endpoint Exists', (p, u) => FollowupApiTests.followupApiEndpointExists(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 = { FollowupResearchTests, FollowupApiTests };
