#!/usr/bin/env node
/**
 * Context Overflow UI Tests
 *
 * Tests for the context overflow analytics page and related functionality.
 *
 * Run: node test_context_overflow_ci.js
 */

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

// ============================================================================
// Context Overflow Page Tests
// ============================================================================
const ContextOverflowTests = {
    async contextOverflowPageLoads(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(() => {
            return {
                hasContent: !!document.querySelector('.context-overflow, .overflow-container, #context-overflow, .analytics'),
                hasHeader: !!document.querySelector('h1, .page-title'),
                headerText: document.querySelector('h1, .page-title')?.textContent?.trim(),
                hasData: document.body.textContent?.toLowerCase().includes('overflow') ||
                         document.body.textContent?.toLowerCase().includes('truncat'),
                is404: document.body.textContent?.includes('404') || document.body.textContent?.includes('Not Found')
            };
        });

        if (result.is404) {
            return { passed: null, skipped: true, message: 'Context overflow page not found (feature may not be enabled)' };
        }

        const passed = result.hasContent || result.hasHeader || result.hasData;
        return {
            passed,
            message: passed
                ? `Context overflow page loaded (header: "${result.headerText}")`
                : 'Context overflow page failed to load'
        };
    },

    async truncationRateDisplay(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(() => {
            const rateElement = document.querySelector(
                '[class*="truncation-rate"], ' +
                '[id*="truncation-rate"], ' +
                '.rate-display, ' +
                '.overflow-rate'
            );

            // Look for percentage pattern
            const percentPattern = /(\d+(?:\.\d*)?)\s*%/;
            const bodyText = document.body.textContent || '';
            const percentMatch = bodyText.match(percentPattern);

            // Look for rate-related text
            const hasRateText = bodyText.toLowerCase().includes('truncation rate') ||
                                bodyText.toLowerCase().includes('overflow rate');

            return {
                hasRateElement: !!rateElement,
                rateText: rateElement?.textContent?.trim(),
                hasPercentage: !!percentMatch,
                percentValue: percentMatch ? percentMatch[1] : null,
                hasRateText
            };
        });

        if (!result.hasRateElement && !result.hasPercentage && !result.hasRateText) {
            return { passed: null, skipped: true, message: 'No truncation rate display found' };
        }

        return {
            passed: true,
            message: result.hasRateElement
                ? `Truncation rate displayed: "${result.rateText}"`
                : `Truncation rate: ${result.percentValue}%`
        };
    },

    async averageTruncatedTokens(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(() => {
            const avgElement = document.querySelector(
                '[class*="average-tokens"], ' +
                '[class*="avg-truncated"], ' +
                '.token-average'
            );

            // Look for token count patterns
            const tokenPattern = /(\d[\d,]*)\s*(?:tokens?|truncated)/i;
            const bodyText = document.body.textContent || '';
            const tokenMatch = bodyText.match(tokenPattern);

            const hasAvgText = bodyText.toLowerCase().includes('average') &&
                               bodyText.toLowerCase().includes('token');

            return {
                hasAvgElement: !!avgElement,
                avgText: avgElement?.textContent?.trim(),
                hasTokenMatch: !!tokenMatch,
                tokenValue: tokenMatch ? tokenMatch[1] : null,
                hasAvgText
            };
        });

        if (!result.hasAvgElement && !result.hasTokenMatch && !result.hasAvgText) {
            return { passed: null, skipped: true, message: 'No average truncated tokens display found' };
        }

        return {
            passed: true,
            message: result.hasAvgElement
                ? `Average tokens: "${result.avgText}"`
                : (result.tokenValue ? `Token count found: ${result.tokenValue}` : 'Average token info found')
        };
    },

    async contextOverflowChart(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);
        await delay(1000); // Wait for charts to render

        const result = await page.evaluate(() => {
            const charts = document.querySelectorAll('canvas, svg, .chart, .recharts-wrapper, [class*="chart"]');
            const overflowChart = document.querySelector('[class*="overflow-chart"], [id*="overflow-chart"]');

            return {
                hasCharts: charts.length > 0,
                chartCount: charts.length,
                hasOverflowChart: !!overflowChart,
                chartTypes: Array.from(charts).map(c => c.tagName.toLowerCase()).slice(0, 5)
            };
        });

        if (!result.hasCharts) {
            return { passed: null, skipped: true, message: 'No charts found on context overflow page' };
        }

        return {
            passed: true,
            message: `Found ${result.chartCount} charts (types: ${result.chartTypes.join(', ')})`
        };
    },

    async periodFilterWorks(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(() => {
            const periodFilter = document.querySelector(
                'select[name*="period"], ' +
                '#period-filter, ' +
                '.period-filter, ' +
                'select[id*="period"]'
            );

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

            // Check for button-based filter
            const buttons = document.querySelectorAll('.period-btn, .time-filter button, [data-period]');
            if (buttons.length > 0) {
                return {
                    exists: true,
                    type: 'buttons',
                    buttonCount: buttons.length,
                    options: Array.from(buttons).map(b => b.textContent?.trim()).slice(0, 6)
                };
            }

            return { exists: false };
        });

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

        return {
            passed: true,
            message: result.type === 'select'
                ? `Period filter (${result.optionCount} options): ${result.options.join(', ')}`
                : `Period buttons (${result.buttonCount}): ${result.options.join(', ')}`
        };
    },

    async overflowDetailsTable(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(() => {
            const table = document.querySelector('table, .overflow-details, .data-table');
            if (!table) return { hasTable: false };

            const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent?.toLowerCase().trim());
            const rows = table.querySelectorAll('tbody tr');

            return {
                hasTable: true,
                headerCount: headers.length,
                rowCount: rows.length,
                headers: headers.slice(0, 6),
                hasResearchColumn: headers.some(h => h.includes('research') || h.includes('query')),
                hasTokenColumn: headers.some(h => h.includes('token'))
            };
        });

        if (!result.hasTable) {
            return { passed: null, skipped: true, message: 'No overflow details table found' };
        }

        return {
            passed: true,
            message: `Details table: ${result.rowCount} rows, columns: ${result.headers.join(', ')}`
        };
    }
};

