#!/usr/bin/env node
/**
 * Comprehensive Mobile UI CI Test
 * Tests all pages for mobile UI issues with proper assertions
 *
 * Features:
 * - Tests all pages at multiple mobile viewports
 * - Checks for horizontal overflow, touch targets, mobile nav
 * - Verifies sheet menu shows all sections including Settings
 * - Generates JSON/JUnit reports for CI
 */

const puppeteer = require('puppeteer');
const AuthHelper = require('../auth_helper');
const { getPuppeteerLaunchOptions } = require('../puppeteer_config');
const path = require('path');
const fs = require('fs').promises;

// Test configuration
const CONFIG = {
    baseUrl: process.env.TEST_BASE_URL || 'http://127.0.0.1:5000',
    timeout: 30000,
    outputFormat: process.env.OUTPUT_FORMAT || 'json',
};

// Mobile viewports to test
const VIEWPORTS = {
    'iPhone_SE': { width: 375, height: 667, isMobile: true, hasTouch: true },
    'iPhone_14_Pro': { width: 430, height: 932, isMobile: true, hasTouch: true },
};

// Pages to test
const PAGES = [
    { path: '/', name: 'Research', requiresAuth: true },
    { path: '/history/', name: 'History', requiresAuth: true },
    { path: '/news/', name: 'News', requiresAuth: true },
    { path: '/news/subscriptions', name: 'Subscriptions', requiresAuth: true },
    { path: '/settings/', name: 'Settings', requiresAuth: true },
    { path: '/metrics/', name: 'Metrics', requiresAuth: true },
    { path: '/benchmark/', name: 'Benchmark', requiresAuth: true },
    { path: '/settings/collections', name: 'Collections', requiresAuth: true },
    { path: '/auth/login', name: 'Login', requiresAuth: false },
];

class MobileAllPagesTest {
    constructor() {
        this.results = {
            startTime: new Date(),
            tests: [],
            summary: { total: 0, passed: 0, failed: 0 }
        };
    }

    async run() {
        console.log('🚀 Mobile UI CI Test - All Pages');
        console.log('='.repeat(50));

        let browser;
        try {
            browser = await puppeteer.launch(getPuppeteerLaunchOptions());

            for (const [deviceName, viewport] of Object.entries(VIEWPORTS)) {
                await this.testDevice(browser, deviceName, viewport);
            }

            await this.outputResults();
            process.exit(this.results.summary.failed > 0 ? 1 : 0);

        } catch (error) {
            console.error('❌ Test suite failed:', error);
            process.exit(1);
        } finally {
            if (browser) await browser.close();
        }
    }

    async testDevice(browser, deviceName, viewport) {
        console.log(`\n📱 Testing ${deviceName} (${viewport.width}x${viewport.height})`);
        console.log('─'.repeat(50));

        const page = await browser.newPage();
        await page.setViewport(viewport);

        // Authenticate
        const authHelper = new AuthHelper(page, CONFIG.baseUrl);
        try {
            await authHelper.ensureAuthenticated();
        } catch {
            console.log('  ⚠️ Authentication failed, testing unauthenticated pages only');
        }

        // Test each page
        for (const pageInfo of PAGES) {
            await this.testPage(page, deviceName, pageInfo);
        }

        // Test sheet menu specifically
        await this.testSheetMenu(page, deviceName);

        await page.close();
    }

    async testPage(page, deviceName, pageInfo) {
        const testName = `${deviceName}/${pageInfo.name}`;
        const startTime = Date.now();

        try {
            await Promise.race([
                this._testPageInner(page, deviceName, pageInfo, testName, startTime),
                new Promise((_, reject) => setTimeout(() => reject(new Error(`Test "${testName}" timed out after 30s`)), 30000))
            ]);
        } catch (error) {
            this.addResult(testName, false, [error.message], Date.now() - startTime);
            console.log(`  ❌ ${pageInfo.name} - Error: ${error.message}`);
        }
    }

