const puppeteer = require('puppeteer');
const { getPuppeteerLaunchOptions } = require('./puppeteer_config');

// Test configuration
const BASE_URL = 'http://127.0.0.1:5000';
const TEST_USER = `metrics_test_${Date.now()}`;
const TEST_PASSWORD = 'TestPass123!';

// Colors for console output
const colors = {
    reset: '\x1b[0m',
    bright: '\x1b[1m',
    green: '\x1b[32m',
    red: '\x1b[31m',
    yellow: '\x1b[33m',
    blue: '\x1b[34m',
    cyan: '\x1b[36m'
};

function log(message, type = 'info') {
    const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
    const typeColors = {
        'info': colors.cyan,
        'success': colors.green,
        'error': colors.red,
        'warning': colors.yellow,
        'section': colors.blue
    };
    const color = typeColors[type] || colors.reset;
    console.log(`${color}[${timestamp}] ${message}${colors.reset}`);
}

async function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function registerAndLogin(page) {
    log('📝 Registering new user...', 'info');

    await page.goto(`${BASE_URL}/auth/register`);
    await page.waitForSelector('#username');

    await page.type('#username', TEST_USER);
    await page.type('#password', TEST_PASSWORD);
    await page.type('#confirm_password', TEST_PASSWORD);

    // Check the acknowledge checkbox
    const acknowledgeCheckbox = await page.$('#acknowledge');
    if (acknowledgeCheckbox) {
        await acknowledgeCheckbox.click();
    }

    await Promise.all([
        page.waitForNavigation(),
        page.click('button[type="submit"]')
    ]);

    log('✅ Registration successful', 'success');
}

async function createResearch(page, query) {
    log(`🔬 Creating research: "${query}"`, 'info');

    await page.goto(`${BASE_URL}/`);
    await page.waitForSelector('#query');

    await page.evaluate((q) => {
        document.getElementById('query').value = q;
    }, query);

    // Wait for the start_research API response so we know submission completed
    // (passing or failing) before checking the URL — replaces a fixed 3s delay.
    const startResearchPromise = page.waitForResponse(
        (response) => response.url().includes('/api/start_research'),
        { timeout: 15000 }
    ).catch(() => null);

    await page.click('#start-research-btn');

    log('⏳ Waiting for research submission...', 'info');
    const response = await startResearchPromise;
    if (response) {
        log(`📥 start_research → ${response.status()}`, 'info');
    }

    // Give the SPA a tiny moment to navigate; check URL right after.
    await page.waitForFunction(
        () => location.pathname.includes('/progress/') || location.pathname.includes('/research/'),
        { timeout: 5000 }
    ).catch(() => {});

    const currentUrl = page.url();
    if (currentUrl.includes('/progress/') || currentUrl.includes('/research/')) {
        log('✅ Research submitted successfully', 'success');
        return;
    }
    log('⚠️ Research did not navigate to progress/research page', 'warning');
}