// ============================================================================
// Context Overflow API Tests
// ============================================================================
const ContextOverflowApiTests = {
    async contextOverflowApiResponds(page, baseUrl) {
        await navigateTo(page, `${baseUrl}/metrics/context-overflow`);

        const result = await page.evaluate(async (url) => {
            try {
                const response = await fetch(`${url}/metrics/api/context-overflow`);
                if (!response.ok) return { ok: false, status: response.status };

                const data = await response.json();
                return {
                    ok: true,
                    status: response.status,
                    hasData: Object.keys(data).length > 0,
                    keys: Object.keys(data).slice(0, 5)
                };
            } catch (e) {
                return { ok: false, error: e.message };
            }
        }, baseUrl);

        if (!result.ok && result.status === 404) {
            return { passed: null, skipped: true, message: 'Context overflow API not found' };
        }

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

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

    const ctx = await setupTest({ authenticate: true });
    const results = new TestResults('Context Overflow 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 {
        // Pre-navigate to context overflow page once to check if it loads
        // (avoids 7 × 30s timeouts in CI when page is slow)
        log.section('Context Overflow Page');
        let pageAccessible = false;
        try {
            await navigateTo(page, `${baseUrl}/metrics/context-overflow`);
            const is404 = await page.evaluate(() =>
                document.body.textContent?.includes('404') || document.body.textContent?.includes('Not Found')
            );
            pageAccessible = !is404;
        } catch (navError) {
            log.warning(`Context overflow page not accessible: ${navError.message}`);
        }

        if (!pageAccessible) {
            const skipMsg = 'Context overflow page not accessible in CI';
            results.skip('Page', 'Context Overflow Page Loads', skipMsg);
            results.skip('Page', 'Truncation Rate Display', skipMsg);
            results.skip('Page', 'Average Truncated Tokens', skipMsg);
            results.skip('Page', 'Context Overflow Chart', skipMsg);
            results.skip('Page', 'Period Filter Works', skipMsg);
            results.skip('Page', 'Overflow Details Table', skipMsg);
            results.skip('API', 'Context Overflow API Responds', skipMsg);
        } else {
            await run('Page', 'Context Overflow Page Loads', (p, u) => ContextOverflowTests.contextOverflowPageLoads(p, u));
            await run('Page', 'Truncation Rate Display', (p, u) => ContextOverflowTests.truncationRateDisplay(p, u));
            await run('Page', 'Average Truncated Tokens', (p, u) => ContextOverflowTests.averageTruncatedTokens(p, u));
            await run('Page', 'Context Overflow Chart', (p, u) => ContextOverflowTests.contextOverflowChart(p, u));
            await run('Page', 'Period Filter Works', (p, u) => ContextOverflowTests.periodFilterWorks(p, u));
            await run('Page', 'Overflow Details Table', (p, u) => ContextOverflowTests.overflowDetailsTable(p, u));

            // API Tests
            log.section('Context Overflow API');
            await run('API', 'Context Overflow API Responds', (p, u) => ContextOverflowApiTests.contextOverflowApiResponds(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 = { ContextOverflowTests, ContextOverflowApiTests };