    async _testPageInner(page, deviceName, pageInfo, testName, startTime) {
        await page.goto(CONFIG.baseUrl + pageInfo.path, {
            waitUntil: 'domcontentloaded',
            timeout: CONFIG.timeout
        });

        // Wait for mobile nav to initialize
        await new Promise(resolve => setTimeout(resolve, 1000));

        // Run all checks
        const checks = await page.evaluate(() => {
            const results = {
                horizontalOverflow: false,
                mobileNavExists: false,
                mobileNavVisible: false,
                sidebarHidden: true,
                smallTouchTargets: 0,
                errors: []
            };

            // Check horizontal overflow
            if (document.body.scrollWidth > window.innerWidth + 5) {
                results.horizontalOverflow = true;
                results.errors.push(`Horizontal overflow: ${document.body.scrollWidth}px > ${window.innerWidth}px`);
            }

            // Check mobile nav
            const mobileNav = document.querySelector('.ldr-mobile-bottom-nav');
            results.mobileNavExists = !!mobileNav;
            if (mobileNav) {
                const style = window.getComputedStyle(mobileNav);
                results.mobileNavVisible = style.display !== 'none' &&
                    mobileNav.classList.contains('visible');
            }

            // Check sidebar is hidden on mobile
            const sidebar = document.querySelector('.ldr-sidebar');
            if (sidebar) {
                const style = window.getComputedStyle(sidebar);
                results.sidebarHidden = style.display === 'none' ||
                    style.visibility === 'hidden';
                if (!results.sidebarHidden && window.innerWidth <= 768) {
                    results.errors.push('Desktop sidebar visible on mobile');
                }
            }

            // Check touch targets (only count critical ones)
            document.querySelectorAll('button:not([disabled]), a:not([disabled]), input:not([type="hidden"])').forEach(el => {
                const rect = el.getBoundingClientRect();
                if (rect.width > 0 && rect.height > 0 && rect.width < 40 && rect.height < 40) {
                    results.smallTouchTargets++;
                }
            });

            return results;
        });

        // Determine test result
        let passed = true;
        const issues = [];

        if (checks.horizontalOverflow) {
            passed = false;
            issues.push('Horizontal overflow detected');
        }

        // Only check mobile nav on authenticated pages (login page won't have it)
        if (pageInfo.requiresAuth && !checks.mobileNavVisible) {
            // Check if we're actually on the page (not redirected to login)
            const currentUrl = page.url();
            if (!currentUrl.includes('/auth/')) {
                passed = false;
                issues.push('Mobile navigation not visible');
            }
        }

        if (!checks.sidebarHidden) {
            passed = false;
            issues.push('Desktop sidebar visible on mobile');
        }

        // Warnings (don't fail test)
        if (checks.smallTouchTargets > 10) {
            issues.push(`${checks.smallTouchTargets} small touch targets (warning)`);
        }

        this.addResult(testName, passed, issues, Date.now() - startTime);

        const status = passed ? '✅' : '❌';
        console.log(`  ${status} ${pageInfo.name} ${issues.length > 0 ? '- ' + issues.join(', ') : ''}`);
    }

    async testSheetMenu(page, deviceName) {
        const testName = `${deviceName}/SheetMenu`;
        const startTime = Date.now();

        try {
            await Promise.race([
                this._testSheetMenuInner(page, deviceName, testName, startTime),
                new Promise((_, reject) => setTimeout(() => reject(new Error(`Test "${testName}" timed out after 30s`)), 30000))
            ]);
        } catch (error) {
            this.addResult(testName, false, [error.message], Date.now() - startTime);
            console.log(`  ❌ SheetMenu - Error: ${error.message}`);
        }
    }