async function testMetricsDashboard() {
    const browser = await puppeteer.launch(getPuppeteerLaunchOptions());

    const page = await browser.newPage();

    // Enable request interception to monitor API calls
    await page.setRequestInterception(true);
    const apiCalls = [];

    page.on('request', (request) => {
        const url = request.url();
        if (url.includes('/api/metrics') || url.includes('/api/')) {
            apiCalls.push({
                url,
                method: request.method(),
                timestamp: new Date().toISOString()
            });
        }
        request.continue();
    });

    // Set console log handler
    page.on('console', msg => {
        if (msg.type() === 'error' && !msg.text().includes('favicon')) {
            log(`Browser console error: ${msg.text()}`, 'error');
        }
    });

    try {
        // Setup: Register and create some research
        await registerAndLogin(page);

        // In CI, we'll start one quick research just to test the flow
        log('\n=== CREATING TEST DATA ===', 'section');

        // Only create one simple research to avoid timeouts
        await createResearch(page, 'What is 2+2?');

        // Navigate to metrics dashboard
        log('\n=== TESTING METRICS DASHBOARD ===', 'section');
        await page.goto(`${BASE_URL}/metrics`);

        // Wait for page to load - check multiple possible selectors. If
        // neither the expected container nor the URL matches, the metrics
        // dashboard is broken and we want the test to fail loudly rather
        // than continue and emit downstream "may not be working" warnings.
        try {
            await page.waitForSelector('.metrics-container, #metrics-dashboard, .container h1, [data-page="metrics"]', { timeout: 10000 });
            log('✅ Metrics page loaded', 'success');
        } catch {
            const currentUrl = page.url();
            if (currentUrl.includes('/metrics')) {
                log('✅ On metrics page (by URL)', 'success');
            } else {
                throw new Error(
                    `Metrics dashboard did not load: no expected selector and URL is ${currentUrl}. ` +
                    'Either the route is broken or the page renders without any of the known root selectors.'
                );
            }
        }

        // Take screenshot if not in headless mode
        const isHeadless = process.env.HEADLESS !== 'false';
        if (!isHeadless) {
            await page.screenshot({
                path: './metrics-dashboard-test.png',
                fullPage: true
            });
            log('📸 Screenshot saved: ./metrics-dashboard-test.png', 'info');
        }

        // Wait for the metrics API response (or a short ceiling) instead of
        // a fixed 3s delay. The metrics page issues several /api/metrics
        // calls on load; we only need to see one land before we assert.
        await page.waitForResponse(
            (response) => response.url().includes('/api/metrics'),
            { timeout: 10000 }
        ).catch(() => {
            log('ℹ️ No /api/metrics response within 10s — page may have no data yet', 'info');
        });

        // Check if metrics API was called
        const metricsApiCalls = apiCalls.filter(call => call.url.includes('/api/metrics'));
        log(`📊 Metrics API calls made: ${metricsApiCalls.length}`, 'info');

        if (metricsApiCalls.length === 0) {
            log('⚠️ No metrics API calls detected (expected in CI with no data)', 'warning');
        } else {
            log('✅ Metrics API was called', 'success');
        }

        // Check page content
        const metricsData = await page.evaluate(() => {
            const data = {
                title: document.title,
                hasCharts: !!document.querySelector('canvas, .chart-container, .chart'),
                sections: [],
                stats: {},
                errors: [],
                gridLayout: null
            };

            // Find metric sections
            const sections = document.querySelectorAll('.metric-section, .ldr-card, .panel');
            sections.forEach(section => {
                const title = section.querySelector('h2, h3, .card-title, .panel-title');
                if (title) {
                    data.sections.push(title.textContent.trim());
                }
            });

            // Find statistic values
            const statElements = document.querySelectorAll('.stat-value, .metric-value, [data-metric]');
            statElements.forEach(el => {
                const label = el.getAttribute('data-metric') ||
                             el.previousElementSibling?.textContent ||
                             'unknown';
                data.stats[label] = el.textContent.trim();
            });

            // Check for error messages
            const errors = document.querySelectorAll('.error, .alert-danger');
            errors.forEach(error => {
                data.errors.push(error.textContent.trim());
            });

            // Check for loading indicators still visible
            data.hasLoadingIndicators = !!document.querySelector('.loading, .spinner');

            // Check grid layout for metrics
            const metricsGrid = document.querySelector('.ldr-metrics-grid, .metrics-grid');
            if (metricsGrid) {
                const gridStyle = window.getComputedStyle(metricsGrid);
                const cards = metricsGrid.querySelectorAll('.ldr-metric-card, .metric-card');
                data.gridLayout = {
                    found: true,
                    display: gridStyle.display,
                    gridTemplateColumns: gridStyle.gridTemplateColumns,
                    gap: gridStyle.gap,
                    cardCount: cards.length,
                    cardsVisible: Array.from(cards).map(card => ({
                        visible: card.offsetParent !== null,
                        width: card.offsetWidth,
                        hasValue: !!card.querySelector('.ldr-metric-value, .metric-value')
                    }))
                };
            } else {
                data.gridLayout = { found: false };
            }

            return data;
        });

        log('📊 Metrics page content:', 'info');
        log(`  - Title: ${metricsData.title}`, 'info');
        log(`  - Sections found: ${metricsData.sections.length}`, 'info');
        log(`  - Has charts: ${metricsData.hasCharts}`, 'info');
        log(`  - Statistics found: ${Object.keys(metricsData.stats).length}`, 'info');

        // Log grid layout info
        if (metricsData.gridLayout) {
            if (metricsData.gridLayout.found) {
                log('📐 Grid Layout:', 'info');
                log(`  - Display: ${metricsData.gridLayout.display}`, 'info');
                log(`  - Grid columns: ${metricsData.gridLayout.gridTemplateColumns}`, 'info');
                log(`  - Gap: ${metricsData.gridLayout.gap}`, 'info');
                log(`  - Cards found: ${metricsData.gridLayout.cardCount}`, 'info');
                const visibleCards = metricsData.gridLayout.cardsVisible.filter(c => c.visible).length;
                log(`  - Cards visible: ${visibleCards}/${metricsData.gridLayout.cardCount}`, 'info');

                // Check if cards are in grid (side by side)
                if (metricsData.gridLayout.display === 'grid' && visibleCards > 1) {
                    const widths = metricsData.gridLayout.cardsVisible.map(c => c.width);
                    const uniqueWidths = [...new Set(widths)];
                    if (uniqueWidths.length === 1 && widths[0] < 600) {
                        log('✅ Metrics cards are displayed in grid layout (side by side)', 'success');
                    } else {
                        log('⚠️ Metrics cards may not be in proper grid layout', 'warning');
                    }
                }
            } else {
                // The page-level metrics grid container is what holds the
                // primary stat cards. If it's missing, the dashboard is
                // structurally broken — fail rather than warn.
                throw new Error(
                    'Metrics grid container (.ldr-metrics-grid / .metrics-grid) not found on /metrics. ' +
                    'The dashboard layout has regressed.'
                );
            }
        }

        if (metricsData.sections.length > 0) {
            log('📋 Sections:', 'info');
            metricsData.sections.forEach(section => {
                log(`  - ${section}`, 'info');
            });
        }

        if (metricsData.errors.length > 0) {
            log('❌ Errors found on page:', 'error');
            metricsData.errors.forEach(error => {
                log(`  - ${error}`, 'error');
            });
        }

        // Test period selector if available
        log('\n=== TESTING PERIOD SELECTOR ===', 'section');
        const periodSelector = await page.$('.period-selector, select[name="period"], #period-select');
        if (periodSelector) {
            // Wait for the period change to trigger an API call carrying the
            // new period — replaces a fixed 2s delay. If nothing fires within
            // 5s, we fall through to the "may not be working" branch.
            const periodApiPromise = page.waitForResponse(
                (response) => response.url().includes('period=7d'),
                { timeout: 5000 }
            ).catch(() => null);

            await page.select('.period-selector, select[name="period"], #period-select', '7d');
            await periodApiPromise;

            // Check if API was called with new period
            const recentApiCalls = apiCalls.filter(call =>
                call.url.includes('period=7d') &&
                new Date() - new Date(call.timestamp) < 3000
            );

            if (recentApiCalls.length > 0) {
                log('✅ Period selector works - API called with new period', 'success');
            } else {
                // The selector is rendered but changing it didn't trigger an
                // API call with the new period — the period filter is broken.
                throw new Error(
                    'Period selector exists but changing it did not trigger an API call with period=7d. ' +
                    'The filter handler has regressed.'
                );
            }
        }

        // Test sub-pages
        log('\n=== TESTING SUB-PAGES ===', 'section');

        // Test cost analytics page
        const costLink = await page.$('a[href*="/costs"], a[href*="/cost-analytics"]');
        if (costLink) {
            await costLink.click();
            await page.waitForNavigation();
            await page.waitForSelector('.cost-analytics, #cost-dashboard', { timeout: 5000 }).catch(() => {});

            const costPageUrl = page.url();
            if (costPageUrl.includes('cost')) {
                log('✅ Cost analytics page accessible', 'success');

                // Check for cost data
                const hasCostData = await page.evaluate(() => {
                    return document.body.textContent.includes('$') ||
                           document.body.textContent.includes('cost') ||
                           document.body.textContent.includes('Cost');
                });

                if (hasCostData) {
                    log('  - Cost data present', 'info');
                }
            }
        }

        // Go back to main metrics; wait for the dashboard to actually render
        // (replaces a fixed 2s delay).
        await page.goto(`${BASE_URL}/metrics`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector(
            '.metrics-container, #metrics-dashboard, .container h1, [data-page="metrics"]',
            { timeout: 10000 }
        ).catch(() => {});

        // Test star reviews page
        const reviewsLink = await page.$('a[href*="/star-reviews"], a[href*="/reviews"]');
        if (reviewsLink) {
            await reviewsLink.click();
            await page.waitForNavigation();
            await page.waitForSelector('.star-reviews, #reviews-dashboard', { timeout: 5000 }).catch(() => {});

            const reviewsPageUrl = page.url();
            if (reviewsPageUrl.includes('review')) {
                log('✅ Star reviews page accessible', 'success');
            }
        }

        // Test rate limiting metrics
        log('\n=== TESTING RATE LIMITING METRICS ===', 'section');
        await page.goto(`${BASE_URL}/metrics`, { waitUntil: 'domcontentloaded' });
        await page.waitForSelector(
            '.metrics-container, #metrics-dashboard, .container h1, [data-page="metrics"]',
            { timeout: 10000 }
        ).catch(() => {});

        const rateLimitingData = await page.evaluate(() => {
            const elements = Array.from(document.querySelectorAll('*'));
            return elements.some(el =>
                el.textContent.includes('rate limit') ||
                el.textContent.includes('Rate Limit')
            );
        });

        if (rateLimitingData) {
            log('✅ Rate limiting metrics present', 'success');
        }

        // Check for charts/visualizations
        log('\n=== TESTING VISUALIZATIONS ===', 'section');
        const hasVisualizations = await page.evaluate(() => {
            return {
                canvas: document.querySelectorAll('canvas').length,
                svg: document.querySelectorAll('svg').length,
                chartContainers: document.querySelectorAll('.chart, .chart-container').length
            };
        });

        log(`📊 Visualizations found:`, 'info');
        log(`  - Canvas elements: ${hasVisualizations.canvas}`, 'info');
        log(`  - SVG elements: ${hasVisualizations.svg}`, 'info');
        log(`  - Chart containers: ${hasVisualizations.chartContainers}`, 'info');


        // Final API call summary
        log('\n=== API CALLS SUMMARY ===', 'section');
        const apiEndpoints = [...new Set(apiCalls.map(call => {
            const url = new URL(call.url);
            return url.pathname;
        }))];

        log(`📡 Unique API endpoints called: ${apiEndpoints.length}`, 'info');
        apiEndpoints.forEach(endpoint => {
            log(`  - ${endpoint}`, 'info');
        });

        log('\n✅ Metrics dashboard test completed successfully!', 'success');

    } catch (error) {
        log(`\n❌ Test failed: ${error.message}`, 'error');


        throw error;
    } finally {
        await browser.close();
    }
}

// Run the test
testMetricsDashboard().catch(error => {
    console.error('Test execution failed:', error);
    process.exit(1);
});