    async _testSheetMenuInner(page, deviceName, testName, startTime) {
        // Navigate to a page with mobile nav
        await page.goto(CONFIG.baseUrl + '/news/', {
            waitUntil: 'domcontentloaded',
            timeout: CONFIG.timeout
        });
        await new Promise(resolve => setTimeout(resolve, 1000));

        // Click More button to open sheet
        const moreBtn = await page.$('.ldr-mobile-nav-tab[data-tab-id="more"]');
        if (!moreBtn) {
            this.addResult(testName, false, ['More button not found'], Date.now() - startTime);
            console.log(`  ❌ SheetMenu - More button not found`);
            return;
        }

        await moreBtn.click();
        await new Promise(resolve => setTimeout(resolve, 500));

        // Check if sheet opened and Settings section is visible
        const sheetStatus = await page.evaluate(() => {
            const sheet = document.querySelector('.ldr-mobile-sheet-menu');
            if (!sheet || !sheet.classList.contains('active')) {
                return { opened: false, error: 'Sheet did not open' };
            }

            const sheetContent = document.querySelector('.ldr-mobile-sheet-content');
            const settingsTitle = Array.from(sheetContent?.querySelectorAll('.ldr-mobile-sheet-title') || [])
                .find(el => el.textContent.includes('Settings'));

            if (!settingsTitle) {
                return { opened: true, settingsVisible: false, error: 'Settings section not found' };
            }

            const titleRect = settingsTitle.getBoundingClientRect();
            const sheetRect = sheetContent.getBoundingClientRect();

            // Settings should be within the visible sheet area (or scrollable to)
            const isAccessible = titleRect.top < sheetRect.bottom + 200; // Allow some scroll

            return {
                opened: true,
                settingsVisible: isAccessible,
                settingsPosition: titleRect.top,
                sheetBottom: sheetRect.bottom
            };
        });

        if (!sheetStatus.opened) {
            this.addResult(testName, false, [sheetStatus.error], Date.now() - startTime);
            console.log(`  ❌ SheetMenu - ${sheetStatus.error}`);
        } else if (!sheetStatus.settingsVisible) {
            this.addResult(testName, false, ['Settings section cut off/not accessible'], Date.now() - startTime);
            console.log(`  ❌ SheetMenu - Settings section not accessible`);
        } else {
            this.addResult(testName, true, [], Date.now() - startTime);
            console.log(`  ✅ SheetMenu - Settings section accessible`);
        }

        // Close sheet
        const overlay = await page.$('.ldr-mobile-sheet-overlay');
        if (overlay) await overlay.click();
    }

    addResult(name, passed, issues, duration) {
        this.results.tests.push({
            name,
            status: passed ? 'passed' : 'failed',
            issues,
            duration: duration / 1000
        });
        this.results.summary.total++;
        this.results.summary[passed ? 'passed' : 'failed']++;
    }

    async outputResults() {
        this.results.endTime = new Date();
        this.results.duration = (this.results.endTime - this.results.startTime) / 1000;

        console.log('\n' + '='.repeat(50));
        console.log('TEST RESULTS');
        console.log('='.repeat(50));
        console.log(`Total: ${this.results.summary.total}`);
        console.log(`Passed: ${this.results.summary.passed}`);
        console.log(`Failed: ${this.results.summary.failed}`);
        console.log(`Duration: ${this.results.duration.toFixed(2)}s`);

        if (this.results.summary.failed > 0) {
            console.log('\n❌ FAILED TESTS:');
            this.results.tests.filter(t => t.status === 'failed').forEach(t => {
                console.log(`  - ${t.name}: ${t.issues.join(', ')}`);
            });
        }

        // Save results
        if (CONFIG.outputFormat === 'json') {
            await fs.writeFile('./mobile-test-results.json', JSON.stringify(this.results, null, 2));
            console.log('\n📁 Results saved to mobile-test-results.json');
        } else if (CONFIG.outputFormat === 'junit') {
            await fs.writeFile('./mobile-test-results.xml', this.toJUnit());
            console.log('\n📁 Results saved to mobile-test-results.xml');
        }
    }

    toJUnit() {
        let xml = `<?xml version="1.0" encoding="UTF-8"?>\n`;
        xml += `<testsuites name="Mobile UI Tests" tests="${this.results.summary.total}" failures="${this.results.summary.failed}" time="${this.results.duration}">\n`;
        xml += `  <testsuite name="MobileAllPages" tests="${this.results.summary.total}" failures="${this.results.summary.failed}">\n`;

        for (const test of this.results.tests) {
            xml += `    <testcase name="${test.name}" time="${test.duration}">\n`;
            if (test.status === 'failed') {
                xml += `      <failure message="${test.issues.join('; ')}">${test.issues.join('\n')}</failure>\n`;
            }
            xml += `    </testcase>\n`;
        }

        xml += `  </testsuite>\n`;
        xml += `</testsuites>`;
        return xml;
    }
}

// Run
const test = new MobileAllPagesTest();
test.run();
